본 글은 Compose file format 3.8+ 버전을 보고 작성하였습니다.
Docker Compose는 다중 컨테이너로 구성된 애플리케이션을 관리하기 위한 오케스트레이션(Orchestration) 도구이다.
YAML
파일을 사용하여 애플리케이션 서비스를 구성(명세)함Dockerfile
에 애플리케이션을 작성. 이를 통해 어디에서나 재현가능한 환경을 제공함compose.yml
에 정의하여 함꼐 실행될 수 있도록 함.Compose를 사용하기 전에 Compose 파일에 대해 먼저 알아보자.
Compose 파일은 Docker 애플리케이션의 서비스, 네트워크, 볼륨 등을 정의(명세) 하는 YAML 파일이다. 이를 통해 플랫폼에 구애받지 않는, 재현가능한 컨테이너 기반 애플리케이션을 정의할 수 있다.
compose.yaml
혹은 compose.yml
이다.docker-compose.yaml
도 호환되지만, 두 파일이 같이 있는 경우 기본양식이 선호됨Compose 파일의 최상위 Level에는 다음과 같은 구조를 포함할 수 있다.
이들이 동작되는 예시를 살펴보자. 다음과 같은 구조를 가진 서비스가 있다고 해보자.
(External user) --> 443 [frontend network]
|
+--------------------+
| frontend service |...ro...<HTTP configuration>
| "webapp" |...ro...<server certificate> #secured
+--------------------+
|
[backend network]
|
+--------------------+
| backend service | r+w ___________________
| "database" |=======( persistent volume )
+--------------------+ \_________________/
이들의 구성은 다음과 같다.
이들을 위한 Compose file은 다음과 같이 구성될 것임.
services:
frontend:
image: awesome/webapp
ports:
- "443:8043"
networks:
- front-tier
- back-tier
configs:
- httpd-config
secrets:
- server-certificate
backend:
image: awesome/database
volumes:
- db-data:/etc/data
networks:
- back-tier
volumes:
db-data:
driver: flocker
driver_opts:
size: "10GiB"
configs:
httpd-config:
external: true
secrets:
server-certificate:
external: true
networks:
# The presence of these objects is sufficient to define them
front-tier: {}
back-tier: {}
이제 Compose의 top-Level component들 중에서 가장 자주사용하는 services, volumess, networks 대해서 자세히 알아보자.
본 글에서는 자주 사용할 것 같은 명령어만 정리한다. 나머지는 공식 문서를 참고하자.
Profiles은 선택적으로 서비스를 활성화할 수 있도록 도와주는 속성이다.
예를 들어보자.
services:
foo:
image: foo
bar:
image: bar
profiles:
- test
baz:
image: baz
depends_on:
- bar
profiles:
- test
zot:
image: zot
depends_on:
- bar
profiles:
- debug
foo
는 항상 활성화됨test
프로필만 활성화된다면, 해당하는 프로필에 속해있는 bar
와 baz
도 실행됨(foo
는 고정적으로 실행되는 중..)debug
프로필만 활성화되면, zot
이 실행되어야 하지만, bar
에 종속되어있으므로 실행되지 않음debug
과 test
가 모두 활성화되면, 위에 있는 모든 서비스가 활성화됨.bar
를 명시적으로 실행하면, profiles을 활성화하지 않아도 해당 서비스(bar
)와 해당 프로필(test
)이 활성화됨baz
를 명시적으로 실행하면, 해당 서비스와 해당 프로필이 활성화되며, depends_on이므로 bar
도 실행됩니다.zot
을 명시적으로 실행하면, 해당 서비스와 해당 프로필이 활성화됩니다. 하지만 depends_on에 의해 bar
를 실행하려 할 때 해당 profile(test
)이 zot
의 profile(debug
)다르므로, 실행될 수 없습니다. 따라서 결국 zot
은 실행되지 않습니다.bar
의 profiles에 test를 추가로 포함시켜 해결할 수 있습니다.image는 컨테이너를 시작할 이미지를 지정한다.
services:
# 이미지 지정은 다음과 같은 양식(도커 빌드할 때와 같음)으로 할 수 있다.
image: redis
image: redis:5
image: redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7
image: library/redis
image: docker.io/library/redis
image: my_private.registry:5000/redis
(image가 없다면) build는 컨테이너 이미지를 생성하기 위한 명령어이다. 해당 명령어를 통해 dockerfile을 자동으로 빌드하여 컨테이너를 생성하고, 이를 사용할 수 있다.
services:
frontend:
image: awesome/webapp
build: ./webapp
backend:
image: awesome/database
build:
context: ../backend
dockerfile: Dockerfile
args:
<abc>:123
awesome/webapp
도커 이미지는 ./webapp
이라는 하위폴더로부터 이미지를 생성한다. 해당 폴더에는 반드시 dockerfile이 있어야 한다.awesome/database
도커 이미지는backend
라는 상위폴더로부터 이미지를 생성한다.depends_on은 서비스 간 종속성을 나타냅니다.
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
redis
&db
→ web
web
→ redis
&db
container_name은 컨테이너 이름을 지정해줄 수 있다. (지정하지 않는다면 default이름으로 지정됨)
container_name: my-web-container
cgroup_parent는 컨테이너에 대해 상위 cgroup을 지정해줄 수 있음
cgroup_parent: m-executor-abcd
depoly는 서비스에 대한 런타임 요구사항들을 정의할 수 있다.
(간략하게 설명하고 넘어가는 component들은 docs를 참고)
services:
frontend:
image: awesome/webapp
ports:
- "8080:80"
deploy:
mode: replicated
replicas: 2
endpoint_mode: vip
replicated
(default) : 복제 모델을 정의함. 이를 통해 동일한 코드를 실행하는 여러 컨테이너를 가질 수 있음.replicas
로 복제할 개수를 설정함global
: 단 하나의 모델만 정의함services:
frontend:
image: awesome/webapp
deploy:
mode: replicated
replicas: 6
limits
: 여기에 해당하는 만큼 리소스를 제한할 수 있음reservations
: 여기에 해당하는 만큼의 리소스 이상을 할당한다는 것을 보장함.services:
frontend:
image: awesome/webapp
deploy:
resources:
limits:
cpus: '0.50'
memory: 50M
pids: 1
reservations:
cpus: '0.25'
memory: 20M
2b
, 1024kb
, 2048k
, 30m
, 1gb
capabilities
에는 사용할 기능을 문자열로 입력 ([gpu] 혹은 [tpu], …) → 필수로 입력해야 하는 필드임(이것만 입력하면 모든 gpu 사용함)count
에는 사용할 장치 개수(int) 혹은 all 입력device_ids
에는 호스트의 장치 id를 입력]driver
에는 문자열로 지정된 값을 넣음# 1
services:
test:
image: nvidia/cuda:10.2-base
command: nvidia-smi
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
# 2
services:
test:
image: tensorflow/tensorflow:latest-gpu
command: python -c "import tensorflow as tf;tf.test.gpu_device_name()"
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['0', '3']
capabilities: [gpu]
none
, on-failure
, any
(default)deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
comman는 Dockerfile에서의 CMD 명령을 재정의(overrides)함.
command: bundle exec thin -p 3000
command: [ "bundle", "exec", "thin", "-p", "3000" ]
entrypoint는 Dockerfile에서의 ENTRYPOINT 명령을 재정의(override)함.
entrypoint: /code/entrypoint.sh
environment는 컨테이너에 설정된 환경변수를 정의함. 작성 형식은 다음과 같이 두 가지가 있음
# 1
environment:
RACK_ENV: development
SHOW: "true"
USER_INPUT:
# 2
environment:
- RACK_ENV=development
- SHOW=true
- USER_INPUT
env_file은 파일 내용을 기반으로 환경 변수를 추가함(만약 environment
와 함께 있다면, environment
가 우선순위를 가짐).
env_file: .env
ports는 컨테이너 포트를 노출한다.. (network_mode: host
와 함께 사용하면 안 됨)
ports:
- "3000"
- "3000-3005"
- "8000:8000"
- "9090-9091:8080-8081"
- "49100:22"
- "127.0.0.1:8001:8001"
- "127.0.0.1:5000-5010:5000-5010"
- "6060:6060/udp"
expose는 호스트 머신에 대한 포트는 공개하지 않고, 포트를 노출하고 싶은 경우(ex. links 기능 사용)에 사용한다.
expose:
- "3000"
- "8000"
links는 다른 서비스의 컨테이너에 접근할 수 있도록 별명을 설정할 수 있다.
(사용하지 않더라도 한 네트워크 안에 있는 서비스끼리는 해당 서비스 이름으로 통신이 가능함)
web:
links:
- db
- db:database # [service:alias]를 통해 별칭으로도 접근할 수 있음
- redis
TTY와 함께 실행되도록 서비스 컨테이너를 구성한다.
service:
service_name:
tty: true
컨테이너와 서비스의 로깅에 관한 설정을 한다.
drive
에는 드라이버 종류를 명시하고, option
s에서는 옵션을 키-값 쌍으로 설정합니다.logging:
driver: syslog
options:
syslog-address: "tcp://192.168.0.42:123"
restart는 컨테이너가 종료될 시 재시작 여부와 방법에 대해 지정한다.
restart: "no" # 재시작하지 않음
restart: always # 항상 재시작
restart: on-failure # exit code가 error를 나타내는 경우만 재시작
restart: unless-stopped # 서비스가 중지되거나 제거되기 전까지 항상 재시작
working_dir은 Dockerfile의 WORKDIR로 지정된 컨테이너의 작업 디렉토리를 재정의(override)한다.
service:
service_name:
working_dir: /home/user/workspace
네트워크는 서비스가 서로 통신할 수 있도록 하는 layer이다.
# 예를 들어 다음과 같이 fromt-tier와 back-tier를 생성했다면
# service 하위 섹션에 이 이름을 명시하여 연결할 수 있음
services:
frontend:
image: awesome/webapp
networks:
- front-tier
- back-tier
networks:
front-tier:
back-tier:
compose.yaml
파일이 있는 디렉토리의 이름을 기반으로 생성됨.my_app_default
가 됨<host_port>:<container_port>
)이다.container_port
를 사용해야 하며, 호스트에서 접속할 때에는 host_port
를 사용해야 한다. host_port
를 사용할 경우 스웜 밖에서도 접근이 가능하다.driver는 새로 생성한 네트워크에서 사용할 드라이버를 지정함
networks:
mynet1: # 네트워크 별칭
driver: overlay
mynet2:
driver: bridge
만약 attachable이 true라면, 다른 독립 실행된 컨테이너에서도 이 네트워크에 접근할 수 있다. 독립 실행된 컨테이너가 이 네트워크에 연결되면, 네트워크에 연결된 서비스와 통신할 수 있다.
networks:
mynet1:
driver: overlay
attachable: true
internal이 True면, 외부로부터 격리된 네트워크를 생성할 수 있다.
external이 True이면, 외부에서 생성된 network를 사용한다는 의미이다. 만약, 해당하는 network가 없다면 오류를 뱉는다.
services:
frontend:
image: awesome/webapp
networks:
- outside_network # 외부에 있는 outside_network라는 이름의 네트워크 사용
networks:
outside_network:
external: true
volume은 영구적으로 데이터를 저장하기 위한 layer이다.
services:
backend:
image: awesome/database
volumes:
- db-data:/etc/data
backup:
image: backup-service
volumes:
- db-data:/var/lib/backup/data
volumes:
db-data:
external이 True이면, 외부에서 생성된 volume를 사용한다는 의미이다. 만약, 해당하는 volume가 없다면 오류를 뱉는다.
services:
frontend:
image: awesome/webapp
networks:
- outside_volume # 외부에 있는 outside_network라는 이름의 volume 사용
volumes:
outside_volume:
external: true