[Docker] Docker compose κ°œλ…, 문법, 예제

Docker Compose λž€?

  • 응집λ ₯ μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ ν•¨κ»˜ μž‘λ™ν•˜λŠ” μ—¬λŸ¬ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‹€ν–‰ν•˜λ €λŠ” 둜컬 개발 및 ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ μœ„ν•΄ μ„€κ³„λœ 도ꡬ
  • docker compose yamlμ½”λ“œλŠ” μ—¬λŸ¬ 개의 docker run μ‹€ν–‰κ³Ό μœ μ‚¬ν•˜λ©° λ„€νŠΈμ›Œν¬, λ³Όλ₯¨ 등을 ν•œλ²ˆμ— 생성할 수 μžˆλ‹€.
  • docker compose둜 μƒμ„±λœ μ»¨ν…Œμ΄λ„ˆλ“€μ€ λ…λ¦½λœ λ„€νŠΈμ›Œν¬λ‘œ κ΅¬μ„±λ˜λ―€λ‘œ μ»¨ν…Œμ΄λ„ˆ  κ°„ 톡신이 쉽닀.
  • Docker file μž‘μ„± -> docker-compose.yml μž‘μ„± -> docker compose up
  • μž₯점
    • μ„œλ‘œ λ‹€λ₯Έ OSν™˜κ²½μ΄λΌλ„ λ™μΌν•œ ν™˜κ²½κ΅¬μ„±μ΄ κ°€λŠ₯ν•˜λ‹€. 
    • λ™μΌν•œ ν™˜κ²½μ„ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— κ°œλ°œν™˜κ²½μ— μ΄μŠˆκ°€ λ°œμƒν•΄λ„ νŒ€κ°„ μ†Œν†΅μ΄ 쉽닀.
    • λ³΅μž‘ν•œ ν™˜κ²½λ„ YAML μ½”λ“œλ‘œ μŠ€ν¬λ¦½νŠΈν™” ν•  수 있기 λ•Œλ¬Έμ— μžλ™ν™”κ°€ κ°€λŠ₯ν•˜λ‹€.
    • docker compose CLIλ₯Ό μ΄μš©ν•˜μ—¬ μ‰½κ²Œ "λ©€ν‹° μ»¨ν…Œμ΄λ„ˆ μ• ν”Œλ¦¬μΌ€μ΄μ…˜"을 관리할 수 μžˆλ‹€.
  • 단점
    • λ™μ‹œμ— λ‹€μˆ˜μ˜ μ»¨ν…Œμ΄λ„ˆ μ„œλΉ„μŠ€λ₯Ό μˆ˜ν–‰ν•˜λŠ” 경우 μžμ› ν™œμš©λ₯ μ΄ μˆœκ°„ λ†’μ•„μ§ˆ 수 μžˆλ‹€.

 

 

Docker compose 와 Kubernetes의 차이

 

 

Docker compose νŠΉμ§•

  • docker composeλŠ” μ—¬λŸ¬ 번의 docker cliλ₯Ό μ‹€ν–‰ν•˜μ§€ μ•Šκ³ , ν•œλ²ˆμ— κ΄€λ ¨ μ• ν”Œλ¦¬μΌ€μ΄μ…˜λ“€μ„ YAML파일둜 κ΅¬μ„±ν•˜μ—¬ λ‚΄λΆ€ ν™˜κ²½ ꡬ성과 속성을 μ‹€ν–‰ν•  수 μžˆλ‹€.
  • μ„€μ • 값을 μΊμ‹±ν•˜κΈ° λ•Œλ¬Έμ— μž¬μ‹œμž‘ μ‹œ 변경이 μ—†λ‹€λ©΄ μΊμ‹±λœ 정보λ₯Ό κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜μ—¬ λΉ λ₯Έ μ„œλΉ„μŠ€ 싀행을 보μž₯ν•  수 μžˆλ‹€. 
  • YAML μ½”λ“œμ— ν¬ν•¨λœ μ• ν”Œλ¦¬μΌ€μ΄μ…˜λ“€μ€ 동일 λ„€νŠΈμ›Œν¬μ— ν¬ν•¨λ˜κΈ° λ•Œλ¬Έμ— λ³΅μž‘ν•œ μ—°κ²° ꡬ성 없이도 μ‰½κ²Œ API 톡신이 κ°€λŠ₯ν•˜λ‹€. 

 

 

자주 μ“°λŠ” Docker compose 문법

build

mydiary-front: 
	build: 
		context: ./my-diary-front 	#Dockerfile μœ„μΉ˜ (동일 경둜면 μƒλž΅ κ°€λŠ₯)
		dockerfile: Dockerfile 		#μ œκ³΅ν•˜λŠ” Dockerfile 이름

container_name

  • μƒλž΅ μ‹œ μžλ™μœΌλ‘œ λΆ€μ—¬, "디렉토리λͺ…_μ„œλΉ„μŠ€λͺ…_n"
  • docker run의 --name μ˜΅μ…˜κ³Ό 동일

 

ports

  • μ„œλΉ„μŠ€ λ‚΄λΆ€ ν¬νŠΈμ™€ μ™ΈλΆ€ 호슀트 포트λ₯Ό μ§€μ •ν•˜μ—¬ λ°”μΈλ“œ. μ™ΈλΆ€ λ…ΈμΆœ 포트 지정
  • docker run의 -p μ˜΅μ…˜κ³Ό 동일

 

networks

  • μ΅œμƒμœ„ 레벨의 networks에 μ •μ˜λœ λ„€νŠΈμ›Œν¬ 이름을 μž‘μ„±
  • docker run의 --net(--network) μ˜΅μ…˜κ³Ό 동일

 

volumes

  • μ„œλΉ„μŠ€ λ‚΄λΆ€ 디렉토리와 호슀트 디렉토리λ₯Ό μ—°κ²°ν•˜μ—¬ 데이터 지속성 μ„€μ •.
  • docker run의 -v

 

environment

  • μ„œλΉ„μŠ€ λ‚΄λΆ€ ν™˜κ²½ λ³€μˆ˜ μ„€μ •
  • ν™˜κ²½ λ³€μˆ˜κ°€ λ§Žμ€ 경우, 파일 (.env)둜 λ§Œλ“€μ–΄ 지정 (env_file: ./envfile.env)

 

command

  • μ„œλΉ„μŠ€κ°€ ꡬ동 이후 μ‹€ν–‰ν•  λͺ…λ Ήμ–΄ μž‘μ„±
  • docker run의 λ§ˆμ§€λ§‰μ— μž‘μ„±λ˜λŠ” λͺ…λ Ήμ–΄

 

restart

  • μ„œλΉ„μŠ€ μž¬μ‹œμž‘ μ˜΅μ…˜ 지정
  • docker run의 --restart μ˜΅μ…˜κ³Ό 동일

 

depends_on

  • μ„œλΉ„μŠ€ κ°„μ˜ 쒅속성을 의미.
  • λ¨Όμ € μ‹€ν–‰ν•΄μ•Ό ν•˜λŠ” μ„œλΉ„μŠ€λ₯Ό μ§€μ •ν•˜μ—¬ μˆœμ„œ 지정

 

deploy.replicas

  • ν•΄λ‹Ή μ„œλΉ„μŠ€μ˜ 볡제 μ»¨ν…Œμ΄λ„ˆ 수 지정
  • replicaλ₯Ό μ‚¬μš©ν•  땐 μ»¨ν…Œμ΄λ„ˆ 이름을 μ§€μ •ν•˜λ©΄ μ•ˆ λœλ‹€. (μœ μΌν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έ)

 

 

 

μ˜ˆμ‹œ1 Docker compose file [Nodejs+ Spring + MySQL + Nginx] 

 

docker-compose.yml

version: '3.8'

services:
 mydiary-db:
  image: mysql:5.7-debian
  container_name: rolling-db
  environment:
   MYSQL_ROOT_PASSWORD: pass123
   MYSQL_DATABASE: paperdb
   MYSQL_ROOT_HOST: '%'
   MYSQL_USER: user
   MYSQL_PASSWORD: user
  ports:
   - '3306:3306'
  networks:
   - mydiary-net
  restart: always
  command:
   - --character-set-server=utf8
   - --collation-server=utf8_general_ci

 mydiary-back:
  build:
   context: ./my-diary-back
   dockerfile: Dockerfile
  deploy:
   replicas: 3
  restart: always
  depends_on:
   - mydiary-db
  ports:
   - '8081-8083:8080'
  environment:
   SPRING_DATASOURCE_URL: jdbc:mysql://rolling-db:3306/paperdb?serverTimezone=Asia/Seoul
   SPRING_DATASOURCE_USERNAME: user
   SPRING_DATASOURCE_PASSWORD: user
  networks:
   - mydiary-net

 mydiary-front:
  build:
   context: ./my-diary-front
   dockerfile: Dockerfile
  deploy:
   replicas: 3
  restart: always
  depends_on:
   - mydiary-back
  ports:
   - '3000-3002:3000'
  networks:
   - mydiary-net

 proxy-be:
  image: nginx:1.21.5-alpine
  container_name: rolling-server-lb
  restart: always
  depends_on:
   - mydiary-back
  ports:
   - '8080:80'
  volumes:
   - ${PWD}/proxy/nginx-be.conf:/etc/nginx/nginx.conf
  networks:
   - mydiary-net

 proxy-fe:
  image: nginx:1.21.5-alpine
  container_name: rolling-front-lb
  restart: always
  ports:
   - '80:80'
  volumes:
   - ${PWD}/proxy/nginx-fe.conf:/etc/nginx/nginx.conf
  networks:
   - mydiary-net

networks:
 mydiary-net:
  driver: bridge
  ipam:
   driver: default
   config:
   - subnet: 172.20.0.0/24
     ip_range: 172.20.0.0/24
     gateway: 172.20.0.1

 

Nginx backend conf

  • μœ„μ—μ„œ μ„€μ •ν•œ λ°±μ—”λ“œ μ»¨ν…Œμ΄λ„ˆ service name인  "mydiray-back" 을 μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.
  • ν”„λ‘ νŠΈμ—μ„œ νŠΈλž˜ν”½μ„ 전달받을 λ•Œ mydiary-back에닀 ν•˜λ‚˜μ”© 전달할 수 있기 λ•Œ. 
events { worker_connections 1024; }
http{
    upstream mydiary-back { # upstream의 이름을 rolling-server둜 μ„€μ •
    server mydiary-back:8081;
    server mydiary-back:8082;
    server mydiary-back:8083;
    }
    server {
            listen *:8080 default_server; # ν΄λΌμ΄μ–ΈνŠΈκ°€ μš”μ²­ν•˜λŠ” 포트 번호
    location / {
            proxy_pass http://mydiary-back; # μ„€μ •ν•œ upstream으둜 μš”μ²­ 보내기
            }
    }
}

 

Nginx front conf

events { worker_connections 1024; }
http {
    upstream front-servers {
        server mydiary-front:3000;
        server mydiary-front:3001;
        server mydiary-front:3002;
    }
    server {
        listen 80 default_server;
        location / {
            proxy_pass http://front-servers;
        }
    }
}

 

 

 

 

μ˜ˆμ‹œ2 Docker compose file [Spring+ MariaDB + Redis] 

MariaDB

  • /database/Dockerfile
FROM mariadb:10

ENV TZ=Asia/Seoul
  • /database/config/mariadb.cnf
[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
skip-character-set-client-handshake

[mysqldump]
default-character-set=utf8mb4

 

Redis

  • /redis/Dockerfile
FROM redis:6

ENV TZ=Asia/Seoul

 

Spring

  • ./Dockerfile
FROM openjdk:11
ARG JAR_FILE=build/libs/app.jar
COPY ${JAR_FILE} ./app.jar
ENV TZ=Asia/Seoul
ENTRYPOINT ["java", "-jar", "./app.jar"]

 

docker-compose.yml

  • ./docker-compose.yml
version: "3.8"                                          # 파일 규격 버전
services:                                               # 이 ν•­λͺ© 밑에 μ‹€ν–‰ν•˜λ €λŠ” μ»¨ν…Œμ΄λ„ˆλ“€μ„ μ •μ˜
  pharmacy-recommendation-redis:                        # μ„œλΉ„μŠ€λͺ…
    container_name: pharmacy-recommendation-redis       # μ»¨ν…Œμ΄λ„ˆ λͺ…
    build:
      dockerfile: Dockerfile
      context: ./redis
    image: dgjinsu/pharmacy-recommendation-redis
    ports:
      - "6380:6379"
  pharmacy-recommendation-database:
    container_name: pharmacy-recommendation-database
    build:
      dockerfile: Dockerfile
      context: ./database
    image: dgjinsu/pharmacy-recommendation-database
    environment:
      - MARIADB_DATABASE=pharmacy-recommendation
      - MARIADB_ROOT_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
    volumes:
      - ./database/config:/etc/mysql/conf.d
      - ./database/init:/docker-entrypoint-initdb.d
    ports:
      - "3307:3306"      # μ ‘κ·Ό 포트 μ„€μ • (μ»¨ν…Œμ΄λ„ˆ μ™ΈλΆ€:μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€)
  pharmacy-recommendation-app:
    container_name: pharmacy-recommendation-app
    build: .
    depends_on:          # DB, REDIS μ»¨ν…Œμ΄λ„ˆκ°€ μ‹€ν–‰λœ λ‹€μŒ WEB을 μ‹€ν–‰μ‹œν‚¨λ‹€.
      - pharmacy-recommendation-database
      - pharmacy-recommendation-redis
    image: dgjinsu/pharmacy-recommendation-app
    environment:
      - SPRING_DATASOURCE_USERNAME=${SPRING_DATASOURCE_USERNAME}
      - SPRING_DATASOURCE_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
      - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
      - KAKAO_REST_API_KEY=${KAKAO_REST_API_KEY}
    ports:
      - "80:8080"
    restart: always # depends on은 μ‹€ν–‰ μˆœμ„œλ§Œ 컨트둀 할뿐,
      # μ»¨ν…Œμ΄λ„ˆ μ•ˆμ˜ μ„œλΉ„μŠ€κ°€ μ‹€ν–‰κ°€λŠ₯ν•œ μƒνƒœμΈμ§€κΉŒμ§€λŠ” 확인 ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—
    # DB λ˜λŠ” Redisκ°€ 아직 μ‹€ν–‰κ°€λŠ₯ν•œ μƒνƒœκ°€ μ•„λ‹ˆμ—¬μ„œ μ‹€νŒ¨ν•˜λŠ” 경우 μž¬μ‹œμž‘ ν•˜λ„λ‘ μ„€μ •