Docker Compose를 사용하여 여러 컨테이너를 한번에 관리해보자
웹 앱을 개발 할 때 대부분의 경우는 앱과 데이터베이스의 두가지가 필요하다.
데이터베이스 부분을 DaaS와 같이 외부 서비스로 이용하는 경우도 있겠지만 자신의 서버에 직접 돌리는 경우도 많을 것이고, 데이터베이스를 컨테이너 안에서 돌리고 싶은 경우도 있을 것이다.
이번 포스트에서는 앱과 데이터베이스의 컨테이너를 동시에 관리 할 수 있는 docker compose에 대해 알아본다. 예제로 사용 할 앱은 node.js
와 MongoDB
를 사용하며 git 링크는 이곳.
#Docker Compose 설치
Docker Compose는 Docker를 설치하는 것 만으로 자동으로 설치가 되는 건 아니고, 따로 설치를 해 주어야 한다. 아래 커맨드를 실행하여 설치해준다.
sudo apt-get install docker-compose
#앱의 컨테이너 이미지 설정
우선 예제 앱을 git clone
으로 다운 받아준다. 다운 받았다면 nodejs-with-mongodb-api-example
이라는 폴더가 생성되어 있을 것이다. 이 폴더로 이동해 준 다음, 앱의 컨테이너 이미지 생성 설정을 위한 Dockerfile
을 만들어 준다.
#Dockerfile
#Base Image
FROM node:14-alpine
#디렉토리 설정
WORKDIR /app
#package.json을 우선적으로 복사
COPY package*.json ./
#패키지들을 설치해주고 캐쉬를 삭제
RUN npm install && npm cache clear --force
#나머지 앱 파일들 복사
COPY . .
#ts파일로 만들었기 때문에 js로 compile
RUN npm run build
우선 위와 같은 설정으로 해 준다. CMD
부분이 빠진 이유는 아래에 설명 할 docker-compose.yml
에서 설정 할 것이기 때문이다.
#docker-compose.yml 설정
여러 컨테이너에 대한 설정, 컨테이너를 동시 구동/정지 하고 싶을 때 사용하면 편리한게 Docker Compose
이다. 물론 각각 개별의 컨테이너를 따로 구동 시켜도 문제 될 건 없지만, 관리의 측면에서 Docker Compose
를 사용하면 더 편리하다.
Docker Compose
를 사용하기 위해서는 docker-compose.yml
이라는 설정 파일이 필요하다. 앱의 루트 디렉토리 안에 docker-compose.yml
파일을 만들어주자. 내용은 아래와 같다.
#Docker Compose의 syntax (문법) 버전 설정
version: '3'
#컨테이너화 할 서비스들의 리스트와 설정
services:
#앱 부분에 관한 설정
app:
build: . #docker-compose.yml이 있는 디렉토리와 같은 곳에 있는 Dockerfile로 이미지 빌드
container_name: app #컨테이너 이름 지정
command: npm run dev:watch #개발 환경 버전의 컨테이너 실행
ports:
- 4000:4000
environment:
MONGO_URL: mongodb #mongodb에 연결 할 아이피를 지정
PORT: 4000
volumnes:
- ./:/app #로컬에 있는 앱 관련 파일들과 연결되게 마운트
- /app/node_modules #로컬에 있는 node_modules와 연결되지 않게 아무 지정 없는 마운트
links:
- mongodb #mongodb 서비스(컨테이너)와 서로 연결
depends_on:
- mongodb #mongodb 컨테이너가 먼저 구동되고 완료 될 때까지 기다림
mongodb:
image: mongo
container_name: mongodb
ports:
- 27017:27017
이해를 돕기 위한 주석을 간단하게 추가 했지만, 중요한 부분을 따로 설명하도록 한다.
우선 맨 위의 버전은 docker-compose의 문법이 어느버전을 따를 것인가를 지정 해 주는 것이다. 자신의 환경에 설치된 Docker의 버전과 호환되는 버전을 지정해 줘야 한다. 호환성은 공식 사이트에서 확인 할 수 있는데 지금 시점에선 일단 3을 쓰면 크게 문제 될 건 없다.
services
부분에는 각각의 컨테이너에 대한 설정을 해준다. 이번 예제에서는 app
과 mongodb
두가지 서비스를 컨테이너화 하기 때문에 그에 대한 설정을 하였다.
컨테이너를 만들기 위해선 컨테이너 이미지가 필요하다. app
의 경우 직접 생성한 이미지를 사용 할 것이기 때문에 build
설정을 사용하였고, 이 설정에서는 Dockerfile
의 위치를 지정해 줘야 한다. 이번 예제에서는 docker-compose.yml
과 Dockerfile
가 같은 디렉토리에 있기 때문에 .
라고 설정하였다. mongodb
의 경우는 이미 존재하는 공식 mongodb
컨테이너 이미지를 사용하면 되기 때문에 build
가 아니고 image
설정을 사용했고, image
설정에는 이미지 파일을 지정해 줘야 한다.
container_name
은 본인이 나중에 알기 쉽게 각 서비스 컨테이너의 이름을 지정해주는 설정이다. 따로 설정하지 않으면 Docker가 알아서 디렉토리이름과 서비스 이름을 조합해서 이름을 만들어준다.
command
는 컨테이너를 구동했을 때 실행 할 커맨드를 지정해준다. 예제 앱 같은 경우 개발 환경은 npm run dev:watch
, 실제 라이브 환경은 npm start
를 이용한다. package.json
의 scripts
부분을 보면 알 수 있다. 개발 환경에서는 nodemon
을 이용해서 앱의 코드가 업데이트 되었을 때 자동으로 ts
를 js
로 컴파일 해주고 앱을 다시 구동해주는 일반적인 개발환경이 사용되어있다. 이번 예제에서는 개발환경을 컨테이너에 옮길 수 있다는 것도 보여주기 위해 npm run dev:watch
를 사용하였다.
app
의 environment
는 환경 변수의 값을 설정 해 줄 수 있는 설정이다. node.js
에서는 일반적으로 process.env.xxxx
이런식으로 불러 올 수 있는 변수의 값을 설정해주는 것이다. 여기서 사용된 MONGO_URL
이란 변수는 index.ts
의 22번째 줄을 보면 몽고디비의 호스트 설정하는 부분에서 MONGO_URL
변수를 사용하기 때문이다. 컨테이너가 아니고 로컬 환경에서 개발을 하고 mongodb를 사용한다면 일반적으로는 mongodb://localhost
로 연결하는게 일반적인데, 앱이 컨테이너에서 구동되고 있고, 또 다른 컨테이너에서 구동되고 있는 mongodb에 연결하기 위해서는 컨테이너안에 있는 mongodb에 접속 할 수 있는 주소를 사용해야한다. 컨테이너 안에 있는 mongodb는 localhost가 아니기 때문에 제대로 설정을 해주지 않으면 앱이 mongodb에 연결을 할 수 없다. 여기서 environment
에서 MONGO_URL
을 설정해주면 docker가 알아서 mongodb의 컨테이너 호스트 주소를 설정해주는 것이다.
volumes
부분에서 /app/node_modules
를 로컬파일과 마운트 하지 않은 이유는, node_modules의 환경설정이 OS에 따라 다르기 때문이다. 컨테이너안은 linux설정을 따르게 되는데, 본인의 로컬 개발환경이 MacOS 일 경우, 로컬과 컨테이너 안의 node_modules
를 마운트해버리면 호환성에 문제가 발생 할 수 있다. 이 문제를 방지하기 위해서 "Volume Trick"을 이용해서 익명 볼륨을 적용하면 로컬과 연결되지 않고 컨테이너의 node_modules
를 유지 할 수 있다. 컨테이너를 중단/파기하고 다시 시작해도 캐쉬된 node_modules
를 사용하는 것이 가능하게 된다.
links
는 컨테이너를 서로 연결해줌으로써 서로간의 통신이 가능하게 해 주는 설정이고, depends_on
은 app
컨테이너는 mongodb
컨테이너에 의존한다는 뜻이며, mongodb
컨테이너가 먼저 구동되야한다는 설정이다. 데이터베이스가 먼저 준비 되어 있지 않으면 앱이 데이터에 접속을 할 수 없기 때문에 당연한 설정이 된다.
#Docker Compose실행
docker-compose.yml
파일에 대한 설정이 끝났다면 이제 실행하기만 하면 된다.
docker-compose up --d
--d
플래그를 넣어주면 백그라운드에서 실행된다.
Starting mongodb ... done
Starting app ... done
이런식으로 mongodb 컨테이너가 먼저 준비되고 그 다음에 app 컨테이너가 구동되는 걸 알 수 있다. 4000번 포트로 앱이 잘 구동되는지 확인해보자.
마지막으로 컨테이너 안에 개발 환경이 구동되어 있는게 제대로 작동하는지 확인을 해보자. 로컬 환경에 있는 index.ts
파일을 열어서 38번째 줄의 title
값을 변경해보자. 값을 변경한 다음 앱 페이지를 새로고침 해보자.
로컬 파일과 컨테이너 파일이 서로 마운트 되어있기 때문에 로컬의 index.ts
를 수정하면 컨테이너 안에서도 수정된 index.ts
를 인식하고, 컨테이너 안에서 실행 중인 nodemon
이 파일 변경을 인식한 후 자동으로 컴파일 & 앱 재시작을 해주기 때문에 바로 변경사항이 반영되는 것이다.
컨테이너를 중단하고 싶을 땐 아래 커맨드로 한꺼번에 중단시킬 수 있다.
docker-compose down
Docker Compose의 사용법과 앱 개발환경을 컨테이너안에서 실행시키는 법에 대해 알아보았다. 실제 앱의 개발 환경을 컨테이너화 시킬 땐 환경 설정 변수를 알맞게 미리 코드안에 적용시켜 두는 것이 중요하다. 개발 환경까지 컨테이너화 시킬 수 있다면, 다른 개발자들과 협업 할 때 각자의 로컬 환경이 중요하지 않게 되기 때문에 매우 유용하게 사용 될 수 있다. 로컬 환경을 깨끗하게 관리 할 수 있는 건 덤 :)