포스트

Docker 기초 (5) - Docker Compose: Commands and YAML

4편의 MySQL run 명령을 compose.yaml 파일로 옮기고, up, ps, logs, exec, down 명령으로 다루는 Docker Compose의 기본을 실습으로 정리합니다.

Docker 기초 (5) - Docker Compose: Commands and YAML

Docker 기초 시리즈의 5편입니다. 전체 목차는 0편에 있습니다.

What is Docker Compose

Docker Compose는 container 실행에 필요한 설정을 파일에 선언해 두고, 명령 한 번으로 실행하는 도구입니다. 4편에서 MySQL을 띄운 명령을 다시 보겠습니다.

1
2
3
4
5
docker run -d \
  --name db \
  -e MYSQL_ROOT_PASSWORD=dev \
  -v mysql-data:/var/lib/mysql \
  mysql:8.4

옵션이 붙으면서 다섯 줄이 됐고, 매번 정확히 다시 쳐야 하고, 팀원에게 공유하려면 따로 적어서 전달해야 합니다. Compose를 사용하면 이 옵션들을 compose.yaml이라는 파일로 관리하고, 실행은 docker compose up 한 번으로 끝납니다. 이번 편에서는 위 MySQL 명령을 Compose로 옮기면서 기본 명령어들을 알아보겠습니다.

참고로 명령 이름은 docker compose(공백)입니다. 예전에는 별도 프로그램인 docker-compose(하이픈)를 따로 설치했는데, 지금은 Docker에 포함되어 있습니다.


What is YAML

Compose가 읽는 파일은 YAML 형식으로 씁니다. YAML은 설정 파일을 적는 문법이고, 규칙 세 개만 알면 읽을 수 있습니다.

1
2
3
4
5
6
Name: Value        # 키: 값. 콜론 뒤에 공백 하나
Parent:
  Child: Value     # 들여쓰기(공백 2칸)가 소속을 표현
List:
  - First          # 하이픈이 목록의 항목
  - Second

괄호나 따옴표 없이 들여쓰기로 구조를 표현합니다. 들여쓰기 자체가 문법이므로 칸 수가 어긋나면 소속이 달라지고 에러가 납니다.


Setting

실습 환경을 아래와 같이 만들어보겠습니다. 파일 이름은 기본적으로 compose.yaml을 사용합니다. 다른 이름으로 저장했다면 docker compose -f server.yaml up -d처럼 -f로 파일을 지정하면 됩니다.

1
2
compose-lab/
└── compose.yaml

기존에 쓰던 run 명령을 compose.yaml로 옮기겠습니다.

1
2
3
4
5
docker run -d \
  --name db \
  -e MYSQL_ROOT_PASSWORD=dev \
  -v mysql-data:/var/lib/mysql \
  mysql:8.4
1
2
3
4
5
6
7
8
9
10
services:
  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: dev
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

run 옵션과 일대일로 대응됩니다.

run 명령에서compose.yaml에서
--name dbservices 아래의 service 이름
mysql:8.4 (image 인자)image
-e MYSQL_ROOT_PASSWORD=devenvironment
-v mysql-data:/var/lib/mysqlvolumes
-d파일이 아니라 up 명령의 옵션

맨 아래의 volumes: 블록은 named volume의 선언입니다. run 때는 -v에 적으면 자동으로 만들어졌지만, Compose에서는 사용하는 volume을 파일 끝에 한 번 더 선언하는 규칙입니다.


docker compose up

compose-lab 폴더 안에서 실행합니다.

1
2
docker   compose   up   -d
  ①        ②      ③    ④
  • docker: Docker CLI를 실행합니다
  • compose: compose.yaml을 읽어 실행하는 하위 명령 묶음입니다
  • up: compose.yaml에 선언된 것을 기준으로 실행합니다. volume이 없으면 만들고, image가 없으면 pull 합니다
  • -d: run의 -d와 같은 백그라운드 실행입니다
1
2
3
4
$ docker compose up -d
[+] Running 2/2
 ✔ Volume "compose-lab_mysql-data"  Created
 ✔ Container compose-lab-db-1       Started

run으로 하나씩 하던 일(volume 생성, container 생성과 시작)이 한 번에 끝났습니다. container 이름은 Compose가 폴더이름-서비스이름-번호 형식으로 자동으로 짓습니다. 이번 실습에는 db 하나뿐이지만, web service를 추가했다면 이름이 아래처럼 붙습니다.

service 이름Compose가 지은 container 이름
dbcompose-lab-db-1
webcompose-lab-web-1

이 긴 이름을 쓸 일은 별로 없습니다. compose 하위 명령들은 전부 service 이름으로 대상을 지정하기 때문입니다.

1
2
3
4
5
docker compose logs db
docker compose stop db
docker compose restart db
docker compose exec db bash
docker compose up -d web


docker compose ps, logs, exec

4편에서 배운 명령들이 compose 아래에 같은 이름으로 있습니다. 차이는 container 이름 대신 service 이름을 쓴다는 것입니다.

1
2
3
docker compose ps                                # 이 파일이 띄운 container만 목록
docker compose logs db 2>&1 | grep "ready"       # 준비 확인. 4편과 같은 요령
docker compose exec db mysql -uroot -pdev -e "SELECT 1;"

docker ps가 전체 목록인 것과 달리 docker compose ps는 이 compose.yaml이 띄운 것만 보여줍니다. 다음 실험을 위해 데이터도 하나 넣어 두겠습니다.

1
docker compose exec db mysql -uroot -pdev -e "CREATE DATABASE demo; CREATE TABLE demo.memo (body VARCHAR(100)); INSERT INTO demo.memo VALUES ('from compose');"


docker compose down

1
2
docker   compose   down
  ①        ②       ③
  • docker: Docker CLI를 실행합니다
  • compose: compose.yaml 기준으로 동작합니다
  • down: up이 만든 container와 network를 멈추고 삭제합니다. volume은 남깁니다

stop과 rm을 하나씩 치던 4편의 정리 절차가 명령 하나가 됐습니다. volume이 남는지는 실험으로 확인해보겠습니다.

1
2
3
docker compose down                # container 삭제
docker compose up -d               # 다시 생성
docker compose exec db mysql -uroot -pdev -e "SELECT * FROM demo.memo;"   # 준비 확인 후
1
2
body
from compose

container를 지웠다 새로 만들었는데 데이터가 그대로입니다. 4편의 -v 실험과 같은 원리이고, 그때의 명령 전부가 파일 하나와 up, down 두 마디로 줄었습니다.

실습을 마치면 volume까지 지웁니다. down에 -v를 붙이면 파일에 선언된 volume도 함께 삭제됩니다.

1
2
docker compose down -v    # 데이터까지 삭제되므로 실무에서는 확인하고 쓴다
rm -rf compose-lab        # 실습 폴더 삭제


부록: 함께 알아둘 docker 명령어

시리즈 실습에는 등장하지 않지만 알아두면 좋은 docker 본체 명령들입니다. 여기서는 목록만 잡아두고, 필요할 때 --help로 확인하면 됩니다.

docker pull

registry에서 image를 받아오는 명령입니다. 2편의 registry 개념이 명령으로는 아래와 같습니다.

1
docker pull python:3.12-slim                  # image 받기

pull을 직접 칠 일은 생각보다 적습니다. docker run과 docker compose up이 로컬에 없는 image를 알아서 pull 하기 때문입니다. 명시적으로 쓰는 경우는 두 가지입니다.

  1. 배포 전에 미리 받아둘 때
  2. latest처럼 움직이는 tag를 쓸 때

로컬에 latest가 있으면 run은 버전을 확인하지 않으므로, 갱신하려면 pull을 직접 실행해야 합니다.

docker tag

1
docker tag hello <Docker계정이름>/hello:1.0   # 올릴 주소 형식으로 이름표 추가

<Docker계정이름> 자리에는 본인의 Docker Hub 계정 ID를 넣습니다. image 이름의 전체 형식이 registry주소/계정이름/저장소이름:태그이고, 앞의 두 조각을 생략하면 각각 docker.io와 library로 채워지기 때문입니다.

mysql:8.4에 계정 이름이 없는 것은 공식 image라 library 계정에 있어서이고, 내가 만든 image를 올리려면 내 계정 이름을 직접 붙여야 합니다. login한 계정과 이 이름이 다르면 push는 권한 오류로 거절됩니다.

docker login, push

1
2
docker login                                  # registry 인증
docker push <Docker계정이름>/hello:1.0        # registry에 올리기

앞의 tag는 로컬에서만 일어나는 일입니다. image를 복사하지 않고 같은 image에 이름표를 하나 더 붙일 뿐이라, 그 시점까지는 아무것도 registry로 나가지 않습니다. 실제로 올라가는 시점이 push입니다.

docker top, diff, history

1
2
3
docker top web        # container 안의 프로세스 목록. 메인 프로세스가 PID 1로 보인다
docker diff web       # image 대비 container가 바꾼 파일 목록. 쓰기 layer의 내용물
docker history hello  # image가 어떤 layer로 쌓였는지와 각 층의 크기

셋 다 바꾸는 것 없이 확인만 하는 명령이고, 각각 4편의 프로세스, 2편의 쓰기 layer, 6편의 layer 구조를 눈으로 확인하는 용도입니다.

docker stop, start, kill

1
2
3
docker stop web     # 종료 요청(SIGTERM)을 보내고 정상 종료를 기다린다
docker start web    # 종료된 container를 그대로 다시 시작
docker kill web     # 즉시 강제 종료(SIGKILL). stop이 안 먹을 때의 최후 수단

4편 정리에서 쓴 stop이 이것입니다. rm과 달리 stop과 start는 container를 지우지 않으므로, start 하면 데이터와 설정이 그대로인 채 다시 뜹니다.

그 외

  • docker save / docker load: image를 tar 파일로 뽑고 다시 넣습니다. registry 없이 폐쇄망으로 옮길 때 씁니다
  • docker commit: 실행 중인 container를 image로 굳힙니다. 가능은 하지만 Dockerfile 없이 만든 image는 만드는 과정이 기록되지 않아 재현이 안 되므로, 실무 관행은 쓰지 않는 것입니다

다음 편에서는 남이 만든 image를 쓰는 단계를 넘어, Dockerfile로 내 image를 만듭니다.

다음 글: Docker 기초 (6) - Dockerfile: Layers, Cache, and Multi-stage Builds

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.