Docker Compose 完整指南:多容器應用程式的定義與執行

概述

版本說明:docker-compose(帶連字號,v1 Python 實作)自 2023 年 7 月已停止維護(EOL)。現行版本為 docker compose(空格,v2 Go 實作,已整合進 Docker Desktop 與 Docker Engine)。本文使用 docker compose 指令。

在現代應用程式開發中,一個專案往往由多個服務組成,例如前端、後端 API、資料庫、快取等。這些服務各自運行在不同的容器中,而 Docker Compose 正是為了解決這種多容器應用程式的定義與管理而生。

Docker Compose 允許您使用一個 YAML 檔案 (docker-compose.yml) 來定義應用程式的所有服務、網路和儲存卷,然後透過單一指令啟動、停止或管理整個應用程式堆疊。這極大地簡化了開發、測試和部署的流程。

常用 Docker Compose 指令

以下是 Docker Compose 的一些常用指令,用於管理您的多容器應用程式:

指令說明範例
docker compose version顯示 Docker Compose 的版本資訊。docker compose version
docker compose --help顯示 Docker Compose 的幫助訊息。docker compose --help
docker compose up啟動並建立所有服務的容器。如果容器已存在,則會重新建立。docker compose up
docker compose up -d在背景模式 (detached mode) 啟動並建立所有服務的容器。docker compose up -d
docker compose down停止並移除所有服務的容器、網路和預設儲存卷。docker compose down
docker compose exec <service_name> <command>在指定服務的容器中執行指令。docker compose exec microService /bin/bash
docker compose ps列出所有服務的容器狀態。docker compose ps
docker compose top顯示服務容器的運行進程。docker compose top
docker compose logs <service_name>顯示指定服務容器的日誌輸出。docker compose logs microService
docker compose config驗證 docker-compose.yml 檔案的語法是否正確,並顯示解析後的配置。docker compose config
docker compose config -q靜默模式驗證配置,只在有錯誤時輸出訊息。docker compose config -q
docker compose restart <service_name>重新啟動指定服務的容器。若未指定服務,則重新啟動所有服務。docker compose restart microService
docker compose start <service_name>啟動指定服務的容器。若未指定服務,則啟動所有已停止的服務。docker compose start microService
docker compose stop <service_name>停止指定服務的容器。若未指定服務,則停止所有運行中的服務。docker compose stop microService

常用組合指令

1
2
# 驗證配置後,停止並重新啟動所有服務
docker compose config -q && docker compose down && docker compose up -d

docker-compose.yml 檔案結構詳解

docker-compose.yml 是 Docker Compose 的核心配置檔案,它使用 YAML 語法來定義應用程式的服務。

Docker Compose v2 注意:Compose 檔案格式版本 2.x 以後,version: 欄位已無意義(被忽略)。Compose v2(docker compose 指令)不再需要此欄位,新範例已移除。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 定義應用程式中的所有服務 (即容器)
services:
    # 自訂服務名稱,例如 'microService'、'web'、'db' 等
    microService:
        # 指定服務所使用的 Docker 映像檔
        image: my_image:1.0
        # 定義容器的名稱,方便識別和管理
        container_name: ms_01
        # 埠映射:將主機埠映射到容器埠 (HOST_PORT:CONTAINER_PORT)
        ports:
            - "8081:8080" # 將主機的 8081 埠映射到容器的 8080 埠
        # 儲存卷映射:將主機路徑映射到容器路徑 (HOST_PATH:CONTAINER_PATH)
        volumes:
            - /Users/bill/microService:/app # 將主機的 /Users/bill/microService 目錄映射到容器的 /app 目錄
        # 指定服務所連接的網路
        networks:
            - my_network # 連接到名為 'my_network' 的網路
        # 定義服務的啟動順序依賴關係
        # 注意:depends_on 只保證服務啟動順序,不保證服務內部應用程式完全就緒
        # 可搭配 https://github.com/vishnubob/wait-for-it 或其他健康檢查機制確保服務可用
        depends_on:
            - redis # microService 服務會在 redis 服務啟動後才啟動
            - mysql # microService 服務會在 mysql 服務啟動後才啟動
        # 範例:等同於 `docker run -d -p 8081:8080 -v /Users/bill/microService:/app --network my_network`

    redis:
        image: redis:6.0.8 # 使用 Redis 6.0.8 映像檔
        ports:
            - "6379:6379" # 映射 Redis 預設埠
        volumes:
            # 映射 Redis 配置檔,讓容器使用自訂配置
            - /app/redis/redis.conf:/etc/redis/redis.conf
            # 映射 Redis 資料目錄,實現資料持久化
            - /app/redis/data:/data
        networks:
            - my_network
        # 覆寫容器的預設啟動指令,讓 Redis 使用指定的配置檔啟動
        command: redis-server /etc/redis/redis.conf

    mysql:
        image: mysql:8.0 # 5.7 已 EOL(2023-10-31)
        # 設定 MySQL 容器的環境變數
        environment:
            MYSQL_ROOT_PASSWORD: '123456' # 設定 root 使用者的密碼
            MYSQL_ALLOW_EMPTY_PASSWORD: 'no' # 不允許 root 密碼為空
            MYSQL_DATABASE: 'my_test_db' # 建立一個名為 'my_test_db' 的資料庫
            MYSQL_USER: 'bill' # 建立一個名為 'bill' 的使用者
            MYSQL_PASSWORD: 'user_pwd' # 設定 'bill' 使用者的密碼
        ports:
            - "3306:3306" # 映射 MySQL 預設埠
        volumes:
            # 映射 MySQL 資料目錄,實現資料持久化
            - /app/mysql/db:/var/lib/mysql
            # 映射 MySQL 配置檔
            - /app/mysql/conf/my.cnf:/etc/my.cnf
            # 映射初始化腳本目錄,容器啟動時會執行此目錄下的 .sh 或 .sql 腳本
            - /app/mysql/init:/docker-entrypoint-initdb.d
        networks:
            - my_network
        # 解決 MySQL 8.0 之後預設認證插件導致外部客戶端無法連接的問題
        command: --default-authentication-plugin=mysql_native_password

# 定義應用程式使用的網路
networks:
    my_network: # 自訂網路名稱,所有服務將在此網路中互相通訊

# 定義應用程式使用的儲存卷 (可選,如果服務中直接使用匿名卷或綁定掛載則不需要在此定義)
# volumes:
#     my_data: # 自訂儲存卷名稱

Healthcheck 與 depends_on condition

基本的 depends_on 只保證容器啟動順序,不等待服務實際就緒。搭配 healthcheck 可確保依賴服務真正可用後再啟動 app:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  app:
    image: my-app
    depends_on:
      db:
        condition: service_healthy  # 等 DB healthcheck 通過再啟動

留言討論