도커 컴포즈
도커 컴포즈는 단일 서버에서 여러 컨테이너를 프로젝트 단위로 묶어서 관리하기 위해 사용된다. 단일 서버에서 여려 컨테이터를 띄우기 위해서는 커맨드 라인을 통해서 하나씩 도커 컨테이너를 실행했다. 하지만 도커 컴포즈에서는 docker-compose.yml
이라는 YAML 파일을 통해 컨테이너들을 명시적으로 관리하는 것이 가능하다. 또한, 프로젝트 단위로 묶어서 관리하기 때문에 프로젝트 별로 조금더 격리된 환경을 사용하게 만들수 있다.
- 프로젝트 단위로 도커 네트워크와 볼륨 관리 가능
- 도커 컴포즈 내에 볼륨도 있고 네트워크도 있는 구조
- 프로젝트 내 서비스 간 의존성 정의 가능
- 프로젝트 내에서 서비스2을 띄우기 위해서 서비스1을 먼저 띄워야 한다와 같은 의존성을 관리
- 프로젝트 내 서비스 디스커버리 자동화
- 의존성이 있는 서비스를 서로 호출할 때 내부 도메인 명으로 호출 가능
- 도커 엔진에서 컨테이너를 관리할 때는 브릿지 네트워크 모드에서 netalias를 통해 특정 이름으로 서비스를 호출햇지만, 도커 컴포즈 내에서는 각각의 서비스 명으로 해당 서비스를 네트워크 상에서 호출 가능
- 이렇게 각각의 서비스 명으로 호출하는 것을 서비스 디스커버리라고 함
- 손 쉬운 컨테이너 수평 확장
- 서비스 하나가 여러 컨테이너를 다루게 되는데 동일한 역할을 수행하는 여러 컨테이너를 수평 확장가능
프로젝트 / 서비스 / 컨테이너
방금 말했던거 처럼 도커 컴포즈는 컨테이너를 프로젝트 단위로 관리하게 해준다. 이 프로젝트는 하나의 워크스페이스라고 생각하면 된다.
- 프로젝트 (Project)
- 도커 컴포즈에서 다루는 워크스페이스 단위
- 함께 관리하는 서비스 컨테이너의 묶음
- docker-composer.yaml은 하나의 프로젝트를 명세함
- 프로젝트 단위로 기본 도커 네트워크가 생성 됨
- 서비스 (Service)
- 도커 컴포즈에서 컨테이너를 관리하기 위한 단위
- scale을 통해 서비스 컨테이너의 수 확장 가능
- 컨테이너 (Container)
- 도커에서 프로세스가 실행되는 기본단위
- 서비스를 통해 컨테이너 관리
docker-compose.yml 작성법
이제 docker-compose.yml 파일의 구조에 대해 알아보자. docker-compose.yml은 version, services, networks, volumes 총 4개의 최상위 옵션을 가진다.
version: '3'
services:
subkey1:
...
subkey2:
...
networks:
...
volumes:
...
- version
- 2021년 11월 기준 버전 3.9가 최신
- 가능한 최신 버전 사용 권장
- 도커 엔진 및 도커 컴포즈 버전에 따라 yaml의 구조가 다르기 때문에 호환성 매트릭스 참조할 것 (호환성 매트릭스는 https://docs.docker.com/compose/compose-file/compose-file-v3/ 참조)
- 버전 3부터 Docker Swarm과 호환 -> Swarm 서비스를 docker-compose.yml로 정의 가능
- services
- 프로젝트 내에 구성되는 여러 서비스들을 subkey들을 활용해 관리함
- networks
- 프로젝트마다 독립적으로 구성됨
- 프로젝트에서 사용할 네트워크 목록 명시
- 명시하지 않아도 브릿지 네트워크가 <프로젝트명>_default라는 이름으로 생성됨 (프로젝트명도 명시하지 않았다면 폴더이름을 프로젝트명으로 사용함)프로젝트명>
- volumes
- 프로젝트마다 독립적으로 구성됨
- 프로젝트에서 사용할 볼륨 목록 명시
version: "3.9"
services:
web:
# container_name: 'web'
build: .
ports:
- "5000"
redis:
image: "redis:alpine"
예시로 위의 docker-compose.yml 파일을 해석해보면, 도커 컴포즈 3.9 파일 포맷을 사용하고 있고, 서비스로는 web 서비스와 redis 서비스 두 가지가 있는데, redis 서비스는 redis:alpine 이미지를 기반으로 만들어지고, web 서비스는 이미지를 명시하지 않고 build 옵션으로 현재 디렉토리에서 Dockerfile을 찾아서 빌드하고 5000번 포트를 사용하라는 의미이다.
여기서 주의할 점은 포트바인딩을 할때 5000:5000과 같이 호스트 포트를 지정하면 나중에 scaling을 할 때 같은 포트를 두개 이상의 컨테이너가 사용하려고 충돌을 일으킨다. 즉, 호스트 포트를 지정하면 스케일링 불가능해진다. container_name도 마찬가지 이유로 주의를 해야한다.
version: '3.9'
services:
db:
image: mysql:5.7
volumes:
- db:/var/lib/mysql
restart: always
environment:
- MYSQL_ROOT_PASSWORD=wordpress
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=wordpress
networks:
- wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
networks:
- wordpress
volumes:
db: {}
networks:
wordpress: {}
조금 더 복잡한 yml 파일을 해석해보자. 서비스로 db 서비스와 wordpress 서비스 두가지가 있고, db 라는 이름의 볼륨을 만들었고 db 서비스의 db:/var/lib/mysql에 마운트 되었다. wordpress라는 이름의 네트워크를 만들어 각 서비스에 workdpress 네트워크를 연결시켰다. 볼륨과 네트워크는 지정하지 않으면 자동으로 만들어주기는 한다. 다른 드라이버를 사용하거나 드라이버 옵션을 지정하고 싶으면 {} 안에 옵션을 지정해주면 된다.
services에 있는 것들은 대부분 docker run에서 제공하는 옵션들이다. image 옵션으로 실행시킬 컨테이너의 이미지를 지정한다. volumes 옵션에서는 db 볼륨을 컨테이너의 /var/lib/mysql 경로에 마운트 한다. restart 키는 컨테이너의 재시작 전략을 관리하며, always 옵션을 통해 컨테이너가 꺼지더라고 항상 재시작한다. environment 옵션으로 환경변수를 전달한는데, key=value 형태 또는 key: column:value의 오브젝트 형태로 지정할 수 있다. networks 옵션은 우리가 만든 네트워크에 해당 서비스를 연결한다. 여러개의 네트워크에도 연결이 가능하다. depends_on으로 컨테이너가 실행되는 순서를 정할 수 있는데, 위에서 wordpress 서비스는 db 서비스가 띄워지고 난 다음에 실행되어야 한다라고 정의했다. 실제로 docker-compose up을 하면 db 서비스부터 실행된다. 주의할 것은 해당 컨테이너가 실행되었음을 보장하지만 준비되었음을 보장하지는 않는다. ports 옵션은 docker run 의 -p 옵션과 동일하다.
- 도커 스왐 (Docker Swarm)
- 여러 서버를 기반으로 스왐 클러스터를 형성하여 컨테이너를 관리하는 컨테이너 오케스트레이션 시스템
- 쿠버네티스와 동일 목적으로 만들어졌지만 인기를 끌지 못함
docker-compose 명령어
아래 명령어들은 docker-compose v2 이상에서만 사용이 가능하다.
# 실행중인 프로젝트 목록 확인
$ docker-compose ls
# 전체 프로젝트 목록 확인
$ docker-compose ls -a
# Foreground로 도커 컴포즈 프로젝트 실행 (docker run 과 유사)
# 이미지를 받아오고 빌드하고 컨테이너를 실행
$ docker-compose up
# Background로 도커 컴포즈 프로젝트 실행
$ docker-compose up -d
# 프로젝트 이름 my-project로 변경하여 도커 컴포즈 프로젝트 실행
$ docker-compose -p my-project up -d
# 프로젝트 내 컨테이너 및 네트워크 종료 및 제거
$ docker-compose down
# 프로젝트 내 컨테이너, 네트워크 및 볼륨 종료 및 제거
$ docker-compose down -v
# web 서비스를 3개로 확장
$ docker-compose up --scale web=3
# 프로젝트 내 서비스 로그 확인
$ docker-compose logs
$ docker-composer logs -f # 실시간
# 프로젝트 내 컨테이너 이벤트 확인
$ docke-compose events
# 프로젝트 내 이미지 목록
$ docker-compose images
$ docker-compose -p <프로젝트명> images
# 프로젝트 내 컨테이너 목록
$ docker-compose ps
$ docker-compose -p <프로젝트명> ps
# 프로젝트 내 실행중인 프로세스 목록
$ docker-compose top
$ docker-compose -p <프로젝트명> top
이제 아래 파일들을 가지고 간단한 docker-compose를 만들어보자.
# app.py
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install flask, redis
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# Dockerfile
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# docker-compose.yml
version: "3.9"
services:
web:
build: .
ports:
- "5000"
redis:
image: "redis:alpine"
web 서비스 만드는 과정
redis 서비스 만드는 과정. Creating build_web_1 에서 build는 프로젝트명, web은 서비스명, 1은 컨테이너 인덱스이다.
도커 컴포즈를 이제 백그라운드 모드로 실행하고 목록을 보자. 참고로 -p 옵션으로 프로젝트명을 바꿧다.
목록을 보자.
이런식으로 다른 명령어도 사용하면 된다.
주요 사용 목적
도커 컴포즈는 주로 아래와 같은 3가지 이유 때문에 사용된다.
- 로컬 개발 환경 구성
- 특정 프로젝트의 로컬 개발 환경 구성 목적으로 사용
- 각 사람마다 프로젝트의 의존성(Redis, MySQL, Kafka 등)을 쉽게 띄울 수 있음
- 자동화된 테스트 환경 구성
- CI/CD 파이프라인 중 쉽게 격리된 테스트 환경을 구성하여 테스트 수행 가능
- 단일 호스트 내 컨테이너를 선언적 관리
- 단일 서버에서 컨테이너를 관리할 때 YAML 파일을 통해 선언적으로 관리 가능
- 파일을 통해서 변경사항 추적 가능