Docker Compose

如果把 Docker Container 比喻為「樂器」,那麼 Docker Compose 就是「樂團指揮」。

  • 沒有 Docker Compose 時:如果你的網站需要網頁伺服器(如 Nginx)、後端程式(如 Node.js/Python)跟資料庫(如 MySQL),你必須手動敲好幾行又長又複雜的 docker run 指令,還要手動幫它們建立網路、掛載資料夾。
  • 有了 Docker Compose 後:你只需要寫一份名為 docker-compose.yml 的設定檔,把所有容器的規格寫清楚,接著只需要執行 一行指令,就能同時啟動或關閉所有服務!

核心概念:一個檔案、兩個指令

學習 Docker Compose,只要掌握以下核心架構:

  1. docker-compose.yml 設定檔:用 YAML 格式編寫,用來定義有哪些容器(Services)、網路(Networks)與資料卷(Volumes)。
  2. docker compose up:一鍵打包啟動所有容器。
  3. docker compose down:一鍵停止並清除所有容器。

快速實戰:建立第一個 Compose 專案

以下建立一個極簡的範例:用 Docker Compose 一起啟動 Nginx 網頁伺服器 與 Redis 快取資料庫。

第一步:建立專案資料夾

在終端機(Terminal)或 Command Prompt 建立一個新資料夾並進入:

mkdir my-compose-demo
cd my-compose-demo

第二步:建立 docker-compose.yml

在該資料夾內建立一個名為 docker-compose.yml 的檔案,貼入以下內容:

version: '3.8'

services:
  webserver:
    image: nginx:alpine
    container_name: my-web-server
    ports:
      - "8080:80"
    restart: always

  cache:
    image: redis:alpine
    container_name: my-redis-cache
    restart: always

關鍵欄位解析:

  • version:定義 Docker Compose 的語法版本(一般維持 '3.8' 即可)。
  • services:底下定義你要啟動的各個容器。
    • webserver: 服務 1:網頁伺服器
    • cache:服務 2:Redis 資料庫
  • image:指定使用的 Docker Image 映像檔名稱(這裡選用輕量化的 alpine 版本)。
  • ports:連接埠對映,格式為 "主機 Port : 容器內 Port"。這裡代表存取電腦的 8080 Port 會對應到 Nginx 的 80 Port。
  • restart: always:設定當容器意外崩潰時會自動重啟。

第三步:啟動服務

docker-compose.yml 所在的資料夾中,執行以下指令:

docker compose up -d

小技巧-d 代表在背景執行(Detached mode),這樣終端機就不會被 Log 訊息佔用。

打開瀏覽器輸入 http://localhost:8080,如果你看到 Nginx 的 “Welcome to nginx!” 畫面,就代表服務成功啟動了!


常用指令

學會這幾行指令,就能應付 90% 的日常操作:

1.背景啟動所有服務:docker compose up -d

根據 docker-compose.yml 的設定,下載映像檔並在背景啟動所有容器。

2.查看運作中的服務:docker compose ps

列出目前由 Compose 管理的所有容器狀態、埠號對映與運作情形。

3.即時查看容器 Log:docker compose logs -f

持續印出所有容器的日誌紀錄。若只想看單一服務,可在後方指定名稱,例如 docker compose logs -f webserver

4.停止並刪除容器:docker compose down

安全停止所有容器,並釋放它們所佔用的網路環境。


注意事項

1. YAML 縮排格式要求極為嚴格

YAML 檔案嚴禁使用 Tab 鍵,只能使用**空白鍵(Space)**進行縮排!如果不小心混用,Docker Compose 讀取時會直接報錯。

2. 容器間的通訊極度簡單

在同一份 docker-compose.yml 底下的容器,預設會自動連到同一個虛擬網路中。

重點: 容器之間要互相連線時,主機名稱直接寫「服務名稱(Service Name)」即可!例如後端程式要連 Redis,連線地址直接填 cache:6379,不需要寫 IP。

3. 資料持久化(Volumes)

預設情況下,當容器被 docker compose down 刪除時,容器裡產生的新資料也會跟著消失。如果需要保留資料(如資料庫檔案),記得加上 volumes 設定將資料對映回實體電腦中。


常用關鍵屬性語法解析

在寫 docker-compose.yml 時,你會經常看到以下 10 個核心屬性:

1. environment (環境變數)

用來注入變數到容器內,常見於設定資料庫帳密、系統時區或 API 金鑰。

environment:
  POSTGRES_USER: root
  POSTGRES_PASSWORD: mysecretpassword

2. volumes (資料持久化與目錄對映)

容器刪除後內部的資料也會跟著消失。volumes 能把主機(Host)的資料夾對映進容器內,確保資料保留下來或用來同步程式碼。

以下是將主機當前的html 對映到容器內/usr/share/nginx/html

volumes:
  - ./html:/usr/share/nginx/html  

3. build (自訂 Image 編譯)

當官方沒有現成的映像檔,需要透過本地的 Dockerfile 自己建立 Image 時使用。

以下用 . 表示 Dockerfile 在當前所在目錄路徑

build: . 

4. depends_on (啟動順序控制)

指定容器間的啟動順序。以下表示必須先啟動db資料庫,才能啟動該服務。

depends_on:
  - db  

5. network_mode (網路模式)

改變容器的網路連接架構。常見的值有 bridge(預設虛擬網路)與 host(直接共用主機網路)。

network_mode: "host"  

6. dns (自訂 DNS 伺服器)

指定容器內部的 DNS 解析伺服器。若預設 DNS 無法解析外網,可自訂 Google (8.8.8.8) 或 Cloudflare (1.1.1.1)。

dns:
  - 8.8.8.8

7. cap_add (新增 Linux 系統權限)

Docker 預設會封鎖許多敏感的權限,透過 cap_add 可以精準解鎖特定系統權限(例如網路封包修改)。 以下是開發與營運中最常看到的幾個選項:

選項名稱允許容器做的事常見使用情境
NET_ADMIN修改網路介面、防火牆規則(iptables)、路由表VPN 用戶端/伺服器、流量監控工具
NET_RAW發送原始封包(Raw Sockets)在容器內執行 ping 指令、Wireshark 抓包
SYS_PTRACE追蹤與偵錯其他程序(Process)使用 gdb 除錯、效能分析工具(如 strace
SYS_TIME修改系統的時鐘與時間NTP 時間同步服務
SYS_ADMIN執行各種系統管理操作(接近半個 root)掛載檔案系統(mount)、容器內執行 FUSE 檔案系統
DAC_READ_SEARCH繞過檔案權限檢查直接讀取所有檔案檔案備份工具、系統安全掃描器
cap_add:
  - NET_ADMIN  

8. privileged (最高特權模式)

將容器賦予接近實體主機 Root 的最高權限。常用於 Docker-in-Docker 或存取實體硬體設備

privileged: true 

注意事項:開啟 privileged: true 會大幅增加容器逃逸的風險,非特殊需求切勿在生產環境開啟!

9. entrypoint (覆蓋預設啟動指令)

強制覆蓋容器啟動時要執行的第一個核心指令。

entrypoint: ["python", "app.py"]  

10. sysctls (修改 Linux 核心參數)

用來調整容器內部的 Linux Kernel 參數,常見於高併發效能調優。以下主要提高最大網路連線佇列數

sysctls:
  net.core.somaxconn: 1024 

實戰範例:完整 docker-compose.yml

我們將上述 10 個屬性整合進一個包含 Python Web、Redis Cache與 Network Agent 的架構中:

version: '3.8'

services:
  # 1. Python Web
  web:
    build: .                           
    container_name: my-web-app
    ports:
      - "5000:5000"
    environment:                       
      - PORT=5000
      - REDIS_HOST=cache
    volumes:                           
      - .:/app
    depends_on:                         
      - cache
    entrypoint: ["python", "app.py"]   
    sysctls:                           
      net.core.somaxconn: 1024

  # 2. Redis Cache 
  cache:
    image: redis:7-alpine
    container_name: my-redis
    dns:                               
      - 8.8.8.8
      - 1.1.1.1

  # 3. Network Agent 
  net-tools:
    image: alpine
    container_name: my-net-tools
    network_mode: "host"               
    cap_add:                           
      - NET_ADMIN
    # privileged: true                 
    command: sleep infinity