본문 바로가기

Container/Docker

12. Docker Compose

기존에 Docker 컨테이너간에 서로 역할이 분리되어 

 
하나의 서비스를 위해서 Docker 컨테이너를 몇개 생성하려 한다면 아래와 같이 입력해야 한다
 
 
그리고 컨테이너가 서로 연동이 되어야 한다면 컨테이너 사이의 링크 설정도 잘 해야 한다.
 
이런 불편함을 일부 해소할 수 있는 툴이 Docker Compose 라는 툴이다.
 
이 툴은 컨테이너 사이의 설정 과 docker container run 에서 사용하는
    
볼륨, 네트워크, 포트 정보를  docker-compose.yml 파일에 설정하여 멀티 컨테이너를 구동시킬 수 있다. 
 
 
YAML
 
Docker Compose 에서 사용하는 파일은 YAML 형식의 파일이다.
 
아래와 같이 YAML 문법에 대한 간단한 내용 및 샘플을 첨부한다.
 
YAML 문법
 
 
 
YAML 문법 확인
 
 
 
docker compose 지시자
 
docker-compose.yml 파일에는 반드시 image 나 build 지시자가 있어야 한다.
 
주요 키로는 아래와 같은 것들이 있다.
 
image
Docker Image 를 입력
build
Dockerfile 이 있는 디렉토리의 경로를 입력
command
Dockerfile 이나 image 의 시작 command 를 오버라이드
links
컨테이너를 다른 서비스들과 통신 가능하게 연결하고  종속성을 설정
extra_links
컨테이너를 compose 파일 외부에 있는 서비스와 연결
extra_hosts
컨테이너의 /etc/hosts 에 외부 호스트정보를 추가
ports
컨테이너에서 호스트 와 통신할 포트를 지정
expose
호스트에는 노출하지 않고 연결된 서비스끼리만 접근 가능한 포트 입력
volumes
현재 디렉토리를 볼륨으로 마운트할 때 컨테이너에서 사용할 경로를 입력
volumes_from
다른 서비스나 컨테이너의 전체 볼륨을 마운트 한다.
depends_on
 서비스 간에 종속성을 설정
deploy
 도커 스웜에서 사용되는 여러 옵션들을 정의함
secrets
 패스워드 등의 secret 정보를 저장시 사용
 
version   
 
도커 컴포즈 파일 버전명 입력시 version 키를 사용한다.
 
 
services
        
관련있는 컨테이너의 묶을 경우 services 키를 사용한다.
        
예를 들어 웹 서비스를 위해 FrontEnd, Application, DB 같이  3가지 컨테이너가 필요하며
 
이를 services 하위에 정의해 준다.
 
 
image
 
Docker 컨테이너의 기반이 되는 베이스 이미지를 지정하기 위한 키로
 
로컬에 이미지가 없으면 그 이미지를 원격 저장소에서 Pull 해서 가져온다.
 
webserver:
  image: ubuntu
 
webserver2:
  image: ewshin/dockersample:1.0
 
build
 
Dockerfile 을 사용해서 이미지를 빌드 할 경우 build 키를 사용한다.
 
해당 키에는 Dockerfile 이 있는 디렉토리의 경로를 입력한다.
 
# docker-compose.yml 파일을 사용시
 
  services:
    webapp:
      build: ./dir
  
# docker-compose.yml 대신 다른 대체 파일 사용시
 
  services:
    webapp:
      build:
        context: ./dir
        dockerfile: Dockerfile-alternate
  
# 도커 파일 내부에 파라미터가 있는 경우 인자 전달 방법
 
## Dockerfile
    ...
    ARG buildno
    RUN echo "Build number: $buildno"
    
## docker-compose.yaml
    services:
      webapp:
        build:
         context: ./dir
         args:
          - buildno: 1
 
command
 
컨테이너 내에서 동작하는 커맨드 지정시 사용시 command 키를 사용한다.
 
도커 파일 내부에 있는 CMD 가 이미 있는 경우 이를 Override 한다.
 
webserver:
  image: ubuntu
  command: /bin/bash
 
entrypoint
 
컨테이너 내에서 동작하는 엔트리 포인트 지정시 entrypoint 키를 사용한다.
 
도커 파일 내부에 있는 ENTRYPOINT 가 이미 있는 경우 이를 Override 한다.
 
webserver:
  image: ubuntu
  entrypoint: /code/entrypoint.sh
 
아래와 같이 명령을 직접 실행할 수도 있다.
 
configuration_generator:
  image: java:8-jdk
  entrypoint:
    - java
    - -cp
    - zookeeper-vertx-example-0.0.1-fat.jar
    - com.lukeolbrish.example.ConfigurationGenerator
 
 
links
 
컨테이너를 다른 서비스들과 연결시 links 키를 사용한다.  
 
그러면 이 컨테이너의 /etc/hosts 파일에 그 내용이 추가되어서 
 
컨테이너에서 다른 컨테이너 들에 접근할 수 있게 된다.
 
links:
 - db
 - db:database
 - redis
 
위와 같이 설정을 하게 되면 /etc/hosts 파일에 내용이 아래와 같이 변경되어 
 
링크된 컨테이너와 통신할 수 있게 된다.
 
192.168.1.186 db
192.168.1.186 database
192.168.1.187 redis
 
또한 링크는 depends_on과 같은 방식으로 서비스 간의 종속성을 표현하므로 서비스 시작 순서를 결정한다.
 
( 즉 links 에 기술한 서비스가 먼저 시작된다. )
 
external_links
 
links 와 비슷한 방식으로 설정하지만 compose 내부의 컨테이너가 아니라
 
compose 외부에 있는 서비스 혹은 컨테이너와 연결시 external_links 키를 사용한다.
 
external_links:
  - redis
  - project_db:mysql
 
external_hosts
 
컨테이너의 /etc/hosts 에 외부 호스트정보를 추가시 external_hosts 키를 사용한다.
 
extra_hosts:
  - "somehost: 162.168.2.31"
 
위와 같이 설정을 하게 되면 /etc/hosts 파일에 내용이 아래와 같이 변경된다.
 
162.168.2.31 somehost
 
volumes
 
컨테이너에 볼륨을 마운트 시 volumes 키를 사용한다.
 
볼륨을 ready only 으로 마운트 하고자 하는 경우 :ro 를 붙임 (설정 파일등에 유용)
 
볼륨을 ready write 로 마운트 하고자 하는 경우 :rw 를 붙임
 
type 이 volume 인 경우 NamedVolume 을 말하고 type 이 bind 인 경우
 
정적 bind ( 호스트 볼륨 ) 및 동적 bind (source 를 비워놓음) 를 사용할 수 있다.
 
version: "3.2"
services:
web:
  image: nginx:alpine
  volumes:
   # Named Volumes 
   - type: volume
     source: mydata
     target: /data
     volume:
       nocopy: true
  # Host Volumes
   - type: bind
     source: ./static
     target: /opt/app/static
 
db:
  image: postgres:latest
  volumes:
    #SHORT SYNTAX
   - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
   - "dbdata:/var/lib/postgresql/data"
 
# Named Volumes
volumes:
  mydata:
  dbdata
 
environment
 
컨테이너 내의 환경 변수 지정시 environment 키를 사용한다.
 
#배열 형식으로 지정
environment:
  - HOGE=fuga
  - FOO
 
#hash 형식으로 지정
environment:  
  HOGE: fuga
  FOO:
 
env_file
 
환경변수를 정의한 파일을 호출하는 파일을 지정시 env_file 킬르 사용한다.
 
설정하고자 하는 환경 변수가 많은 경우 env_file 에 환경 변수값을 넣어놓고 
 
이를 가져다 사용하는 경우 필요하다.
 
#환경변수 파일 읽기
env_file: .env
 
#여러 개의 환경변수 파일 읽기
env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env
 
depends_on
 
서비스간 명시적 종속성을 부여하고자 하는 경우 depends_on 키를 사용한다.
 
아래와 같이 설정하면 db와 redis 시작전에 시작 web 이 먼저 시작된다.
 
다른 방법으로는 links 로 다른 컨테이너와 연결 관계를 설정하면 
 
암시적으로 연결이 필요한 컨테이너를 먼저 구동시킨다.
 
version: '3'
services:
  web:
    build: .
    depends_on:
       - db
       - redis
  redis:
    image: redis
  db:
    image: postgres
 
devices

 

호스트의 장치를 컨테이너에서 사용할 수 있도록 연결시 devices 키를 사용한다.
 
devices:
  - "/dev/ttyUSB0:/dev/ttyUSB0"
 
ports
 
<컨테이너 포트> 혹은 <호스트 포트>:<컨테이너 포트> 형식으로 
 
컨테이너에서 호스트 와 통신할 포트를 지정시 ports 키를 사용한다.
 
# 호스트 포트와 컨테이너 포트를 둘 다 지정하는 경우
mysql:
image: mysql:5.7
ports:
  - 3333:3306
 
 
# 컨테이너 포트만 지정하는 경우
mysql:
  image: mysql:5.7
  ports:
    - 3306
 
호스트 포트를 생략하고 컨테이너 포트만 지정하면 호스트 포트는 랜덤으로 지정된다.
 
# 호스트 포트와 컨테이너 포트를 둘 다 지정하는 경우
$ docker-compose ps
Name      Command                            State       Ports
-------------------------------------------------------------------------------------
mysql_1   docker-entrypoint.sh mysqld     Up         0.0.0.0:3333->3306/tcp
 
# 컨테이너 포트만 지정하는 경우
$ docker-compose ps
Name      Command                           State       Ports
-------------------------------------------------------------------------------------
mysql_1   docker-entrypoint.sh mysqld    Up         0.0.0.0:32769->3306/tcp
 
expose
 
호스트에는 노출하지 않고 연결된 서비스끼리만 접근 가능한 포트를 입력시 expose 키를 사용한다.
 
이 포트는 호스트에서 접근 할 수 없고 docker-compose up 시 연결할 포트를 지정한다.
 
dockerfile 의 EXPOSE 와 동일하다.
 
mysql:
  image: mysql:5.7 
  expose:
    - 3306/tcp
 
아래와 같이 host 쪽과 연결 정보가 없다.
 
$ docker-compose ps
 
  Name      Command                               State              Ports
  -------------------------------------------------------------------------------------
  mysql_1    docker-entrypoint.sh mysqld       Up                3306/tcp   
 
dns
 
컨테이너에서 사용할 DNS 서버를 설정하려는 경우 dns 키를 사용한다.
 
dns:
  - 8.8.8.8
  - 9.9.9.9
 
dns-search
 
컨테이너에서 사용할 DNS 검색 도메인을 설정하는 경우 dns-search 키를 사용한다.
 
dns_search:
 
 
network-mode
 
컨테이너의 네트워크 모드를 설정하는 경우 network-mode 키를 사용한다.
 
# Docker 네트워크 브리지에 새 네트워크를 생성
network_mode: "bridge"
 
# 컨테이너 안에서 호스트의 네트워크를 그대로 사용
network_mode: "host"
 
# 네트워크를 사용안함
network_mode: "none"
 
# 다른 서비스의 네트워크를 함께 사용
network_mode: "service:[service name]"
 
# 다른 컨테이너의 네트워크를 함께 사용
network_mode: "container:[container name/id]"   
 
 
networks 
 
네트워크를 구성하기 위한 최상위 키,  이하 옵션 키들을 조합하여  네트워크를 지정해야 한다.
 
 
driver
 
bridge / overlay / host / none 인 경우를 지정하는데 host 나 none 인 경우는 network_mode 를 사용하는 방식이 더 편하다.
 
version: "3.3"
services:
  wordpress:
    image: wordpress
    ports:
      - "8080:80"
    networks:
      mynet1:
        driver: overlay
 
networks:
  mynet1:
이외 옵션들이 많이 있다.
 
 
페이지에서 확인하자.
 
 
healthcheck
 
서비스의 상태를 체크하려는 경우 healthcheck 키를 사용한다.
 
# 컨테이너가 시작하고 40초 후부터 1분 30 초마다 웹 서버가 10초 이내에 
# 기본 페이지를 제공하는 확인
# 실패시 3번 재시도 함
#
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"]
  interval: 1m30s
  timeout: 10s
  retries: 3
  start_period: 40s
 
secrets
 
v 3.1 이상에서 사용할 수 있다.
 
서비스에 암호화가 필요한 데이터를 넣으려는 경우 secrets 키를 사용하면 된다.
 
# psql_user, psql_password 를 암호화 
# 해당 데이터를 environment 에서 사용함
#
version: "3.1"
 
services:
  psql:
    image: postgres
    secrets:
      - psql_user
      - psql_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/psql_password
      POSTGRES_USER_FILE: /run/secrets/psql_user
 
secrets:
  psql_user:
    file: ./psql_user.txt
  psql_password:
    file: ./psql_password.txt
 
사실 파일을 사용하기 보다는 secret 에 들어가는 것은 바로 만들어 사용해야 한다.
 
# psql_user, psql_password 를 암호화 
# 해당 데이터를 environment 에서 사용함
#
version: "3.1"
 
services:
  psql:
    image: postgres
    secrets:
      - psql_user
      - psql_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/psql_password
      POSTGRES_USER_FILE: /run/secrets/psql_user
 
secrets:
  psql_user:
    external: true
  psql_password:
    external: true
 
그리고 secret 오브젝트를 CLI 에서 만들면 된다.
 
# echo "<string>" | docker secret create psql_user - <value>
 
$ echo "aaafba" | docker secret create psql-password -
#asdoaiuer34543490$#apdf
 
$ echo "aaafba" | docker secret create psql-user - 
wer$%$#%&fgsfgsdf456ghj
 
Docker Compose CLI
 
주요 명령으로 아래와 같은 것들이 있다.
 
# Help
docker-compose --help
 
# volumes/networks 를 셋팅하고 모든 컨테이너를 시작
docker-compose up
 
# 다른 경로에 있는 yml 파일 사용시 -f 옵션 사용
docker-compose -f ./sample/docker-compose.yml up
 
# 컨테이너를 백그라운드에서 구동
docker-compose up -d
 
 
# 모든 컨테이너를 스톱하고 cont/vol.net 을 제거함
docker-compose down
 
# 실행중인 컨테이너 표시
docker-compose ps
 
# 실행중인 컨테이너의 프로세스 표시
docker-compose top
 
 
Docker Compose Version
 
 
 
 
V1 은 기본 컨테이너 네트워크로 기본 브리지 네트워크를 사용하여 
 
만약에 사용자가 다른 network 를 사용하려고 하면 이를 표현하는 지시자가 없고
 
컨테이너간 종속성을 관리하기 위한 지시자도 없었다.
 
 
V2 는 다양한 네트워크를 정의할 수 있고 컨테이너 종속성도 관리할 수 있도록 지시자를 추가했다.
 
그리고 version 지시자를 추가했고 services 라는 지시자 아래 서비스를 기술하도록 바뀌었다.
 
 
V3 는 V2 와 비슷하지만 V3 에서는 Docker Swarm 을 지원하며 V2 에서 지원하던 몇몇 지시자가 빠지거나 바뀌었다.
 
V3 의 지시자 부분은 Docker Swarm 부분을 정리할 때 다루기로 하자.
 
 
Exercise
 
docker run 으로 시작해서 서비스를 구동해보고
 
이를 docker-compose.yaml 에 기술하여 멀티 컨테이너를 구동하는 연습을 해보자.
 
해당 App 은 사용자가 voting-app 을 통해 투표를 하면
 
result-app 을 통해 통합 결과를 화면에 보여주는 구조로 되어 있다.
 
 
단순히 이 구조를 연결 링크 관계없이 아래와 같이 컨테이너만 구동하면 컨테이너간은 
 
$ docker run -d --name=redis redis
 
$ docker run -d --name=db
 
$ docker run -d --name vote -p 5000:80 voting-app
 
$ docker run -d --name=result -p 5001:80 result-app
 
$ docker run -d --name=worker worker
 
 
다른 컨테이너의 존재를 모르므로 아래의 에러 메시지를 보게 될 것이다.
 
 
따라서 컨테이너 간에 연결 관계를 확인해 보면 다음과 같다.
 
voting-app (python) 웹 서버 코드 조각을 보면 웹 서버가 시작되면 redis 를 찾는다.
 
 
result-app (node-js) 웹 서버 코드 조각을 보면 웹 서버가 시작되면 db 를 찾는다.
 
 
worker (.NET) 웹 서버 코드 조각을 보면 백엔드 프로세스는 db 와 redis 를 모두 확인한다.
 
즉 각 링크 관계를 생각해보면 아래와 같은 구조로 링크가 맺어지므로
 
 
 docker run 명령에 아래와 같이 --link 옵션을 더 주어야 한다.
 
$ docker run -d --name=redis redis
 
$ docker run -d --name=db
 
$ docker run -d --name vote -p 5000:80  --link redis:redis voting-app
 
$ docker run -d --name=result -p 5001:80 --link db:db result-app
 
$ docker run -d --name=worker --link db:db --link redis:redis worker
그리고 voting-app 에 접근해서 /etc/hosts 파일을 확인해 보면
 
아래와 같이 변경되어 있는 것을 확인할 수 있다.
 
 
이제 이 과정을 docker-compose.yml 파일에 녹여보자.
 
먼저 할일은 docker-compose.yml 파일을 만들고 컨테이너 이름을 넣는다.
 
 
그리고 각 컨테이너에서 사용한 이미지를 넣는다.
 
 
그 다음 사용한 포트 정보를 입력한다.
 
 
마지막으로 사용하는 링크 정보를 넣는다.
 
 
위의 경우 docker compose v1 기준으로 작성하였는데
 
여기에 더해 만약 네트워크를 사용자 트래픽을 받을 front-end 와 앱 통신에서 사용할
 
back-end 로 나누었다고 생각해 보자. 
 
   
 
이 경우 아래와 같이 변경하면 된다.
 
 
그리고 나서 간단히 아래와 같이 구동하면 된다.
 
 
 
 
 
 

'Container > Docker' 카테고리의 다른 글

14. Docker Network Test  (0) 2020.01.14
13. Docker Network Overview  (0) 2020.01.14
11. Docker Container Life Cycle  (0) 2020.01.14
10. Docker Image 생성 (도커파일)  (0) 2020.01.14
09. Docker Image 생성 (기존이미지 사용)  (0) 2020.01.14