Skip to content

2. Docker Compose 다루기


1. Docker Compose 서비스 정의

  • 다음은 Dockerfile을 Docker Compose로 변환하여 간략하게 다중 컨테이너 애플리케이션을 정의해 본다.


1) Docker Compose YAML services 파라미터

version: "1.0" # Docker Compose 버전 정보

services: # 서비스 목록 정의
  mongodb: # 서비스 이름 정의
    image: mongo # 해당 서비스에서 사용할 이미지
    restart: always # 컨테이너 실행 시 항상 재시작
    command: sh -c "echo -_-" # 컨테이너 실행 시 커맨드
    working_dir: / # 컨테이너 실행 시 작업 디렉터리
    environment: # 서비스 환경변수 정의
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: secret
    volumes: # 컨테이너에서 호스트로 마운트
      - type: bind
        source: ./data/db # 호스트 경로
        target: /data/db # 컨테이너 내부 경로
    container_name: "mongodb" # 컨테이너 이름 정의
    ports: # 서비스의 포트 정의
      - "27017:27017" # 호스트포트:컨테이너포트


2) App 서비스 정의

$ sudo docker run -dp 3000:3000 \
  -w /app -v ${PWD}:/app \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  node:12-alpine \
  sh -c "yarn install && yarn run dev"


  • 위의 docker run 명령어를 다음과 같이 Docker Compose로 작성이 가능하다.


version: "3.8"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos


  • Docker Compose의 장점 중 하나로는 상대 경로 입력이 가능하며, YAML(.yml)로 구성하여 설정 파일의 가독성이 좋아진다.


3) MySQL 서비스 정의

$ docker run -d \
  --network todo-app --network-alias mysql \
  -v todo-mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=todos \
  mysql:5.7


  • 다음과 같이 Docker Compose로 작성이 가능하다.


version: "3.8"

services:
  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:


4) App과 MySQL 서비스 결합

version: "3.8"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:


  • 위와 같이 YAML 파일(.yml) 하나에 app, mysql 두 개의 설정을 한꺼번에 정의하여 실행 환경을 빠르게 구성할 수 있다.
  • 이렇게 작성된 설정 파일로 한꺼번에 관리가 되어 서비스를 올리고 내리는 것을 쉽게 할 수 있다.



2. 주요 명령어

  • docker-compose에서 사용하는 명령어는 기존 Docker에 있는 명령어와 비슷하다.
  • 최신 버전의 Dockerd에는 Compose가 내장되어 있어 docker-compose 대신 docker compose 명령어로도 실행이 가능하다.


1) up

  • docker-compose.yml 파일의 내용에 따라 이미지를 빌드하고 서비스를 실행한다.


sudo docker-compose -p test up -d


  • up 명령어로 Compose 실행 시 단계별 진행 사항은 다음과 같다.


1] 서비스를 띄울 네트워크 설정

2] 필요한 볼륨 생성 혹은 이미 존재하는 볼륨과 연결

3] 필요한 이미지 Pull

4] 필요한 이미지 Build

5] 서비스 의존성에 따라 서비스 실행


  • up 명령어의 주된 옵션은 다음과 같다.


1] -d

  • 서비스를 백그라운드로 실행

2] --force-recreate

  • 컨테이너를 지우고 새로 생성

3] --build

  • 서비스 시작 전 이미지를 새로 생성

4] -f

  • 기본으로 제공하는 docker-compose.yml이 아닌 별도의 파일명을 실행할 때 사용
  • sudo docker-compose -f docker-compose.yml -f docker-compose-test.yml up 형태와 같이 사용 가능
  • 위와 같이 YAML을 두 개 이상 설정할 경우 앞에 있는 파일보다 뒤에 있는 파일이 우선


2) down

  • 실행 중인 서비스를 삭제한다.
  • 이때 컨테이너와 네트워크를 삭제하면, 옵션에 따라 볼륨도 같이 삭제할 수 있다.


sudo docker-compose down


  • down 명령어의 주된 옵션은 다음과 같다.


1] -v, --volume

  • 볼륨까지 같이 삭제
  • DB 데이터를 초기화하는 데 용이
  • 모든 설정을 초기화하고 새로 시작하는 데 사용


3) stop, start

  • 서비스를 멈추거나, 멈춰 있는 서비스를 시작한다.


sudo docker-compose stop
sudo docker-compose start


4) ps

  • 현재 환경에서 실행 중인 각 서비스의 상태를 표시한다.


sudo docker-compose ps


5) exec

  • 실행 중인 컨테이너에서 명령어를 실행한다.
  • 명령어는 다음과 같은 패턴으로 실행된다.


sudo docker-compose exec 정의한서비스이름 실행될명령어
sudo docker-compose exec app ./upload_logs.sh
sudo docker-compose exec mysql mysql -uroot -psecret todos


6) run

  • docker run과 마찬가지로 특정 명령어를 일회성으로 실행한다.
  • runexec와 비슷한 역할을 실행하지만 다른 점은 컨테이너 재실행의 여부이다.
  • run은 실행 시 새로운 컨테이너를 띄우는 반면, exec는 실행되어 있는 컨테이너에 접속한다.
  • exec는 프로세서를 실행시켜 놓을 때 사용되고, run은 Batch성 작업에 특화되어 있다.


  • run 명령어의 주된 옵션은 다음과 같다.


1] --rm

  • 해당 명령어가 종료된 뒤 컨테이너도 종료


7) logs

  • 출력으로 나온 로그를 확인할 때 사용한다.
  • Docker의 logs와 마찬가지로 --follow 또는 -f를 지정하여 실시간으로 나오는 로그를 확인할 수 있다.


sudo docker-compose logs -f


8) config

  • docker-compose에 최종적으로 적용된 설정을 보여준다.
  • sudo docker-compose -f docker-compose.yml -f docker-compose-test.yml up와 같이 여러 개의 YAML 파일을 실행시켰을 경우 최종적으로 적용된 설정을 확인하기 좋은 옵션이다.


sudo docker-compose config


3. Docker Compose Enviornment

  • Docker Compose의 장점으로는 파일 하나를 정의해 놓으면 여러 곳에서 동일한 동작을 보장하는 것이다.
  • 하지만 각 실행 환경에 따라 변경되어야 하는 옵션들이 있는데, 이것들 때문에 일일이 파일을 수정하는 건 비효율적이다.
  • 그래서 각 실행하는 환경마다 환경변수 파일을 정의하여 동일한 Compose 파일로 각각의 환경과 동적인 옵션으로 실행이 가능하다.


1) .env 파일

  • 기본적으로 sudo docker-compose up을 한 상태에서, .env 파일을 찾아 파일 내부에 있는 값을 환경변수로 사용한다.


$ cat .env # 환경변수 파일
TAG=v1.5

$ cat docker-compose.yml # Compose 파일
version: '3'
services:
  web:
    image: "webapp:${TAG}"$ # .env 파일의 TAG 변수 사용

$ sudo docker-compose config # 실제 적용된 옵션
version: '3'
services:
  web:
    image: 'webapp:v1.5'

$ export TAG=v2.0 # export를 사용하여 환경변수 지정 가능
$ sudo docker-compose config
version: '3'
services:
  web:
    image: 'webapp:v2.0'


  • 여러 파일에서 동일한 환경변수를 설정할 때 사용할 값을 선택하기 위해, Compose에서 사용하는 우선순위는 다음과 같다.


1] Compose 파일(docker-compose.yml)

2] Shell Environment Variables($ export ...)

3] Environment 파일(.env)

4] Dockerfile

5] Variable is not defined


  • 여러 복합적인 상황에 따라서 의도하지 않은 설정이 들어가지 않게 우선순위를 잘 파악하는 것이 중요하다.


2) 상황에 따른 .env 적용 예제

  • dev, prod, local 환경에 따라서 .env.dev, .env.prod, .env.local 형식으로 할당을 줄 수 있다.
  • 이때 --env-file 옵션으로 환경변수 파일 지정이 가능하다.


sudo docker-compose --env-file ./config/.env.dev up


3) CLI Environment

  • 다음의 명령어는 각각 그 아래의 Compose 파일이 일치하게 매핑된다.


$ sudo docker run -e VARIABLE1

web:
  environment:
    - VARIABLE1
$ sudo docker-compose run -e DEBUG=1 web python console.py

web:
  environment:
    - DEBUG=1


4. Compose Network 설정 옵션

version: "3.8"
services:
  app:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
    ports:
      - "8001:5432"


  • 위의 Compose 파일을 실행했을 때 네트워크가 생성되는 과정은 아래와 같다.
  • 이때 프로젝트 이름은 myapp이라고 가정한다.


1] myapp_default 네트워크 생성

2] app 컨테이너 생성

  • app이라는 이름으로 myapp_default에 접속

3] db 컨테이너 생성

  • db라는 이름으로 myapp_default에 접속


  • 네트워크가 구성된 이후 postgres://db:5432 주소로 접속이 가능하다.
  • 내부 컨테이너끼리는 postgres://db:5432로 접속하고, 외부에서는 postgres://{DOCKER_IP}:8001로 접속해야 한다.
  • 컨테이너 업데이트를 위해 다시 시작할 경우에는 컨테이너 이름은 같지만 다른 IP로 네트워크가 생성되어 IP 베이스는 IP를 다시 찾아서 연결해야 하고, 다른 컨테이너에서는 기존에 접속했던 컨테이너 이름으로 재연결을 시도해야 한다.


  • links는 다른 서비스의 컨테이너와 연결시켜 주는 역할을 하고 있다.
  • 이름만 지정하거나, {name}:{alias} 형식으로 지정할 수 있다.


version: "3"
services:
  web:
    build: .
    links:
      - "redis"
      - "db:database"
  db:
    image: postgres


  • links는 레거시 기능으로 공식 페이지에서는 권장하지 않는 방법이다.
  • depend_on처럼 의존성 관계가 맺어지며, 네트워크와 함께 사용될 때는 하나 이상의 네트워크가 관리되기 때문에 네트워크만 사용하길 권장한다.


2) Custom Network

  • Custom Network를 통해 좀 더 복잡한 네트워크 토폴로지를 만들 수 있다.


version: "3"
services:
  proxy:
    build: ./proxy
    networks:
      - frontend
  app:
    build: ./app
    networks:
      - frontend
      - backend
  db:
    image: postgres
    networks:
      - backend
networks: # top-level network
  frontend:
    driver: custom-driver-1 # Use a custom driver
  backend:
    driver: custom-driver-2 # Use a custom driver which takes special options
    driver_opts:
      foo: "1"
      bar: "2"


  • top-levelnetworks 옵션에서 커스텀하게 지정이 가능하다.
  • 위와 같이 설정하면 app에서만 proxy, db에 접근 가능하고, dbproxy는 서로 접근이 불가능하게 설정이 된다.


3) IPv4, IPv6

  • IPv6를 사용하기 위해서는 top-levelnetworks 옵션에 enable_ipv6 옵션이 반드시 들어가야 한다.


version: "2.4"

services:
  app:
    image: nginx:alpine
    networks:
      app_net:
        ipv4_address: 172.16.238.10
        ipv6_address: 2001:3984:3989::10

networks:
  app_net:
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: "172.16.238.0/24"
        - subnet: "2001:3984:3989::/64"


4) Default Network

  • default를 이용하여 자체 네트워크를 지정하면서 앱 전체의 기본적인 네트워크 구성이 가능하다.


version: "3"

services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres

networks:
  default:
    driver: custom-driver-1 # Use a custom driver


5) User Pre Existing Network

  • 기존 외부의 네트워크를 사용하려면 다음과 같이 external을 사용하여 명시해야 한다.


networks:
  default:
    external:
      name: my-pre-existing-network


5. Docker Compose Version

1) Versioning

  • docker-compose.yml 상단에 작성하는 Compose File Format의 버전을 명시해 줘야 한다.


version: "3.8"


  • Versioning은 버전별로 다음과 같은 특징이 있다.

1] Version 1

  • 버저닝을 생략

2] Version 2

  • 마이너 버전(2.x)까지 설정해야 한다.
  • 생략 시 2.0으로 적용된다.

3] Version 3

  • 도커 스웜과 같이 사용되도록 디자인되었다.



2) Version Dependency

  • 다음은 각 Docker 버전별 사용가능한 docker-compose.yml 버전이다.


001


(1) Version 1

  • Version 1은 Deprecated되었다.


(2) Version 2

  • Compose 1.6.0 이상 Docker Engine of version 1.10.0+.에서 동작
  • YAML 문서에 버전명을 마이너 버전까지 작성
  • 모든 각각의 서비스는 services 아래에 선언하여 다중 컨테이너 구성
  • volumes에 Named Volumes 생성 가능
  • networks에 Network 생성 가능
  • 기본적으로 모든 컨테이너는 애플리케이션 전체의 기본 네트워크에 연결되며 서비스 이름과 동일한 호스트 이름에서 검색 가능


  • 다음은 Version 2의 예시이다.


version: "2.4"
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
    networks:
      - front-tier
      - back-tier
  redis:
    image: redis
    volumes:
      - redis-data:/var/lib/redis
    networks:
      - back-tier
volumes:
  redis-data:
    driver: local
networks:
  front-tier:
    driver: bridge
  back-tier:
    driver: bridge


  • Version 2에서 추가된 파라미터 중 중요한 항목은 aliases, depends_on이 있다.


version: "2.4"
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres


  • 실행 시 depends_on에 명시된 db, redis가 실행된 다음 web이 실행된다.
  • 실행 순서가 중요한 작업에서 유용하게 사용이 가능하다.


(3) Version 3

  • Version 3에서 삭제된 파라미터는 다음과 같다.


1] volume_driver

2] volumes_from

3] cpu_shares

4] cpu_quota

5] cpuset

6] mem_limit

7] memswap_limit

8] extends

9] group_add


  • 추가된 파라미터는 다음과 같다.


1] deploy


  • Version 3.1에서 추가된 파라미터는 다음과 같다.


1] secrets


  • Version 3.2에서 추가된 파라미터는 다음과 같다.


1] build - cache_from

2] ports, volumeLONG SYNTAX 지원

3] network driver option - attachable

4] deploy - endpoint_mode

5] deploy - placement preference


  • Version 3.3에서 추가된 파라미터는 다음과 같다.


1] build - labels

2] credential_spec

3] configs


  • Version 3.4에서 추가된 파라미터는 다음과 같다.


1] build - target, network

2] healthcheck - start_preiod

3] update - order

4] volumes - name


  • Version 3.5에서 추가된 파라미터는 다음과 같다.


1] service - isolation

2] networks, secrets, configs - name

3] build - shm_size


  • Version 3.6에서 추가된 파라미터는 다음과 같다.


1] tmpfs

2] volume mount type - tmpfs


  • Version 3.7에서 추가된 파라미터는 다음과 같다.


1] service - init

2] deploy - rollback_config

3] root of service, network, volume, secret에 extension-fields 제공


  • Version 3.8에서 추가된 파라미터는 다음과 같다.


1] placement - max_replicas_per_node

2] config, secret - template_driver option (swarm에 배포 시에만 적용)

3] secret - driver, driver_opts (swarm에 배포 시에만 적용)


References