기존에 Docker 컨테이너간에 서로 역할이 분리되어
하나의 서비스를 위해서 Docker 컨테이너를 몇개 생성하려 한다면 아래와 같이 입력해야 한다
그리고 컨테이너가 서로 연동이 되어야 한다면 컨테이너 사이의 링크 설정도 잘 해야 한다.
이런 불편함을 일부 해소할 수 있는 툴이 Docker Compose 라는 툴이다.
이 툴은 컨테이너 사이의 설정 과 docker container run 에서 사용하는
볼륨, 네트워크, 포트 정보를 docker-compose.yml 파일에 설정하여 멀티 컨테이너를 구동시킬 수 있다.
YAML
Docker Compose 에서 사용하는 파일은 YAML 형식의 파일이다.
아래와 같이 YAML 문법에 대한 간단한 내용 및 샘플을 첨부한다.
YAML 문법
YAML 문법 확인
docker compose 지시자
docker-compose.yml 파일에는 반드시 image 나 build 지시자가 있어야 한다.
주요 키로는 아래와 같은 것들이 있다.
image
|
Docker Image 를 입력
|
build
|
Dockerfile 이 있는 디렉토리의 경로를 입력
|
command
|
Dockerfile 이나 image 의 시작 command 를 오버라이드
|
links
|
컨테이너를 다른 서비스들과 통신 가능하게 연결하고 종속성을 설정
|
extra_links
|
컨테이너를 compose 파일 외부에 있는 서비스와 연결
|
extra_hosts
|
컨테이너의 /etc/hosts 에 외부 호스트정보를 추가
|
ports
|
컨테이너에서 호스트 와 통신할 포트를 지정
|
expose
|
호스트에는 노출하지 않고 연결된 서비스끼리만 접근 가능한 포트 입력
|
volumes
|
현재 디렉토리를 볼륨으로 마운트할 때 컨테이너에서 사용할 경로를 입력
|
volumes_from
|
다른 서비스나 컨테이너의 전체 볼륨을 마운트 한다.
|
depends_on
|
서비스 간에 종속성을 설정
|
deploy
|
도커 스웜에서 사용되는 여러 옵션들을 정의함
|
secrets
|
패스워드 등의 secret 정보를 저장시 사용
|
version
도커 컴포즈 파일 버전명 입력시 version 키를 사용한다.
services
관련있는 컨테이너의 묶을 경우 services 키를 사용한다.
예를 들어 웹 서비스를 위해 FrontEnd, Application, DB 같이 3가지 컨테이너가 필요하며
이를 services 하위에 정의해 준다.
image
Docker 컨테이너의 기반이 되는 베이스 이미지를 지정하기 위한 키로
로컬에 이미지가 없으면 그 이미지를 원격 저장소에서 Pull 해서 가져온다.
webserver:
image: ubuntu
webserver2:
image: ewshin/dockersample:1.0
|
build
Dockerfile 을 사용해서 이미지를 빌드 할 경우 build 키를 사용한다.
해당 키에는 Dockerfile 이 있는 디렉토리의 경로를 입력한다.
# docker-compose.yml 파일을 사용시
services:
webapp:
build: ./dir
# docker-compose.yml 대신 다른 대체 파일 사용시
services:
webapp:
build:
context: ./dir
dockerfile: Dockerfile-alternate
# 도커 파일 내부에 파라미터가 있는 경우 인자 전달 방법
## Dockerfile
...
ARG buildno
RUN echo "Build number: $buildno"
## docker-compose.yaml
services:
webapp:
build:
context: ./dir
args:
- buildno: 1
|
command
컨테이너 내에서 동작하는 커맨드 지정시 사용시 command 키를 사용한다.
도커 파일 내부에 있는 CMD 가 이미 있는 경우 이를 Override 한다.
webserver:
image: ubuntu
command: /bin/bash
|
entrypoint
컨테이너 내에서 동작하는 엔트리 포인트 지정시 entrypoint 키를 사용한다.
도커 파일 내부에 있는 ENTRYPOINT 가 이미 있는 경우 이를 Override 한다.
webserver:
image: ubuntu
entrypoint: /code/entrypoint.sh
|
아래와 같이 명령을 직접 실행할 수도 있다.
configuration_generator:
image: java:8-jdk
entrypoint:
- java
- -cp
- zookeeper-vertx-example-0.0.1-fat.jar
- com.lukeolbrish.example.ConfigurationGenerator
|
links
컨테이너를 다른 서비스들과 연결시 links 키를 사용한다.
그러면 이 컨테이너의 /etc/hosts 파일에 그 내용이 추가되어서
컨테이너에서 다른 컨테이너 들에 접근할 수 있게 된다.
links:
- db
- db:database
- redis
|
위와 같이 설정을 하게 되면 /etc/hosts 파일에 내용이 아래와 같이 변경되어
링크된 컨테이너와 통신할 수 있게 된다.
192.168.1.186 db
192.168.1.186 database
192.168.1.187 redis
|
또한 링크는 depends_on과 같은 방식으로 서비스 간의 종속성을 표현하므로 서비스 시작 순서를 결정한다.
( 즉 links 에 기술한 서비스가 먼저 시작된다. )
external_links
links 와 비슷한 방식으로 설정하지만 compose 내부의 컨테이너가 아니라
compose 외부에 있는 서비스 혹은 컨테이너와 연결시 external_links 키를 사용한다.
external_links:
- redis
- project_db:mysql
|
external_hosts
컨테이너의 /etc/hosts 에 외부 호스트정보를 추가시 external_hosts 키를 사용한다.
extra_hosts:
- "somehost: 162.168.2.31"
|
위와 같이 설정을 하게 되면 /etc/hosts 파일에 내용이 아래와 같이 변경된다.
162.168.2.31 somehost
|
volumes
컨테이너에 볼륨을 마운트 시 volumes 키를 사용한다.
볼륨을 ready only 으로 마운트 하고자 하는 경우 :ro 를 붙임 (설정 파일등에 유용)
볼륨을 ready write 로 마운트 하고자 하는 경우 :rw 를 붙임
type 이 volume 인 경우 NamedVolume 을 말하고 type 이 bind 인 경우
정적 bind ( 호스트 볼륨 ) 및 동적 bind (source 를 비워놓음) 를 사용할 수 있다.
version: "3.2"
services:
web:
image: nginx:alpine
volumes:
# Named Volumes
- type: volume
source: mydata
target: /data
volume:
nocopy: true
# Host Volumes
- type: bind
source: ./static
target: /opt/app/static
db:
image: postgres:latest
volumes:
#SHORT SYNTAX
- "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
- "dbdata:/var/lib/postgresql/data"
# Named Volumes
volumes:
mydata:
dbdata:
|
environment
컨테이너 내의 환경 변수 지정시 environment 키를 사용한다.
#배열 형식으로 지정
environment:
- HOGE=fuga
- FOO
#hash 형식으로 지정
environment:
HOGE: fuga
FOO:
|
env_file
환경변수를 정의한 파일을 호출하는 파일을 지정시 env_file 킬르 사용한다.
설정하고자 하는 환경 변수가 많은 경우 env_file 에 환경 변수값을 넣어놓고
이를 가져다 사용하는 경우 필요하다.
#환경변수 파일 읽기
env_file: .env
#여러 개의 환경변수 파일 읽기
env_file:
- ./common.env
- ./apps/web.env
- /opt/secrets.env
|
depends_on
서비스간 명시적 종속성을 부여하고자 하는 경우 depends_on 키를 사용한다.
아래와 같이 설정하면 db와 redis 시작전에 시작 web 이 먼저 시작된다.
다른 방법으로는 links 로 다른 컨테이너와 연결 관계를 설정하면
암시적으로 연결이 필요한 컨테이너를 먼저 구동시킨다.
version: '3'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
|
devices
호스트의 장치를 컨테이너에서 사용할 수 있도록 연결시 devices 키를 사용한다.
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
|
ports
<컨테이너 포트> 혹은 <호스트 포트>:<컨테이너 포트> 형식으로
컨테이너에서 호스트 와 통신할 포트를 지정시 ports 키를 사용한다.
# 호스트 포트와 컨테이너 포트를 둘 다 지정하는 경우
mysql:
image: mysql:5.7
ports:
- 3333:3306
# 컨테이너 포트만 지정하는 경우
mysql:
image: mysql:5.7
ports:
- 3306
|
호스트 포트를 생략하고 컨테이너 포트만 지정하면 호스트 포트는 랜덤으로 지정된다.
# 호스트 포트와 컨테이너 포트를 둘 다 지정하는 경우
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------
mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3333->3306/tcp
# 컨테이너 포트만 지정하는 경우
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------
mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:32769->3306/tcp
|
expose
호스트에는 노출하지 않고 연결된 서비스끼리만 접근 가능한 포트를 입력시 expose 키를 사용한다.
이 포트는 호스트에서 접근 할 수 없고 docker-compose up 시 연결할 포트를 지정한다.
dockerfile 의 EXPOSE 와 동일하다.
mysql:
image: mysql:5.7
expose:
- 3306/tcp
|
아래와 같이 host 쪽과 연결 정보가 없다.
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------
mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp
|
dns
컨테이너에서 사용할 DNS 서버를 설정하려는 경우 dns 키를 사용한다.
dns:
- 8.8.8.8
- 9.9.9.9
|
dns-search
컨테이너에서 사용할 DNS 검색 도메인을 설정하는 경우 dns-search 키를 사용한다.
dns_search:
|
network-mode
컨테이너의 네트워크 모드를 설정하는 경우 network-mode 키를 사용한다.
# Docker 네트워크 브리지에 새 네트워크를 생성
network_mode: "bridge"
# 컨테이너 안에서 호스트의 네트워크를 그대로 사용
network_mode: "host"
# 네트워크를 사용안함
network_mode: "none"
# 다른 서비스의 네트워크를 함께 사용
network_mode: "service:[service name]"
# 다른 컨테이너의 네트워크를 함께 사용
network_mode: "container:[container name/id]"
|
networks
네트워크를 구성하기 위한 최상위 키, 이하 옵션 키들을 조합하여 네트워크를 지정해야 한다.
driver
bridge / overlay / host / none 인 경우를 지정하는데 host 나 none 인 경우는 network_mode 를 사용하는 방식이 더 편하다.
version: "3.3"
services:
wordpress:
image: wordpress
ports:
- "8080:80"
networks:
mynet1:
driver: overlay
networks:
mynet1:
|
이외 옵션들이 많이 있다.
페이지에서 확인하자.
healthcheck
서비스의 상태를 체크하려는 경우 healthcheck 키를 사용한다.
# 컨테이너가 시작하고 40초 후부터 1분 30 초마다 웹 서버가 10초 이내에
# 기본 페이지를 제공하는 확인
# 실패시 3번 재시도 함
#
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
|
secrets
v 3.1 이상에서 사용할 수 있다.
서비스에 암호화가 필요한 데이터를 넣으려는 경우 secrets 키를 사용하면 된다.
# psql_user, psql_password 를 암호화
# 해당 데이터를 environment 에서 사용함
#
version: "3.1"
services:
psql:
image: postgres
secrets:
- psql_user
- psql_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/psql_password
POSTGRES_USER_FILE: /run/secrets/psql_user
secrets:
psql_user:
file: ./psql_user.txt
psql_password:
file: ./psql_password.txt
|
사실 파일을 사용하기 보다는 secret 에 들어가는 것은 바로 만들어 사용해야 한다.
# psql_user, psql_password 를 암호화
# 해당 데이터를 environment 에서 사용함
#
version: "3.1"
services:
psql:
image: postgres
secrets:
- psql_user
- psql_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/psql_password
POSTGRES_USER_FILE: /run/secrets/psql_user
secrets:
psql_user:
external: true
psql_password:
external: true
|
그리고 secret 오브젝트를 CLI 에서 만들면 된다.
# echo "<string>" | docker secret create psql_user - <value>
$ echo "aaafba" | docker secret create psql-password -
#asdoaiuer34543490$#apdf
$ echo "aaafba" | docker secret create psql-user -
wer$%$#%&fgsfgsdf456ghj
|
Docker Compose CLI
주요 명령으로 아래와 같은 것들이 있다.
# Help
docker-compose --help
# volumes/networks 를 셋팅하고 모든 컨테이너를 시작
docker-compose up
# 다른 경로에 있는 yml 파일 사용시 -f 옵션 사용
docker-compose -f ./sample/docker-compose.yml up
# 컨테이너를 백그라운드에서 구동
docker-compose up -d
# 모든 컨테이너를 스톱하고 cont/vol.net 을 제거함
docker-compose down
# 실행중인 컨테이너 표시
docker-compose ps
# 실행중인 컨테이너의 프로세스 표시
docker-compose top
|
Docker Compose Version
V1 은 기본 컨테이너 네트워크로 기본 브리지 네트워크를 사용하여
만약에 사용자가 다른 network 를 사용하려고 하면 이를 표현하는 지시자가 없고
컨테이너간 종속성을 관리하기 위한 지시자도 없었다.
V2 는 다양한 네트워크를 정의할 수 있고 컨테이너 종속성도 관리할 수 있도록 지시자를 추가했다.
그리고 version 지시자를 추가했고 services 라는 지시자 아래 서비스를 기술하도록 바뀌었다.
V3 는 V2 와 비슷하지만 V3 에서는 Docker Swarm 을 지원하며 V2 에서 지원하던 몇몇 지시자가 빠지거나 바뀌었다.
V3 의 지시자 부분은 Docker Swarm 부분을 정리할 때 다루기로 하자.
Exercise
docker run 으로 시작해서 서비스를 구동해보고
이를 docker-compose.yaml 에 기술하여 멀티 컨테이너를 구동하는 연습을 해보자.
해당 App 은 사용자가 voting-app 을 통해 투표를 하면
result-app 을 통해 통합 결과를 화면에 보여주는 구조로 되어 있다.
단순히 이 구조를 연결 링크 관계없이 아래와 같이 컨테이너만 구동하면 컨테이너간은
$ docker run -d --name=redis redis
$ docker run -d --name=db
$ docker run -d --name vote -p 5000:80 voting-app
$ docker run -d --name=result -p 5001:80 result-app
$ docker run -d --name=worker worker
|
다른 컨테이너의 존재를 모르므로 아래의 에러 메시지를 보게 될 것이다.
따라서 컨테이너 간에 연결 관계를 확인해 보면 다음과 같다.
voting-app (python) 웹 서버 코드 조각을 보면 웹 서버가 시작되면 redis 를 찾는다.
result-app (node-js) 웹 서버 코드 조각을 보면 웹 서버가 시작되면 db 를 찾는다.
worker (.NET) 웹 서버 코드 조각을 보면 백엔드 프로세스는 db 와 redis 를 모두 확인한다.
즉 각 링크 관계를 생각해보면 아래와 같은 구조로 링크가 맺어지므로
docker run 명령에 아래와 같이 --link 옵션을 더 주어야 한다.
$ docker run -d --name=redis redis
$ docker run -d --name=db
$ docker run -d --name vote -p 5000:80 --link redis:redis voting-app
$ docker run -d --name=result -p 5001:80 --link db:db result-app
$ docker run -d --name=worker --link db:db --link redis:redis worker
|
그리고 voting-app 에 접근해서 /etc/hosts 파일을 확인해 보면
아래와 같이 변경되어 있는 것을 확인할 수 있다.
이제 이 과정을 docker-compose.yml 파일에 녹여보자.
먼저 할일은 docker-compose.yml 파일을 만들고 컨테이너 이름을 넣는다.
그리고 각 컨테이너에서 사용한 이미지를 넣는다.
그 다음 사용한 포트 정보를 입력한다.
마지막으로 사용하는 링크 정보를 넣는다.
위의 경우 docker compose v1 기준으로 작성하였는데
여기에 더해 만약 네트워크를 사용자 트래픽을 받을 front-end 와 앱 통신에서 사용할
back-end 로 나누었다고 생각해 보자.
이 경우 아래와 같이 변경하면 된다.
그리고 나서 간단히 아래와 같이 구동하면 된다.
'Container > Docker' 카테고리의 다른 글
14. Docker Network Test (0) | 2020.01.14 |
---|---|
13. Docker Network Overview (0) | 2020.01.14 |
11. Docker Container Life Cycle (0) | 2020.01.14 |
10. Docker Image 생성 (도커파일) (0) | 2020.01.14 |
09. Docker Image 생성 (기존이미지 사용) (0) | 2020.01.14 |