Docker Compose
https://docs.docker.com/compose/
멀티 컨테이너 도커 애플리케이션을 정의하고 실행하는 도구
YAML 파일을 사용하여 애플리케이션의 서비스를 설정한다
자세한 내용은 레퍼런스 문서를 참고하자. 여기서는 실용적인 부분만 다룬다
Compose Version
V1은 더이상 지원을 하지 않는다
Docker Compose CLI는 현재 V2를 지원한다
설치
도커 설치하면 도커 컴포즈도 설치되어 있다
동작 방식
YAML 설정 파일에 의존한다
YAML 파일은 Compose Specification에 의해 제공되는 규칙을 따른다
아래에서 설명하겠다
명령
아주 쉽다. YAML 파일이 있는 위치에서 아래 두 가지 명령어만 사용하면 된다
docker compose up
애플리케이션 시작
docker compose down
컨테이너 중지 및 제거
Compose 파일
compose.yaml 또는 compose.yml 이름을 가진 파일이 필요하다
공식 문서에는 compose.yaml을 선호한다고 되어 있는데 나는 짧은 compose.yml을 주로 사용한다
Nginx를 예로 들어보겠다
https://hub.docker.com/_/nginx
최소한의 설정은 아래와 같다
mkdir learn-nginx && cd $_
vi compose.yml
services:
some-nginx:
image: nginx:1.27.0
ports:
- "80:80"
- some-nginx
- 컨테이너 서비스명. 원하는 이름으로 작성 가능하다
- image
- 컨테이너 이미지명과 버전 명시
- ports
- YAML에 List 형태로 입력해야 함
- 컨테이너의 Nginx의 80 포트를 호스트 OS의 80 포트에 Publish
- 이렇게 설정함으로써 호스트 OS에서 localhost:80으로 컨테이너에 접속 가능하다
테스트 해보자
docker compose up
실행 후 Ctrl + C를 눌러 중지할 수 있다
백그라운드로 실행하는 방법
docker compose up -d
[+] Running 8/8
✔ some-nginx 7 layers [⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 30.0s
✔ 559a76444520 Pull complete 9.3s
✔ 6d49e2cc7379 Pull complete 10.2s
✔ de7de5956f3a Pull complete 10.3s
✔ d3756208472d Pull complete 10.3s
✔ 8e17037a07ce Pull complete 12.2s
✔ 1879d5982048 Pull complete 12.2s
✔ c06f6b42c3b9 Pull complete 14.3s
[+] Running 2/2
✔ Network learn-nginx_default Created 0.0s
✔ Container learn-nginx-some-nginx-1 Created 0.2s
Attaching to learn-nginx-some-nginx-1
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
learn-nginx-some-nginx-1 | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
learn-nginx-some-nginx-1 | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
learn-nginx-some-nginx-1 | /docker-entrypoint.sh: Configuration complete; ready for start up
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: using the "epoll" event method
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: nginx/1.27.0
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: OS: Linux 6.1.30-0-virt
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: start worker processes
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: start worker process 30
learn-nginx-some-nginx-1 | 2024/06/14 15:30:56 [notice] 1#1: start worker process 31
아래 순서로 동작한다
- Nginx 이미지 다운로드
- 도커 네트워크 생성
- 컨테이너 생성
- 컨테이너 실행
컨테이너와 네트워크명을 보면 some-nginx가 아니라 learn-nginx-some-nginx-1로 출력이 되는데, 기본적으로 폴더명-서비스명-컨테이너번호로 이름을 짓기 때문이다
이를 바꾸고 싶다면 container_name을 설정한다
vi compose.yml
services:
some-nginx:
container_name: some-nginx
image: nginx:1.27.0
ports:
- "80:80"
docker compose down
docker rm some-nginx
docker compose up
[+] Running 2/0
✔ Network learn-nginx_default Created 0.0s
✔ Container some-nginx Created 0.0s
Attaching to some-nginx
some-nginx | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
some-nginx | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
some-nginx | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
some-nginx | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
some-nginx | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
some-nginx | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
some-nginx | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
some-nginx | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
some-nginx | /docker-entrypoint.sh: Configuration complete; ready for start up
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: using the "epoll" event method
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: nginx/1.27.0
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: OS: Linux 6.1.30-0-virt
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: start worker processes
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: start worker process 29
some-nginx | 2024/06/14 15:45:30 [notice] 1#1: start worker process 30
이제 호스트 OS에서 웹 브라우저로 http://localhost에 접속하면 아래와 같이 Nginx의 기본 index.html이 보여진다

기본적으로 Nginx 컨테이너의 html 폴더 내 파일은 아래와 같이 2개이다
# ls -l
total 8
-rw-r--r-- 1 root root 497 May 28 13:22 50x.html
-rw-r--r-- 1 root root 615 May 28 13:22 index.html
이 기본 index.html의 내용을 Docker volume을 사용해 바꿔보자
https://docs.docker.com/compose/compose-file/07-volumes/
mkdir html
vi html/index.html
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>안녕 Compose야</title>
</head>
<body>
<p>반갑다 Compose야</p>
</body>
</html>
vi compose.yml
services:
some-nginx:
container_name: some-nginx
image: nginx:1.27.0
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
- volumes
- YAML에 List 형태로 입력해야 함
호스트 OS의 HTML 폴더를 컨테이너의 /usr/share/nginx/html 폴더와 연결 후 폴더채로 덮어쓴다
폴더채로 덮어쓰기 때문에 index.html 파일은 변경되고 50x.html 파일은 사라진다는 것에 주의해야 한다
docker compose down
볼륨을 사용하면 down을 해도 데이터는 보존된다
docker compose up
http://localhost
변경된 HTML을 확인할 수 있다
Network
https://docs.docker.com/compose/networking/
기본적으로 컴포즈는 하나의 기본 네트워크를 준비한다
각 서비스 컨테이너들은 기본 네트워크에 연결되어 있고 서로 접근할 수 있다. 그리고 서비스명으로도 접근할 수 있다. 항상 서비스명으로 접근하는 것을 권장한다. 그래야 추후 컨테이너의 IP가 변경되더라도 변경사항이 없다
네트워크명은 프로젝트명에 기반하는데, YAML 파일이 있는 폴더명이 프로젝트명이 된다
네트워크명은 최종적으로 프로젝트명_default로 생성된다
Restart policy
restart: always
컨테이너가 꺼지면 무조건 재시작
Docker 데몬이 재시작되어도 해당 컨테이너를 자동으로 실행
unless-stopped
always와 비슷하지만, 사용자가 docker stop으로 직접 멈춘 경우에는 다시 안 켜짐
개발 환경에서는 코드 바꾸고 껐다 켰다 할 일이 많으니 보통 restart: unless-stopped나 아예 restart를 안 쓰는 경우가 많고, 운영 환경에서는 서비스가 멈추면 안 되니까 restart: always를 많이 사용