2022. 7. 10. 11:14ㆍ☁️ Cloud
이번 포스트의 목표는 간단한 nodejs 어플리케이션을 docker 에서 실행해보는 것이다. 컨테이너 내부에서 어플리케이션이 실행되기 위한 도커의 원리를 위주로 알아보자.
우선 NodeJs 앱을 도커 환경에서 실행하려면,
- 이미지를 생성하고,
- 그 이미지를 이용해 컨테이너를 실행 후
- 컨테이너 내부에서 nodeJs 어플리케이션을 실행해야 한다.
즉 이미지를 생성하기 위해선 Dockerfile 을 먼저 작성해야만 한다.
디렉토리 구조
package.json
{
"name": "nodejs-docker-app",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start":"node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express" : "^4.16.1"
}
}
server.js
실행하고자 하는 nodejs 어플리케이션의 코드다.
5000번 포트로 접속시 Hello world 문구를 띄우는게 전부인 간단한 어플리케이션이다. 이를 도커 환경에서 띄워보자.
const express = require("express");
const PORT = 5000;
// APP
const app = express();
app.get("/", (req, res) => {
res.send("Hello world");
});
app.listen(PORT);
console.log("server is running");
Dockerfile 작성하기
From node:10
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]
FROM : 이미지 생성 시 기반이 되는 이미지 레이어 -> node:10 은 node 라는 이미지의 10 버전을 의미한다.
COPY : Dockerfile 의 현재 디렉토리에는 server.js 와 package.json
이 존재한다. 이 두 파일도 container 내부
에서 필요한데, COPY 명령어
를 누락하면 해당 파일들이 도커 컨테이너에 존재하지 않기 때문에 RUN npm install
명령어가 제대로 수행되지 않는다.
RUN : 노드 어프리케이션을 실행시키는 명령어
CMD : 노드 웹 서버를 작동시키기 위해선 node + 엔트리 파일명
을 입력해야 한다.
위와 같이 파일을 모두 작성 후 docker 이미지를 빌드하고 컨테이너를 실행해보자.
docker build -t tbnsok/nodejs ./
docker run tbnsok/nodejs
이제 localhost:8080 을 브라우저에 입력하여 "Hello world" 가 출력되는지 확인해보자.
왜 예상대로 출력되지 않는걸까? 정답은 포트 매핑에 있다. 포트 매핑을 제대로 이해하지 못하면 개발 시 에러를 자주 마주하기에 이번 기회에 알아보자.
포트 매핑
server.js 에서 PORT 변수엔 8080 값을 할당했다.
즉 도커 컨테이너 내부에서 실행된 nodejs 어플리케이션은 8080 포트에서 실행된다는 의미인데,
클라이언트 브라우저에서는 도커 컨테이너 내부의 8080 포트
로 접속하지 못한다. 즉 브라우저와 도커 컨테이너 포트 간의 매핑 작업
이 필요하다.
docker run -p 8080:8080 tbnsok/nodejs:latest
위 처럼 -p 옵션
을 사용해 브라우저에서 접속하는 포트와 컨테이너 내부의 포트를 매핑시켜주면, 우리가 원하는대로 node 어플리케이션에 접속할 수 있다.
WORKDIR 설정
WORKDIR 는 working directory 를 의미하는데, Dockerfile 에 이 WORKDIR 를 명시해줘야 한다.
뭐 때문에 추가해야하는 걸까? 우선 WORKDIR 는 무엇을 의미할까?
바로 이미지 내부에서 nodeJs 애플리케이션 소스 코드를 가지고 있을 디렉터리를 생성하는 것이다.
From node:10
WORKDIR /usr/src/app # 추가된 부분
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]
Dockerfile 에 WORKDIR 설정을 해주고 다시 빌드한 후 실행해보자.
docker build -t tbnsok/nodejs ./
docker run -it tbnsok/nodejs sh
쉘 환경에서 ls 명령어로 디렉토리 구조를 살펴보면, 우리가 직접 생성한 server.js, Dockerfile, package.json
파일이 존재하는 것을 확인할 수 있다.
만약 WORKDIR
경로를 설정하지 않았다면, 아래 이미지 처럼 루트 디렉토리
에 우리가 생성할 파일이 모두 섞이게 된다.
Dockerfile 에 아래처럼 /usr/src/app 경로를 설정해주었기에, 빌드시에 해당 경로로 어플리케이션 파일이 생성된다. 이렇게 하여 모든 파일이 루트 디렉토리에 모여있는 혼잡한 구조를 피하고, 짜임새 있는 디렉토리 구조를 만들 수 있다.
WORKDIR /usr/src/app
어플리케이션 소스 변경으로 재빌드시 효율적으로 하는 법
현재 빌드 구조는, 소스코드의 작은 부분을 수정하더라도 처음부터 종속성을 설치하고 빌드하기 때문에 비효율적이다.
이런 문제는 어떻게 해결할 수 있을까?
우선 소스 코드가 변경될 시 소스 코드는 당연히 재빌드 시켜야 하지만, 패키지 종속성은 변경이 없다면 굳이 재빌드 할 필요가 없다.
# 변경 전
From node:10
WORKDIR /usr/src/app
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]
# 변경 후
From node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["node", "server.js"]
변경 후의 Dockerfile 을 보자.
npm install 이전에 COPY package.json ./
명령어가 추가됐다.
이렇게 하여 변경사항이 server.js 의 소스에만 있다면, 불필요하게 모듈(package)을 다시 받지 않도록 했다.
RUN npm install
이 수행되기 이전에 COPY
할 때는 오직 module 에 관한 것만 해주었다.
RUN npm install
이후에 다시 모든 파일들을 COPY
하는데, 이렇게 하면 모듈은 모듈에 변동사항이 있을 때만 다운로드 할 수 있게 되어, 불필요한 빌드과정을 배제할 수 있다.
Docker Volume
도커 볼륨은 무엇일까? 도커 볼륨이란 도커 컨테이너에서 호스트 디렉터리(로컬)에 있는 파일들을 매핑해서 사용하는 것을 의미한다.
즉 도커 컨테이너에서 로컬에 있는 파일들을 참조하게 만들어, 소스 코드에 수정이 있을 때 즉각적으로 대응할 수 있게 된다. (빌드 과정이 불필요해지는 것)
docker run -d -p 5000:8080 -v /usr/src/app/node_modules -v ${pwd}:/usr/src/app tbnsok/nodejs
첫번째 -v
옵션에는 /usr/src/app/node_modules
를 입력한다. 로컬에는 node_modules 가 없기 때문에 배제하는 것이며,
두번째 -v
옵션은 : 콜론을 사용해 현재경로(pwd)
와 도커 컨테이너 내부의 경로(/usr/src/app) 을 매핑해준다.
이렇게 volume 을 이용해서 실행하면 빌드 시 소스가 변경되더라도, 바뀐 코드가 바로 적용된다.
본 포스팅은 인프런의 따라하며 배우는 도커와 CI 환경 강의를 수강하고 정리한 내용을 바탕으로 정리됐습니다.
사용된 도표와 코드는 해당 강의에서 참조했음을 밝힙니다.
'☁️ Cloud' 카테고리의 다른 글
[docker 에러] When using COPY with more than one source file, the destination must be a directory and end with a / (0) | 2022.07.18 |
---|---|
Travis CI 와 Docker 를 이용해 자동 배포하기 (feat.AWS EB) (0) | 2022.07.12 |
Docker 개발/운영 환경 분리 (0) | 2022.07.12 |
Docker 이미지 직접 만들어보기 (0) | 2022.07.09 |
Docker 명령어와 컨테이너 이해 (계속 추가) (0) | 2022.07.09 |