Docker Compose YAML 完整教學:從零開始打造多容器自動化部署流程

在現代的微服務(Microservices)架構中,單一容器的運行早已無法滿足複雜的應用需求。一個典型的現代化 Web 應用,通常需要包含前端網頁伺服器、後端 API 邏輯、資料庫、快取機制(如 Redis)以及訊息佇列(如 RabbitMQ)。如果我們使用傳統的 docker run 指令來逐一啟動這些服務,不僅過程極其繁瑣,更會面臨網路連線、環境變數、啟動順序等一系列難以管理的痛點。

這就是 Docker Compose 出現的原因。透過一個單一的 docker-compose.yml 設定檔,你可以將整個應用程式的基礎設施「程式碼化」(Infrastructure as Code, IaC),實現一鍵部署、一鍵啟動、一鍵銷毀。

本篇 Docker Compose YAML 完整教學 將帶領你從基礎語法開始,深入探討如何撰寫高效、安全且具備擴展性的 YAML 配置,並透過實戰案例讓你掌握多服務部署的核心技術。


1. 認識 Docker Compose:為什麼你不再需要手動執行 Docker Run?

在深入研究 YAML 語法之前,我們必須先理解 Docker Compose 解決的核心問題。

Docker 與 Docker Compose 的核心差異

許多初學者會混淆 Docker 與 Docker Compose 的角色。簡單來說: * Docker:是一個容器化引擎,負責建立、執行與管理單個容器(Container)。 * Docker Compose:是一個工具(Tool),用於定義與執行多容器的 Docker 應用程式。它建立在 Docker 之上,透過讀取 YAML 檔案來管理一組相互關聯的容器。

什麼是「多容器編排」 (Orchestration)?

當你的應用程式拆分為多個服務時,你需要處理以下挑戰: 1. 依賴關係:資料庫必須在 API 啟動前準備就緒。 2. 網路通訊:API 服務如何透過特定的名稱找到資料庫服務? 3. 環境一致性:開發、測試、生產環境的配置如何保持同步?

Docker Compose 提供了一種「宣告式」的機制。你只需要告訴 Docker:「我需要一個 Nginx、一個 Python App 和一個 PostgreSQL」,Docker Compose 就會自動處理網路建立、磁碟掛載與啟動順序。如果你正在尋找更多關於自動化部署的資源,可以參考我們的 Docker Compose 實戰指南 來深化你的知識。

Docker Compose 的核心價值:可重複性與一致性

使用 Docker Compose 的最大好處在於可重複性。當你將 docker-compose.yml 提交到 Git 版本控制系統後,任何開發者或 CI/CD 流水線只要執行 docker-compose up,就能在完全相同的環境下重現整套服務架組成的架構,徹底解決了「在我的電腦上明明可以跑」的經典問題。


2. Docker Compose YAML 檔案結構深度解析

YAML (YAML Ain't Markup Language) 是一種人類可讀性極高的資料序列化格式。在 Docker Compose 中,正確的縮排(Indentation)是成敗的關鍵。

YAML 語法基礎與注意事項

在撰寫 Compose 檔案時,請務必遵守以下規則: * 使用空格而非 Tab:YAML 不支援 Tab 鍵,請務限定位使用空格(通常是 2 個或 4 個)。 * 鍵值對 (Key-Value Pairs):使用冒號加空格 key: value 的格式。 * 列表 (Lists):使用橫線 - 開頭來表示陣列或清單。 * 大小寫敏感:Services 與 services 是不同的,請務必使用小寫。

核心組成要素:Version, Services, Networks, Volumes

一個完整的 docker-compose.yml 通常包含以下四個主要區塊:

  1. version:指定 Compose 檔案的版本格式(雖然在最新的 Docker Compose V2 中這已不再是強制要求,但保留版本號仍是良好的實踐)。
  2. services:這是最核心的部分,定義了每一個容器的行為、映像檔、環境變數、連接埠映射等。
  3. networks:定義容器之間的通訊網路。透過自定義網路,你可以實現服務間的隔離與安全通訊。
  4. volumes:定義持久化儲存空間。確保容器刪除後,資料庫中的數據不會隨之消失。

逐行拆解:一個標準的 docker-compose.yml 範例

讓我們透過一個簡單的結構來觀察各個元件如何協作:

version: '3.8' # 指定版本

services:
  web-app: # 服務名稱
    image: nginx:latest # 使用的映像檔
    ports:
- "8080:80" # 主機連接埠:容器連接埠
    networks:
      - frontend

networks:
  frontend: # 定義一個名為 frontend 的網路
    driver: bridge

3. 實戰演練:從零開始部署一個 Web + Database + Redis 系統

為了讓你真正掌握技術,我們將模擬一個真實的開發場景:部署一個包含 Nginx (反向代理)、Python API (應用程式)、PostgreSQL (資料庫) 與 Redis (快取) 的完整系統。

環境準備與專案目錄結構

在開始之前,建議建立如下的目錄結構,這有助於管理你的程式碼與設定檔:

my-project/
├── docker-compose.yml
├── .env                  # 存放敏感資訊(如密碼)
├── nginx/
│   └── default.conf      # Nginx 設定檔
├── app/
│   ├── main.py           # Python 程式碼
│   └── requirements.txt
└── data/                 # 用於掛載資料庫持久化

撰寫 docker-compose.yml(包含詳細註解)

請將以下內容複製到你的 docker-compose.yml 中。這是一個具備生產力水準的配置範例。

version: '3.8'

services:
  # 1. Nginx 反向代理服務
  proxy:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - api
    networks:
      - web_network

  # 2. Python API 應用程式
  api:
    build: ./app           # 使用當前目錄下的 Dockerfile 進行構建
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
      - REDIS_HOST=cache
    depends_on:
      - db
      - cache
    networks:
      - web_network
      - backend_network

  # 3. PostgreSQL 資料庫
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTG驗證_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - backend_network

  # 4. Redis 快取服務
  cache:
    image: redis:7-alpine
    networks:
      - backend_network

# 定義網路隔離
networks:
  web_network:
    driver: bridge    # 用於前端與 API 溝通
  backend_network:
    driver: bridge    # 用於 API 與 DB/Redis 溝通,確保 DB 不直接暴露在外部

# 定義持久化磁碟卷
volumes:
  db_data:            # 命名卷,確保資料庫重啟後數據不丟失

關鍵參數詳解

在上述範例中,有幾個進階參數是開發者必須精通的:

  • build:與 image 不同,build 會告訴 Docker Compose 去找尋指定的路徑下的 Dockerfile 來現場製作映像檔。這對於開發階段非常有用。
  • depends_on:這定義了啟動順序。在上面的例子中,api 會等待 db 與 cache 啟動後才開始執行。但請注意,depends_on 只保證容器「啟動」,不保證內部的資料庫服務已經「準備好接收連線」,因此在程式碼中仍需加入重試機制。
  • volumes (Bind Mount vs Named Volume):
    • Bind Mount (./nginx/...:/etc/...):將主機的特定路徑掛載進容器,適合存放設定檔,方便即時修改。
    • Named Volume (db_data:/var/lib/...):由 Docker 管理的命名卷,效能較好,適合存放資料庫等需要高 IO 的數據。
  • networks (網路隔離):我們建立了 web_network 與 backend_network。注意到 proxy 只能連到 api,而 db 卻無法被 proxy 直接存取。這種隔離機制是提升系統安全性的核心手段。

執行指令:掌控你的容器生命週期

學會了撰寫 YAML,接下來就是如何操作。請在專案目錄下執行以下指令:

  1. 啟動所有服務(後台執行): docker-le-compose up -d
  2. 查看服務運行狀態: docker-compose ps
  3. 查看服務日誌(除錯必備): docker-compose logs -f api
  4. 停止並移除所有容器與網路: docker-compose down
  5. 重新構建並啟動(當你修改了 Dockerfile 時): docker-compose up -d --build

如果你在部署過程中遇到環境變數配置的困難,可以利用我們的 DevOps 自動化工具集 來優化你的工作流程。


4. 進階技巧:優化你的 Docker Compose 配置

當你的專案規模擴大,單純的 docker-compose.yml 會變得難以維護。以下是專業開發者會使用的三種進階技巧。

使用 .env 檔案管理環境變數

絕對不要將資料庫密碼或 API Key 直接寫死在 docker-compose.yml 中。這會導致安全風險,且不利於不同環境的切換。

建立一個 .env 檔案:

DB_PASSWORD=super_secret_password_12html
API_PORT=8000

然後在 docker-compose.yml 中引用:

services:
  db:
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}

這樣一來,你只需要在 .env 中修改,就能在不更動主配置檔案的情況下切換環境。

網路隔離 (Networks) 的實作與重要性

在微服務架構中,安全性來自於「最小權限原則」。你不希望你的資料庫直接暴露在公網(Public Network)上。透過建立兩個不同的網路(如前文範例所示),你可以確保只有 API 服務具備存取資料庫的權力,而 Nginx 只能看到 API。這能有效防止攻擊者透過 Web 漏洞直接對資料庫進行掃描或攻擊。

多階段部署 (Multiple Compose Files) 的應用場景

在開發(Development)與生產(Production)環境中,配置往往不同。例如,開發環境需要掛載程式碼(Hot Reload),但生產環境則需要使用預先構建好的映像檔。

你可以建立兩個檔案: 1. docker-compose.yml (基礎配置) 2. docker-compose.override.yml (開發專用,自動覆蓋基礎配置)

當你執行 docker-compose up 時,Docker 會自動合併這兩個檔案。這讓你能夠保持基礎配置的乾淨,同時靈活地在本地開發時加入偵錯工具或額外的掛載路徑。


5. 效能與安全性最佳實踐 (Best Practices)

為了讓你的 Docker Compose 配置達到專業級水準,請遵循以下準則:

避免使用 latest 標籤

在 image: nginx:latest 中使用 latest 是非常危險的。當 Nginx 發布新版本並引入破壞性變更(Breaking Changes)時,你的服務可能會在自動更新後突然崩潰。永遠指定明確的版本號,例如 nginx:1.25-alpine。

資源限制 (Resource Limits) 的設定

如果你的伺服器同時運行多個服務,一個失控的服務(例如記憶體洩漏的 Python App)可能會耗盡主機的所有資源,導致整個系統癱瘓。你可以在 Compose 中加入資源限制:

services:
  api:
    deploy:
      resources:
        limits:
          cpus: '0.50'    # 限制最多使用 0.5 個 CPU
          memory: 512M    # 限制最多使用 512MB 記憶體

最小權限原則與 User 配置

預設情況下,容器內的進程通常以 root 身份執行。為了安全起見,建議在 Dockerfile 中建立一個非 root 用戶,並在 Compose 中透過 user: "1000:1000" 來指定執行身份,減少容器逃逸攻擊的風險。


6. Docker Compose vs. Kubernetes (K8s):你該如何選擇?

隨著技術進階,你可能會遇到另一個強大的工具:Kubernetes。那麼,在什麼樣的場景下該使用 Docker Compose,何時該轉向 K8s 呢?

以下是兩者的詳細比較:

特性 Docker Compose Kubernetes (K8s)
主要用途 單機多容器開發與簡單部署 分散式集群編排與大規模生產環境
學習曲線 低,非常容易上手 極高,需要深厚的運維知識
擴展性 (Scaling) 僅限於單一主機的擴展 支援跨多台伺服器的水平擴展
自我修復 基本的容器重啟功能 強大的自動化健康檢查與自動重啟
複雜度 低,適合小型專案、CI/CD 測試 高,適合大型微服務架構
網路管理 簡單的橋接網路 (Bridge) 複雜的 Service, Ingress, CNI 網路

總結建議: * 如果你正在進行本地開發、單機小規模部署或是自動化測試環境,Docker Compose 是你的不二之選。 * 如果你的應用需要跨伺服器擴展、具備高可用性 (High Availability) 且面臨大規模流量,則應考慮學習 Kubernetes。


7. 常見問題與疑難排解 (FAQ)

Q1: 我修改了 docker-compose.yml 後,如何讓變更生效?

A: 最安全的方法是執行 docker-compose up -d。Docker Compose 會自動偵測設定檔的變更,並僅重新建立(Recreate)那些發生變動的服務,而不會影響其他未變動的服務。

Q2: docker-compose down 會刪除我的資料庫數據嗎?

A: 這取決於你如何配置。如果你使用的是 Named Volume(如範例中的 db_data),執行 down 僅會移除容器與網路,數據會保留。但如果你使用了 -v 參數(即 docker-compose down -v),則會連同所有命名卷一起刪除,請務必小心!

Q3: 為什麼我的 API 服務啟動了,但卻連不到資料庫?

A: 這通常有兩個原因: 1. 啟動順序問題:資料庫容器雖然啟動了,但內部的資料庫引擎還在初始化。建議在程式碼中加入「重試連線」的邏輯。 2. 網路配置錯誤:請檢查兩個服務是否位於同一個 networks 區塊下。

Q4: 如何在 Docker Compose 中使用本地開發的程式碼?

A: 使用 volumes 的 Bind Mount 功能。例如:./app:/app,這會將你主機上的 ./app 目錄直接映射到容器內,你在主機修改程式碼,容器內會立即同步,實現熱更新。

Q5: 可以在 Docker Compose 中直接使用 build 指令嗎?

A: 可以。build: . 會告訴 Compose 在當前目錄尋找 Dockerfile。這對於開發階段非常方便,讓你不需要手動執行 docker build。

Q6: 為什麼我的 depends_on 沒有生效?

A: 如前所述,depends_on 只保證「容器啟動順序」,不保證「服務就緒」。如果你的應用程式在資料庫完全啟動前就嘗試連線並失敗,程式可能會直接崩潰。建議配合 healthcheck 功能使用。


結論

Docker Compose YAML 是一項極其強大的技術,它將複雜的容器化部署流程簡化為可維護、可重複且可版本化的設定檔。透過掌握 服務定義、網路隔離、磁碟掛載、環境變數管理 以及 資源限制,你能夠建立出既專業又穩定的開發與部署環境。

從單機開發到微服務架構,Docker Compose 是每一位現代開發者與 DevOps 工程師必備的利器。現在,就開始嘗試撰寫你的第一個 docker-compose.yml,將你的應用程式從手動部署的泥淖中解脫出來吧!