Docker Compose는 여러 개의 컨테이너를 하나의 파일에 정의하고 복잡한 애플리케이션의 구조를 파일로 관리할 수 있게 해줍니다.
각각의 컨테이너는 이 하나의 파일 안에 모두 정의되어 있고 서비스 간의 의존성이나 Docker의 네트워크도 이 파일 안에서 설정할 수 있습니다.
즉, 여러 개의 컨테이너들을 동시에 관리하는 도구이며, docker compose up 과 같은 한번의 명령어로 여러 개의 컨테이너와 네트워크, 볼륨 등를 한번에 실행하거나 종료할 수 있습니다.
이러한 Docker Compose는 Docker Desktop을 설치할 때 기본으로 포함되어 설치되기 때문에 별도로 설치할 필요는 없습니다.
이렇게 Docker Compose를 사용하면 여러 컨테이너를 관리할 수 있다는 것 외에도 장점이 있는데
바로 로컬 개발 환경에서 아주 편리하게 활용할 수 있다는 것입니다.
로컬 개발 환경은 소스코드를 개발하는 실제 PC 환경을 의미합니다.
개발자는 자신의 PC에서 자신이 개발하는 프로그램 뿐만 아니라 DB 서버나 애플리케이션과 연관이 있는 다른 애플리케이션 서버도 함께 실행해야 하는 경우가 종종 있는데 docker compose를 활용하지 않는 경우에는 여러 컨테이너를 각각 실행시켜야 했습니다.
그러나 docker compose를 활용하면 이런 환경에서 실제 운영 환경과 비슷한 방식으로 개발PC에서 빠르게 환경을 구성할 수 있습니다.
이 과정에서 Docker Compose는 docker-compose.yml 이라는 정의서 파일을 사용하며,
.yml 확장자는 YAML 문법을 사용해서 작성된 파일이라는 것을 의미합니다.
💡 YAML
yml은 데이터를 표현하기 위한 포맷 중 하나로, 이 외에 가장 유명한 포맷은 JSON이 있습니다.JSON은 HTTP 프로토콜에서 데이터를 주고받는 가장 대중적인 포맷 중 하나이며,
YAML 포맷은 상대적으로 최근에 등장한 포맷으로, JSON 보다는 가독성에 더 초점을 맞춘 방식입니다.
파이썬 문법과 같이 띄어쓰기와 대시를 활용해서 데이터의 깊이, 리스트와 객체를 표현하는 것이 특징입니다.
name: John
age: 30
isAlive: true
hobbies:
- reading
- fishing
address:
- street: 123
- city: Seoul그러면 이제 Docker Compose 문법을 알아보겠습니다.
그 전에 Docker Compose에서 흔히 말하는 Service는 컨테이너와 동일한 개념이니 헷갈려하지 않으시길 바랍니다.
# YAML 파일에 정의된 서비스 생성 및 시작
docker compose up -d
# 현재 실행 중인 서비스 상태 표시
docker compose ps
# 서비스를 실행하지 않고 서비스의 이미지만 빌드
docker compose build
# 실행 중인 서비스의 로그 표시
docker compose logs
# YAML 파일에 정의된 서비스 종료 및 제거
docker compose down먼저, YAML 파일에 정의되어 있는 서비스를 생성하고 시작하려면 docker compose up 명령어를 실행하면 됩니다. -d 옵션은 docker run 과 마찬가지로 실행하는 컨테이너들을 백그라운드에서 실행하는 옵션입니다.
그리고 ps 명령을 사용하면 현재 실행 중인 서비스들의 상태를 표시할 수 있습니다.
docker compose build 명령어를 사용하면 서비스를 실행하지 않고 서비스의 이미지만 빌드할 수 있습니다.
# app > Dockerfile
FROM python:3.7-alpine
WORKDIR /app
COPY . /app
RUN pip install flask redis
CMD ["python", "app.py"]# docker-compose.yml
version : '3'
services:
exampleservice:
build: ./app
image: exampleservice:1.0.0
ports:
- "5000:5000"
redis:
image: "redis:alpine"docker-compose.yml 파일에서 첫번째 version 부분은 Docker Compose의 버전을 의미합니다.
어떤 버전을 사용하느냐에 따라 기능들이 더 추가되거나 없을 수도 있습니다.
그 다음 서비스 부분에는 실제로 실행할 컨테이너들의 리스트를 출력합니다.
위 예시 같은 경우에는exampleservice와 redis 두개의 서비스를 확인할 수 있으며,
이 중 exampleservice 는 사용자가 개발한 애플리케이션이며, 소스코드의 내용이 변경될 때마다 애플리케이션을 다시 빌드해야 하기 때문에 build 속성에 소스코드와 Dockerfile이 있는 app 이라는 폴더를 지정해서 이미지를 빌드하도록 설정했습니다.
그래서 PC에 exampleservice:1.0.0 이미지가 있으면 그대로 사용하되, 이 이미지가 없는 경우에는 Dockerfile 을 사용해서 이미지를 빌드한 후 컨테이너로 실행합니다.
⭐ 그런데 만약 소스 코드의 파일이 변경되었어도 이미지가 이미 있는 경우에는 해당 이미지를 우선적으로 사용하기 때문에 이미지를 다시 빌드하려면 이미지의 태그를 수정하거나 Docker Compose에 별도의 옵션을 주어야 합니다.
아래와 같은 옵션을 사용하면 로컬에 동일한 이름의 이미지가 있어도 해당 이미지를 제거하고 새로운 이미지로 빌드합니다.
# 로컬에 이미지가 있어도 다시 이미지를 빌드
docker compose up -d --build또한, redis는 이미지만 지정되어 있는데 이는 docker run 명령을 실행할 때 이미지만 지정을 한 것과 같은 의미이고
exampleservice와 같이 포트 옵션이 있는 경우에는 docker run 명령을 실행할 때 -p 옵션을 준 것과 동일하게 실행한 것을 의미합니다.
또한 build 옵션을 설정하면 이 exampleservice 이미지를 사용하기 전에 build에 있는 경로에 접근해서 exampleservice의 1.0.0으로 이미지를 먼저 빌드 합니다. (자동으로 빌드까지 수행)
그리고 이 build에 들어가는 명령어는 Dockerfile이 있는 경로를 지정해주어야 합니다.
이제 이 애플리케이션을 실행한다고 가정해보겠습니다.
먼저, docker compose build 명령을 실행하면 서비스들 중 build 옵션이 있는 서비스를 찾아서 이미지를 먼저 빌드합니다.
그래서 결과적으로는 exampleservice 1.0.0 이라는 이미지가 빌드되는 것을 확인할 수 있습니다.
그리고 docker compose up -d 와 같이 데몬으로 실행하면 먼저, redis 이미지를 다운받는 것을 확인할 수 있고 그 다음, 네트워크를 생성하고 redis 컨테이너와 exampleservice 컨테이너가 실행된 것을 볼 수 있습니다.
다른 예시로 이중화 DB에 대한 docker-compose.yml 파일을 살펴보겠습니다.
version : '3'
services:
postgres-primary-0:
image: bitnami/postgresql-repmgr:15
volumes:
- postgres_primary_data:/bitnami/postgresql
environment:
POSTGRESQL_POSTGRES_PASSWORD: adminpassword
POSTGRESQL_USERNAME: myuser
POSTGRESQL_PASSWORD: mypassword
POSTGRESQL_DATABASE: mydb
REPMGR_PASSWORD: repmgrpassword
REPMGR_PRIMARY_HOST: postgres-primary-0
REPMGR_PRIMARY_PORT: 5432
REPMGR_PARTNER_NODES: postgres-primary-0,postgres-standby-1:5432
REPMGR_NODE_NAME: postgres-primary-0
REPMGR_NODE_NETWORK_NAME: postgres-primary-0
REPMGR_PORT_NUMBER: 5432
postgres-standby-1:
image: bitnami/postgresql-repmgr:15
volumes:
- postgres_standby_data:/bitnami/postgresql
environment:
POSTGRESQL_POSTGRES_PASSWORD: adminpassword
POSTGRESQL_USERNAME: myuser
POSTGRESQL_PASSWORD: mypassword
POSTGRESQL_DATABASE: mydb
REPMGR_PASSWORD: repmgrpassword
REPMGR_PRIMARY_HOST: postgres-primary-0
REPMGR_PRIMARY_PORT: 5432
REPMGR_PARTNER_NODES: postgres-primary-0,postgres-standby-1:5432
REPMGR_NODE_NAME: postgres-standby-1
REPMGR_NODE_NETWORK_NAME: postgres-standby-1
REPMGR_PORT_NUMBER: 5432
volumes:
postgres_primary_data:
postgres_standby_data:위와 같은 YAML 파일을 보면 2개의 서비스를 실행하고 있으며, 이미지는 동일하게 bitnami/postgresql 을 사용하고 있습니다.
그리고 데이터베이스는 볼륨을 사용하기 때문에 Docker Compose에서 볼륨을 만들고 컨테이너에 연결하기 위해서는 version과 같은 위치에 volumes 라는 속성을 추가해주면 됩니다.
컨테이너와 마찬가지로 volumes 아래 작성한 내용들이 하나의 볼륨이 되며, 추가로 설정을 하지 않으면
위와 같이 postgres_primary_data, postgres_standby_data로 이름만 받아서 가장 기본 설정으로 볼륨을 생성할 수 있습니다.
컨테이너에서 볼륨을 마운트하면서 postgresql_primary_data를 primary 컨테이너의 bitnami/postgresql로 마운트하고 stanby 도 동일하게 데이터를 standby 컨테이너에 마운트 합니다.
그리고 위 yml 파일 중 가장 눈에 띄는건 아무래도 이중화 DB를 생성하면서 사용되는 많은 환경변수들 일겁니다.
Docker Compose도 위와 같이 enviroment 필드에 키와 값을 주어서 환경변수를 지정할 수 있습니다.
그런데 잘 보면 중복되는 환경변수들이 여러 개 보이는데
이렇게 공통적인 속성이 있는 환경변수들은 공통 환경변수로 뽑아낼 수 있습니다.
이러한 공통 환경변수 기능은 Compose 3 버전부터 지원합니다.
version : '3'
x-enviroment: &common_enviroment
POSTGRESQL_POSTGRES_PASSWORD: adminpassword
POSTGRESQL_USERNAME: myuser
POSTGRESQL_PASSWORD: mypassword
POSTGRESQL_DATABASE: mydb
REPMGR_PASSWORD: repmgrpassword
REPMGR_PRIMARY_HOST: postgres-primary-0
REPMGR_PRIMARY_PORT: 5432
REPMGR_PORT_NUMBER: 5432
services:
postgres-primary-0:
image: bitnami/postgresql-repmgr:15
volumes:
- postgres_primary_data:/bitnami/postgresql
environment:
<<: *common_enviroment
REPMGR_PARTNER_NODES: postgres-primary-0,postgres-standby-1:5432
REPMGR_NODE_NAME: postgres-primary-0
REPMGR_NODE_NETWORK_NAME: postgres-primary-0
postgres-standby-1:
image: bitnami/postgresql-repmgr:15
volumes:
- postgres_standby_data:/bitnami/postgresql
environment:
<<: *common_enviroment
REPMGR_PARTNER_NODES: postgres-primary-0,postgres-standby-1:5432
REPMGR_NODE_NAME: postgres-standby-1
REPMGR_NODE_NETWORK_NAME: postgres-standby-1
volumes:
postgres_primary_data:
postgres_standby_data:위와 같이 x-enviroment 로 해서 & 표시에 common-enviroment라는 변수그룹명을 지정해준 뒤
이 아래에 공통 변수로 사용되는 부분을 모두 정의합니다.
그리고 이 공통변수를 사용하는 컨테이너는 enviroment 아래에 위에서 보는 것처럼 << 표시와 함께 콜론과 아스타리크 를 입력한 다음 위에서 지정한 공통 변수명을 지정하면 됩니다.
⭐ 참고!
services 내 depends_on 옵션은 특정 컨테이너가 실행될 때까지 컨테이터 실행을 보류합니다.
# docker-compose.yml
version: '3'
services:
postgres-2:
build: ./backend
image: backend:5.0.0-compose
depends_on:
- postgres-1
# postgres-1 컨테이너가 실행되기 전까지는 postgres-2 컨테이너는 실행되지 않습니다.⭐ 리소스 사용량 제한, 재시작 정책 지정
# docker-compose.yml
version: '3'
services:
postgres-2:
build: ./backend
image: backend:5.0.0-compose
deploy:
resources:
limits:
cpus: '1'
memory: 256M
restart: always
# restart 속성은 컨테이너가 종료되었을 때, 자동으로 실행해주는 기능을 제공
# deploy:
# resources:
# limits:
# cpus: '최대CPU사용량'
# memory: 최대RAM사용량
# restart:
# on-failure: 실패 시에만 재실행
# always: 무조건 재실행이 링크를 통해 구매하시면 제가 수익을 받을 수 있어요. 🤗