toggle menu

[Docker] node.js 를 위한 Dockerfile 작성하기

2016. 7. 4. 20:00 Docker

들어가며


지난 포스팅 Docker 시작하기 에서 기본적인 Docker 개념과 사용 방법 전반에 대해 다루었다.

이번에는 간단한 node.js application 을 위한 Dockerfile 을 직접 작성해서 Docker 기반으로 node.js application 실행 환경을 구성하기 위한 과정을 정리해보려고 한다.




Dockerfile


Dockerfile 은 Docker 이미지 생성을 위한 일종의 배치 파일이라고 볼 수 있다. 특정 이미지를 기준으로 새로운 이미지 구성에 필요한 명령어들을 저장해놓은 파일이다.


기본 이미지(예를 들면, Ubuntu 기본 상태)을 기반으로 원하는 구성요소를 갖춘 실행 환경까지 구축하기 위해 필요한 과정들을 코드화해서 처리할 수있도록 Docker 에서는 간단한 명령들을 지원하고 있고 각각의 명령에 대해 새로운 레이어를 생성하게 된다. 


Dockerfile 에서 사용하는 커맨드는 다음과 같은 동작을 한다.

파일 및 디렉토리 추가

환경 변수 만들기

대상 이미지를 사용하여 컨테이너를 시작할 때 어떤 프로세스를 실행


이런 내용들을 Dockerfile 라고 부르는 파일에 저장해두면 Docker는 이 명령들을 토대로 실행해서 최종 이미지를 생성해주게 된다.




Dockerfile 작성을 위한 준비


Dockerfile 을 작성하기에 앞서, Express Generator 를 활용해서 Docker 위에 올릴 간단한 Express 기반 웹 어플리케이션을 생성해보자. (로컬에 node.js 가 설치되어 있지 않다면, https://nodejs.org/ 에서 다운로드 받아 설치하자)

# Express Generator 설치
$ npm install --global express-generator

# Express Generator 로 express-example 이라는 샘플 프로젝트 생성
$ express express-example

# 생성된 express-example 프로젝트로 이동
$ cd express-example

# 디펜던시 설치
$ npm install

# express-example 프로젝트 실행
$ npm start


위와 같이 입력하면, Express Generator 를 설치한 뒤, 'express-example' 이라는 폴더에 express 기반 웹 어플리케이션의 boilerplate 가 생성된다. 특별한 문제가 없다면, 아래와 같이 express-example 폴더로 이동해서 "npm start" 를 입력하면 로컬에 3000번 포트로 노드기반 웹 서버가 실행될 것이다.


이제 브라우저에서 localhost:3000 에 접속하면 "Welcome to Express" 라는 메시지를 볼 수 있다.




Dockerfile 작성하기


Dockerfile 은 앞서 이야기한 것처럼 Docker 이미지 생성을 위한 일종의 배치 파일이다. Ubuntu 이미지가 기본 베이스라면, node.js 기반 프로그램을 실행하기 위해서는 node.js 를 설치하고 해당 프로그램의 소스코드를 가져오고, npm 으로 디펜던시를 설치하고, 적당한 환경변수를 설정한 뒤 npm start 등의 커맨드로 프로그램을 실행할 것이다.


이런 과정들을 하나하나의 명령으로 표현한 것이 Dockerfile 이다. 아래의 Dockerfile 예를 살펴보자.

#어떤 이미지로부터 새로운 이미지를 생성할지를 지정
FROM node:6.2.2

#Dockerfile 을 생성/관리하는 사람
MAINTAINER Jaeha Ahn <eu81273@gmail.com>

# /app 디렉토리 생성
RUN mkdir -p /app
# /app 디렉토리를 WORKDIR 로 설정
WORKDIR /app
# 현재 Dockerfile 있는 경로의 모든 파일을 /app 에 복사
ADD . /app
# npm install 을 실행
RUN npm install

#환경변수 NODE_ENV 의 값을 development 로 설정
ENV NODE_ENV development

#가상 머신에 오픈할 포트
EXPOSE 3000 80

#컨테이너에서 실행될 명령을 지정
CMD ["npm", "start"]



Dockerfile에 포함된 주석까지 읽어보면 대부분의 경우 어떻게 Dockerfile 을 작성해야 할 지 감이 올 것이다.



#어떤 이미지로부터 새로운 이미지를 생성할지를 지정
FROM node:6.2.2

먼저 어떤 이미지를 베이스로 할지를 FROM 명령으로 지정한다. 여기에서는 node:6.2.2 를 지정했는데, 이미지의 이름은 node 이고 태그명은 6.2.2 라는 뜻이고 결과적으로 node.js 6.2.2 버전이 설치된 linux 이미지를 기반으로 Docker 이미지가 생성된다.



#Dockerfile 을 생성/관리하는 사람
MAINTAINER Jaeha Ahn <eu81273@gmail.com>

그 다음 라인은 Dockerfile 을 생성/관리하는 사람의 정보이다.



# /app 디렉토리 생성
RUN mkdir -p /app
# /app 디렉토리를 WORKDIR 로 설정
WORKDIR /app
# 현재 Dockerfile 있는 경로의 모든 파일을 /app 에 복사
ADD . /app
# npm install 을 실행
RUN npm install

그 다음은 루트에 app 이라는 디렉토리를 생성하고 여기에 현재 Dockerfile 이 있는 경로의 모든 파일을 app 디렉토리에 복사한 뒤 npm install 명령으로 디펜던시를 설치하는 부분이다.



#환경변수 NODE_ENV 의 값을 development 로 설정
ENV NODE_ENV development

그 다음은 환경변수를 설정하는 부분이다. 여기에서는 NODE_ENV 값을 development 로 설정했다.



#가상 머신에 오픈할 포트
EXPOSE 3000 80

가상 머신에서 오픈해줄 포트를 지정한다. Express Generator로 만들어준 샘플 프로젝트의 경우 development 에서는 3000번을 사용하고 production 에서는 80을 사용하므로 3000 과 80 을 모두 오픈할 포트로 지정했다.



#컨테이너에서 실행될 명령을 지정
CMD ["npm", "start"]

이제 모든 실행을 위한 구성이 갖춰졌으므로 실행을 위해 npm start 커맨드를 실행하는 것을 볼 수 있다.

주의해야할 점은 마지막에 실행하는 커맨드는 반드시 foreground 로 실행되어야 한다는 점이다. background 로 실행되게 되면 Docker는 해당 Docker 컨테이너의 실행이 종료된 것으로 인지한다.



이외에도 Dockerfile 에서 사용할 수 있는 명령들은 아래의 레퍼런스 페이지를 참고하도록 한다.


https://docs.docker.com/engine/reference/builder/





Dockerfile 빌드하기


build 명령을 사용해서 이렇게 만들어진 Dockerfile 을 빌드할 수 있다. 


아래와 같이 원하는 레파지토리명과 이미지명, 태그명을 지정하고 빌드대상디렉토리를 지정해서 실행하면 Dockerfile 을 기반으로 Docker 이미지가 생성된다.

$ docker build -t 레파지토리명/이미지명:태그명 빌드대상디렉토리


build 명령을 사용해서 Dockerfile 을 빌드하면 아래와 같이 진행된다.

Sending build context to Docker daemon 17.92 kB
Step 1 : FROM node:6.2.2
6.2.2: Pulling from library/node
5c90d4a2d1a8: Pull complete 
ab30c63719b1: Pull complete 
c6072700a242: Pull complete 
abb742d515b4: Pull complete 
22efa86cdb65: Pull complete 
a379ffacc05f: Pull complete 
Digest: sha256:67123dcbed68c55296aa04bdbe85440a27c74481e2668aafd66e2a11934bb15d
Status: Downloaded newer image for node:6.2.2
 ---> 9121f2a78909
Step 2 : MAINTAINER Jaeha Ahn <eu81273@gmail.com>
 ---> Running in 7cfd351d60d2
 ---> cfcaa99f2378
Removing intermediate container 7cfd351d60d2
Step 3 : RUN mkdir -p /app
 ---> Running in 9543c10c49d1
 ---> 927cb86da237
Removing intermediate container 9543c10c49d1
Step 4 : WORKDIR /app
 ---> Running in c1555ee5fa20
 ---> d726ddab4343
Removing intermediate container c1555ee5fa20
Step 5 : ADD . /app
 ---> 39c0d18e976e
Removing intermediate container c39433e610e1
Step 6 : RUN npm install
 ---> Running in f842c8850d0a
npm info it worked if it ends with ok
npm info using npm@3.9.5
npm info using node@v6.2.2
npm info attempt registry request try #1 at 9:48:22 AM

... 생략 ...

+-- morgan@1.7.0 
| +-- basic-auth@1.0.4 
| `-- on-headers@1.0.1 
`-- serve-favicon@2.3.0 

npm info ok 
 ---> e1bbe3527efb
Removing intermediate container f842c8850d0a
Step 7 : ENV NODE_ENV development
 ---> Running in 84459ea0ba70
 ---> 03295705245a
Removing intermediate container 84459ea0ba70
Step 8 : EXPOSE 3000 80
 ---> Running in c24756dde2ff
 ---> 96ff54107e4a
Removing intermediate container c24756dde2ff
Step 9 : CMD npm start
 ---> Running in 9e48d2bfde0e
 ---> 8593f49e1ec0
Removing intermediate container 9e48d2bfde0e
Successfully built 8593f49e1ec0




Dockerfile 로 생성한 뒤 실행


이제 Dockerfile 을 기반으로 생성된 이미지로 컨테이너를 실행할 때는 run 명령어를 사용하는데, 우리가 예제로 만든 프로젝트는 포트번호를 지정해줄 필요가 있기 때문에 -p 옵션을 추가해서 실행해야 한다. 포트번호 외에도 -e color=red 옵션을 통해 실행할 때 환경 변수를 추가해줄수도 있다.

$ docker run -d -p 8080:3000 생성한이미지/태그명

-d 옵션은 -i 옵션의 반대로 컨테이너가 백그라운드에서 실행되도록 한다. 또 앞서 이야기한대로 -p 옵션은 포트포워딩 옵션으로 컨테이너 외부에서 8080 포트로 접근하면 내부에서는 3000으로 연결하게 된다. 우리가 예제로 만든 프로젝트의 경우 development 모드에서는 3000번 포트로 실행되는데 이렇게 -p 옵션을 활용해서 외부와 내부 포트를 연결해줄 수 있다.


이제 localhost:8080 혹은 192.168.99.100:8080 으로 접속하면 반가운 "Welcome to Express" 메시지를 볼 수 있다.




컨테이너의 로그보기


이제 Dockerfile 로 생성한 이미지를 실행했다면, 컨테이너 상의 로그를 보고 싶을 수 있다.

$ docker logs --follow 해시

이때는 위와 같이 logs 명령을 사용해서 확인할 수 있다.

npm info it worked if it ends with ok
npm info using npm@3.9.5
npm info using node@v6.2.2
npm info lifecycle express-example@0.0.0~prestart: express-example@0.0.0
npm info lifecycle express-example@0.0.0~start: express-example@0.0.0

> express-example@0.0.0 start /app
> node ./bin/www

GET / 200 255.653 ms - 170
GET /stylesheets/style.css 200 4.077 ms - 111
GET /favicon.ico 404 41.693 ms - 855




마치며 


지금까지 간단한 node.js application 을 위한 Dockerfile 을 직접 작성해서 Docker 기반으로 node.js application 을 배포하기 위한 과정을 살펴보았다. 여기에서는 로컬에 있는 소스코드를 기반으로 Docker 이미지를 생성했지만, Dockerfile 에 소소코드를 Git에서 가져오는 부분까지 추가한다면 로컬의 상태와 관계없이 Dockerfile 만으로 해당 애플리케이션이 실행되기 위한 모든 요소를 구성하고 실행까지 할 수 있게 된다.


많은 경우 이런 방식으로, Git에 커밋이 이루어지면 Git Hook을 통해 Dockerfile 에 기술된대로 커밋된 소스를 Git에서 받아 Docker 이미지를 생성하고 생성된 이미지를 기준으로 테스트를 수행해서 테스트를 통과하면 운영에 배포하는 형태로 개발과 배포까지의 프로세스를 Docker 기반으로 만들어 활용하고 있다.


아주 작은 부분이지만 Docker 를 활용하는 방법에 대해 이해하는데 도움이 되었기를 기대한다.

Docker 관련 포스팅 더보기