在現代軟體開發與維運實務中,將命令行工具容器化是提升環境一致性與管理效率的關鍵策略。本文從 Docker 容器執行 Terraform 的流程切入,說明如何藉由容器封裝基礎設施即代碼(IaC)工具,達成環境標準化與版本彈性。然而,當應用架構走向多服務協作時,單一容器的管理模式便顯不足。因此,文章將焦點轉向 Docker Compose,深入探討其作為多容器應用協調工具的核心概念,解釋如何透過宣告式 YAML 配置文件,定義服務、網路與儲存卷之間的依賴關係。透過具體部署案例,呈現 Docker Compose 在簡化服務通訊與生命週期管理方面的實踐價值。

Docker 運行 Terraform 與 Docker Compose 基礎

本節將延續 Docker 運行命令行工具的討論,完成 Terraform 的執行流程,並介紹 Docker Compose 的基本概念與用途,為後續的多容器應用部署奠定基礎。

完成 Terraform 執行流程

在 Docker 容器內運行 Terraform 的過程中,繼 initplan 命令之後,我們需要執行 apply 命令來實際創建或修改基礎設施。

  • 執行 terraform apply:
    docker run -i -t -v ${PWD}:/usr/tf -w /usr/tf \
      --env ARM_CLIENT_ID="<azure_client_id>" \
      --env ARM_CLIENT_SECRET="<azure_client_secret>" \
      --env ARM_SUBSCRIPTION_ID="<azure_subscription_id>" \
      --env ARM_TENANT_ID="<azure_tenant_id>" \
      hashicorp/terraform:latest apply plan.tfplan
    
    這個命令使用之前 plan 命令生成的 plan.tfplan 文件來執行變更。這確保了 Terraform 將應用先前預覽過的確切變更。

使用 Docker 運行命令行工具的優勢

將命令行工具(如 Terraform)運行在 Docker 容器內,提供了顯著的優勢:

  • 簡化本地環境配置: 無需在本地機器上安裝和配置這些工具及其複雜的依賴項。Docker 映像已經包含了所有必需的組件,實現了環境的隔離和標準化。
  • 版本管理靈活性: 可以輕鬆地在本地運行同一工具的不同版本,而不會產生衝突。只需拉取並運行對應版本的 Docker 映像即可,這對於測試新版本或維護與特定版本兼容的項目至關重要。

Docker Compose 介紹

在之前的章節中,我們學習了如何單獨構建和運行 Docker 映像。然而,現代應用程式通常不是獨立運行的,它們需要與其他服務(如數據庫、API 服務)協同工作。這就引入了多容器部署的需求。

Docker Compose 是一個用於定義和運行多容器 Docker 應用程式的工具。它允許您使用一個 YAML 文件來配置應用程式的所有服務、網絡和卷。通過一個簡單的命令,您可以啟動、停止和管理應用程式的所有組件。

Docker Compose 的核心概念:

  • 服務 (Services): 在 Docker Compose 文件中,每個服務代表一個容器。您可以定義多個服務,例如一個 Web 應用服務、一個數據庫服務等。
  • 映像 (Images): 每個服務都基於一個 Docker 映像構建。
  • 卷 (Volumes): 用於持久化容器數據,確保數據不會隨著容器的銷毀而丟失。
  • 網絡 (Networks): 允許容器之間相互通信,並與外部環境進行交互。

使用 Docker Compose,您可以將一個複雜的應用程式(例如,一個 Web 應用程式與一個 MySQL 數據庫)定義為一個單一的部署單元,極大地簡化了多容器應用的管理和部署流程。

Docker Compose 安裝

  • Windows 和 macOS: Docker Compose 通常已預裝在 Docker Desktop 中。
  • Linux: 在 Linux 上,您可能需要按照官方文檔進行單獨安裝。

基本 Docker Compose 配置示例

我們將以一個常見的場景為例:運行一個 Nginx Web 服務器,並將其連接到一個 MySQL 數據庫。

  1. 創建 docker-compose.yml 文件: 在項目目錄中創建一個名為 docker-compose.yml 的文件,並使用 YAML 語法定義服務。

    version: '3.8' # 指定 Docker Compose 文件格式版本
    
    services:
      # Nginx Web 服務
      web:
        image: nginx:latest # 使用官方 Nginx 映像
        ports:
          - "80:80" # 將主機的 80 端口映射到容器的 80 端口
        volumes:
          - ./html:/usr/share/nginx/html # 掛載本地 html 目錄到 Nginx 的 Web 根目錄
        depends_on:
          - db # 確保數據庫服務先啟動
    
      # MySQL 數據庫服務
      db:
        image: mysql:5.7 # 使用官方 MySQL 5.7 映像
        environment:
          MYSQL_ROOT_PASSWORD: your_strong_password # 設定 MySQL root 用戶密碼
          MYSQL_DATABASE: myappdb # 設定數據庫名稱
        volumes:
          - db_data:/var/lib/mysql # 使用命名卷持久化數據庫數據
    
    # 定義命名卷
    volumes:
      db_data:
    
    • services: 定義了應用程式的各個組件(服務)。
    • web 服務: 使用 Nginx 映像,暴露端口 80,並將本地的 html 文件夾掛載到 Nginx 的 Web 根目錄。depends_on 指示 Nginx 服務依賴於 db 服務。
    • db 服務: 使用 MySQL 5.7 映像,並通過 environment 變量設置了數據庫名稱和 root 密碼。volumes 指示使用一個名為 db_data 的命名卷來持久化數據。
    • volumes: 在頂層定義了命名卷 db_data

執行 Docker Compose 配置

在保存 docker-compose.yml 文件後,您可以在終端中執行以下命令來啟動應用程式:

docker-compose up -d
  • up: 啟動並運行服務。
  • -d: 在後台模式(detached mode)下運行容器。

執行此命令後,Docker Compose 將會自動拉取所需的映像(Nginx 和 MySQL),創建網絡,創建卷,然後啟動所有定義的服務。您可以在瀏覽器中訪問 http://localhost 來查看 Nginx 服務的運行情況(如果您的本地 html 目錄中有相應的 index.html 文件)。

Docker Compose 實戰:Nginx 與 MySQL 部署

本節將深入探討如何利用 Docker Compose 編寫配置文件,並實際運行一個包含 Nginx Web 服務器和 MySQL 數據庫的多容器應用程式。

Docker Compose 配置文件編寫

在開始之前,首先確認 Docker Compose 已正確安裝。可以通過運行 docker-compose version 命令來驗證。

接下來,我們將創建一個名為 docker-compose.yml 的文件,其中定義了我們應用程式的服務。

  1. Nginx 服務配置:

    version: '3' # 指定 Docker Compose 文件格式版本
    
    services:
      nginx:
        image: nginx:latest # 使用最新的官方 Nginx 映像
        container_name: nginx-container # 為容器指定一個易於識別的名稱
        ports:
          - "8080:80" # 將主機的 8080 端口映射到容器的 80 端口
    
    • services: 這是 Docker Compose 文件的主鍵,用於定義應用程式中的各個服務。
    • nginx: 這是一個服務名稱,代表我們的 Nginx 服務。
    • image: nginx:latest: 指定使用最新的官方 Nginx Docker 映像。
    • container_name: nginx-container: 為此 Nginx 容器指定一個固定的名稱,方便管理和識別。
    • ports: - "8080:80": 這是端口映射。它將主機(您的電腦)的 8080 端口映射到 Nginx 容器內部的 80 端口。這意味著您可以通過訪問主機的 localhost:8080 來訪問 Nginx 服務。
  2. MySQL 服務配置: 在同一個 docker-compose.yml 文件中,我們添加 MySQL 服務的配置:

      mysql:
        image: mysql:5.7 # 使用 MySQL 5.7 版本映像
        container_name: mysql-container # 為 MySQL 容器指定名稱
        environment:
          MYSQL_ROOT_PASSWORD: secret # 設定 MySQL root 用戶的密碼
          MYSQL_DATABASE: mydb # 設定要創建的數據庫名稱
          MYSQL_USER: myuser # 設定一個新的數據庫用戶名
          MYSQL_PASSWORD: password # 設定新用戶的密碼
    
    • mysql: 這是 MySQL 服務的名稱。
    • image: mysql:5.7: 指定使用 MySQL 5.7 版本映像。選擇特定版本有助於確保環境的一致性。
    • container_name: mysql-container: 為 MySQL 容器指定一個名稱。
    • environment: 這是一個關鍵部分,用於設置 MySQL 容器啟動時所需的環境變量。
      • MYSQL_ROOT_PASSWORD: 設定了 root 用戶的密碼。
      • MYSQL_DATABASE: 在啟動時自動創建一個名為 mydb 的數據庫。
      • MYSQL_USERMYSQL_PASSWORD: 設定了一個新的數據庫用戶 myuser 及其密碼,用於訪問 mydb 數據庫。

執行 Docker Compose

編寫好 docker-compose.yml 文件後,我們就可以使用 Docker Compose 命令來啟動這些容器了。

  1. 啟動容器: 在終端中,導航到包含 docker-compose.yml 文件的目錄,然後執行以下命令:

    docker-compose up -d
    
    • docker-compose up: 這個命令會讀取當前目錄下的 docker-compose.yml 文件,並根據其中的定義創建和啟動所有服務的容器。
    • -d (detached mode): 這個選項讓容器在後台運行,不會阻塞終端。

    執行此命令後,Docker Compose 會自動完成以下操作:

    • 拉取 nginx:latestmysql:5.7 映像(如果本地不存在)。
    • 創建一個 Docker 網絡,讓 nginxmysql 服務能夠相互通信。
    • 創建並啟動 nginx-containermysql-container 這兩個容器。
    • 為 MySQL 容器配置數據庫、用戶和密碼。

視覺化 Docker Compose 服務架構

以下圖示展示了通過 docker-compose.yml 文件定義的 Nginx 和 MySQL 服務之間的關係。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title Docker Compose Multi-Container Setup

component "Docker Host Machine" {
  artifact "docker-compose.yml" as COMPOSE_FILE
  artifact "Nginx Container\n(nginx-container)" as NGINX_CONT
  artifact "MySQL Container\n(mysql-container)" as MYSQL_CONT
  artifact "Docker Network\n(e.g., projectname_default)" as DOCKER_NET
  artifact "Volume\n(e.g., db_data)" as DB_VOLUME
}

component "External Services" {
  artifact "Nginx Image" as NGINX_IMG
  artifact "MySQL Image" as MYSQL_IMG
}

COMPOSE_FILE --> NGINX_CONT : Defines Nginx Service
COMPOSE_FILE --> MYSQL_CONT : Defines MySQL Service
COMPOSE_FILE --> DOCKER_NET : Defines Network
COMPOSE_FILE --> DB_VOLUME : Defines Volume

NGINX_CONT --> NGINX_IMG : Uses Image
MYSQL_CONT --> MYSQL_IMG : Uses Image
MYSQL_CONT --> DB_VOLUME : Persists Data

NGINX_CONT -- DOCKER_NET : Connects to Network
MYSQL_CONT -- DOCKER_NET : Connects to Network

NGINX_CONT --> MYSQL_CONT : Communicates via Network\n(using service name 'mysql')

NGINX_CONT : Exposes Port 8080 (Host)\n to Port 80 (Container)

stop

@enduml

看圖說話:

此圖示描繪了 Docker Compose 如何協調多個容器構成一個應用程式。核心是「Docker Host Machine」上的 docker-compose.yml 文件,它定義了「Nginx Container (nginx-container)」和「MySQL Container (mysql-container)」兩個服務。

每個容器都基於相應的 Docker 映像(「Nginx Image」和「MySQL Image」)構建。Docker Compose 會自動創建一個「Docker Network」,讓這兩個容器能夠在內部進行通信。Nginx 容器通過主機的 8080 端口暴露服務,而 MySQL 容器則使用一個「Volume (db_data)」來持久化其數據。

關鍵在於,Nginx 容器可以通過服務名稱 mysql(在 docker-compose.yml 文件中定義)直接訪問 MySQL 容器,而無需知道其 IP 地址。這極大地簡化了服務之間的通信配置。

縱觀現代應用程式開發與維運的複雜性演進,從單一工具的容器化到多容器應用的系統性編排,標示了一次關鍵的思維躍遷。將 Terraform 這類命令行工具置於 Docker 中運行,是解決環境依賴與版本衝突的卓越起點,實現了開發環境的標準化與隔離,屬於一種高效的資源最佳化實踐。

然而,Docker Compose 的引入,則將我們的視角從單一「元件效能」提升至「系統架構」的層次。其核心價值不僅在於簡化多容器啟動的指令,更在於提供了一種宣告式的語言來定義服務間的依賴關係、網絡通訊與數據持久化策略。此舉將原本隱晦、需要透過指令式腳本維護的系統拓撲,轉化為清晰、可版本控制的組態文件。真正的挑戰與瓶頸,並非學習 YAML 語法,而是促使開發與維運團隊從關注獨立服務,轉向建立系統性思考的心智模式。

展望未來,這種以 Docker Compose 為代表的宣告式編排方法,將進一步模糊本地開發、持續整合與生產環境的界線,形成一個高度一致的開發維運生態系。服務間的互動關係在開發初期即被明確定義,大幅降低了後期整合的風險與溝通成本。

對於追求高效能交付的技術領導者而言,引導團隊掌握從單一工具容器化到多容器應用編排的思維躍遷,不僅是技術能力的升級,更是提升團隊整體協作效率與交付品質的關鍵槓桿。