Docker Swarm 叢集編排
當你的容器應用程式需要跨越多台伺服器部署,並要求具備自動修復 (Self-healing)、水平擴展 (Scaling)、負載均衡 (Load Balancing) 與滾動更新 (Rolling Updates) 等能力時,你就需要叢集編排 (Orchestration) 工具。
雖然 Kubernetes (K8s) 是目前的主流,但對於中小型專案或追求簡單運維的團隊來說,Docker 內建的 Swarm Mode 是一個極佳的選擇,它簡單易用且與 Docker 指令高度整合。
為什麼選擇 Docker Swarm?
Docker Swarm 是 Docker 官方提供的叢集管理工具。它的最大優勢在於:開箱即用。只要你安裝了 Docker,就已經具備了 Swarm,無需額外安裝複雜的組件。
核心優勢:
- 極簡安裝:一條指令即可初始化叢集。
- 宣告式狀態 (Declarative Service Model):你只需定義理想狀態(例如:維持 5 個副本),Swarm 會自動確保實際運行狀態與之相符。
- 內建負載均衡:自動處理內部與外部網路流量派送。
- 安全性:預設啟用 TLS 相互認證,節點間通訊皆經過加密。
Swarm 核心架構
Swarm 叢集由多個 Nodes (節點) 組成,每個節點都是一個 Docker 主機。
節點角色
- Manager Nodes:
- 管理叢集狀態、調度任務並維護集群一致性。
- 使用 Raft 共識演算法 來同步狀態。為了維持高可用性,建議部署奇數個 (1, 3, 5) Manager 節點。
- Worker Nodes:
- 單純接收 Manager 分派的任務並執行容器。
- Manager 節點預設也具備執行任務的能力(可設定為僅限管理)。
服務與任務 (Service & Task)
- Service (服務):這是你在 Swarm 中操作的最小單位。你定義「我要一個 Nginx 服務,副本數為 3」。
- Task (任務):正在節點上執行的單個容器實例。Service 是 Blueprint,Task 是具體的實例。
實戰:建立與管理叢集
初始化叢集 (Manager)
在預計作為管理者的機器執行:
docker swarm init --advertise-addr <MANAGER_IP>
執行後會得到兩行關鍵指令:一行用於加入新的 Manager,另一行用於加入 Worker。
加入工作節點 (Worker)
在其他伺服器上貼上剛產生的 docker swarm join 指令:
docker swarm join --token <TOKEN_STR> <MANAGER_IP>:2377
查看與管理節點
在 Manager 上執行:
# 查看所有節點
docker node ls
# 查看特定節點詳細資訊
docker node inspect <NODE_ID>
節點維護與可用性
你可以手動控制節點的調度狀態:
active:正常狀態,可以接受新任務。pause:暫停狀態,現有任務繼續執行,但不接受新任務。drain:排空狀態,現有任務會被遷移到其他節點,並不接受新任務(常用於系統維護)。
# 將節點改為維護模式
docker node update --availability drain <NODE_ID>
# 恢復節點
docker node update --availability active <NODE_ID>
離開叢集 (Leave)
當節點不再需要參與叢集時:
# 在 Worker 節點執行
docker swarm leave
# 在 Manager 節點執行 (若要強制解散或離開)
docker swarm leave --force
部署與管理 Service
在 Swarm 中,我們使用 docker service 系列指令來管理個別服務。
建立服務
# 建立一個名為 web-service 的服務,啟動 3 個副本,對外開放 80 port
docker service create --name web-service --replicas 3 -p 80:80 nginx:latest
服務模式:Replicated vs Global
- Replicated (預設):根據指定的副本數在各節點分佈。
- Global:在叢集中的「每一個」合格節點上都運行一個容器(常用於監控或日誌收集工具)。
# 建立一個 global 模式的服務
docker service create --name monitor --mode global prometheus-exporter
服務維護指令
當服務上線後,你經常需要進行動態調整:
# 水平擴展 (Scaling)
docker service scale web-service=5
# 更新服務 (例如更換映像檔版本)
docker service update --image nginx:1.25 web-service
# 查看服務日誌
docker service logs -f web-service
# 刪除服務
docker service rm web-service
常用 Service 管理與除錯指令
除了上述的基本指令,運維時你經常需要檢查服務健康狀況或排查錯誤:
# 查看所有服務列表 (含副本數狀態)
docker service ls
# 查看特定服務的詳細任務 (Task) 狀態
# 這是最常用的除錯指令,可以看出哪個 Container 失敗及其錯誤訊息與所在的節點
docker service ps web-service
# 查看包含詳細錯誤訊息 (不截斷)
docker service ps --no-trunc web-service
# 查看服務詳細設定 (Pretty print)
docker service inspect --pretty web-service
# 強制重啟服務 (Force Update)
# 當服務卡住或需要強制重跑時使用
docker service update --force web-service
# 服務回滾 (Rollback)
# 當新部署的版本爛掉時,快速回到上一個版本
docker service rollback web-service
如何進入 Service/Stack 容器 (Exec)
這是初學者最常遇到的問題:「我怎麼 exec 進去 Swarm 的服務?」
首先要釐清觀念:docker exec 是針對 Container (容器) 指令,而不是 Service。Service 只是抽象層。所以你需要先找到具體的 Container ID 及其所在的機器。
步驟一:找出 Container 在哪一台機器
使用 docker service ps 查詢任務分佈:
$ docker service ps web-service
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
x9d8... web-service.1 nginx:latest node-1 Running Running 5 minutes ago
從上面的輸出來看,web-service.1 這個任務正在 node-1 這個節點上運作。
步驟二:進入該節點的操作環境
- 如果 Container 剛好在本機 (Manager):直接跳步驟三。
- 如果 Container 在遠端 Worker 節點:你需要先 SSH 連線到該伺服器 (
ssh user@node-1)。
步驟三:執行 Exec
在該節點上,先用 docker ps 找到對應的容器 ID,再進入:
# 1. 搜尋容器 ID (過濾名字通常比較快)
docker ps | grep web-service
# 2. 進入容器
docker exec -it <CONTAINER_ID> bash
# 若映像檔沒有 bash,改用 sh
docker exec -it <CONTAINER_ID> sh
Swarm 網路與路由 (Routing Mesh)
Swarm 使用 Overlay Network 來實現跨主機的容器通訊。
Overlay 網路
建立一個可以跨越多台伺服器的網路:
docker network create --driver overlay my-net
將服務加入此網路後,容器間可以透過 Service Name 互相訪問,Swarm 會自動處理 VIP (Virtual IP) 的解析與負載均衡。
Ingress Routing Mesh (路由網格)
這是 Swarm 最神奇也最強大的功能。這解決了一個核心問題:「在叢集中,Container 可能隨時會在任何節點上漂移,那外部流量到底要連去哪裡?」
在傳統架構中,如果 Nginx 跑在 Node A,你就必須連去 Node A 的 IP。但在 Swarm 中,你可以訪問叢集中 「任何一個節點」 的 IP,Swarm 都會自動把流量轉送到正確的 Container 身上,即使該 Container 根本不在你訪問的那個節點上。
運作原理 (The Flow)
當你對外開放一個 Port (例如 -p 80:80) 時,Swarm 會在每一個節點上都監聽這個 80 Port。
- Client Request:使用者訪問叢集中的 任意節點 (Node A) 的 80 Port。
- Ingress Network:如果該節點上剛好有跑這個服務的容器,直接處理。如果沒有,Swarm 的 Ingress Overlay Network 會介入。
- IPVS Load Balancing:Swarm 內部的 Load Balancer (IPVS) 會將請求轉發到真正的 Service VIP (虛擬 IP)。
- Forwarding:流量透過 Overlay Network 被隧道傳輸到真正運行該容器的 節點 (Node B)。
- Container:最終由 Node B 上的容器接收並處理請求。
限制與注意事項
雖然方便,但 Routing Mesh 也有一個知名的限制:Source IP NAT。
當流量經過 Routing Mesh 轉發後,容器看到的 來源 IP (Source IP) 會變成 Ingress Network 的內部 IP,而不是使用者的真實 IP。如果你需要紀錄使用者真實 IP (例如 Access Log),你需要:
- 使用 host mode (
mode: host) 發布端口(這會繞過 Routing Mesh,但你必須確保該節點真的有跑該容器)。 - 或者在最外層的 Load Balancer (例如 Nginx Proxy) 使用
X-Forwarded-For標頭傳遞真實 IP。
安全管理:Secrets 與 Configs
在生產環境中,你不應該把密碼或配置寫死在映像檔或環境變數中。
Docker Secrets
用於存取機密資訊(如資料庫密碼、SSL 證書)。Secret 會加密儲存在 Manager 節點中,並僅在任務啟動時掛載給特定的容器。
如何使用 Secrets?
- 掛載路徑:Secrets 預設會被掛載到容器內的
/run/secrets/<secret_name>檔案中(這是一個內存文件系統,不會寫入硬碟)。 - 存取內容:應用程式可以直接讀取該檔案來取得密碼。
- 環境變數模式:許多官方映像檔(如 MySQL、Postgres)支援
_FILE結尾的環境變數,讓你指定 Secret 檔案的路徑。
# 建立一個名為 db_password 的 secret
echo "my_secure_password" | docker secret create db_password -
# 在服務中使用該 secret,並告知 MySQL 從該路徑讀取密碼
docker service create \
--name db \
--secret db_password \
-e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_password \
mysql
Docker Configs
類似於 Secrets,但用於非加密的設定檔(如 nginx.conf 或 prometheus.yml),讓你可以動態更新配置而無需重新建置映像檔。
如何使用 Configs?
- 掛載路徑:預設也是掛載到容器根目錄下的某處,但通常我們會自訂掛載位置。
- 動態更新:當你更新 Config 時,Swarm 會重新啟動關聯的 Service 以套用新設定。
# 1. 建立一個 config (從本地 nginx.conf 檔案)
docker config create my_nginx_config ./nginx.conf
# 2. 建立服務,並將 config 掛載到 Nginx 預設讀取的地方
docker service create \
--name web \
--config source=my_nginx_config,target=/etc/nginx/nginx.conf \
-p 80:80 \
nginx
大規模部署:Docker Stack (堆疊)
到目前為止,我們都是用 docker service create 一個一個建立服務。但在真實的生產環境中,一個應用程式通常包含多個關聯服務(例如:Frontend + Backend + Redis + DB)。
這時候,我們需要 Docker Stack。
Stack vs Service:有什麼不同?
- Service:是 Swarm 的最小單位,就像是「手動單點」一道菜。適合測試或簡單操作。
- Stack:是 Service 的集合,就像是「宣告式套餐」。你寫好一張
docker-compose.yaml菜單,Swarm 就會自動幫你把整桌菜(所有服務、網路、Volume、Config)一次準備好。
docker stack deploy,而不是手動敲 docker service create。核心觀念:Zero Downtime (零停機) 更新
Swarm 最強大的賣點就是「更新服務時,使用者完全無感」。這背後依賴兩個關鍵機制:Rolling Update (滾動更新) 與 Health Check (健康檢查)。
想像你要把服務從 v1 更新到 v2:
- Rolling Update:Swarm 不會一次殺掉所有舊容器。它會分批(例如一次 2 個)處理。
- Health Check:這是紅綠燈。Swarm 啟動新容器後,會等待它通過「健康檢查」。只有新容器亮綠燈了,Swarm 才會真正把流量導過去,並開始關閉舊容器。
如果沒有設定 Health Check 會怎樣? Swarm 會以為「容器啟動」就等於「服務好了」。但實際上你的 Java/Node.js 可能還在初始化。結果流量進來全部報錯 (502 Bad Gateway)。所以 Health Check 是零停機的絕對關鍵!
實戰:生產級 docker-compose.yaml 範例
這是一個包含所有進階觀念(資源限制、滾動更新策略、節點限制、Secrets)的完整範例:
version: '3.8'
services:
web-app:
image: my-app:v2.0
# 資源限制 (Resource Limits)
# 這是生產環境必備!避免某個容器吃光整台機器的 CPU/RAM 導致當機。
deploy:
replicas: 5 # 副本數:維持 5 個容器在跑
# 資源預留與限制
resources:
limits:
cpus: '0.50' # 最多只能用 50% CPU
memory: 512M # 最多只能用 512MB RAM
reservations:
cpus: '0.10' # 保證最少有 10% CPU 可用
memory: 128M # 保證最少有 128MB RAM 可用
# 滾動更新策略 (Update Config)
update_config:
parallelism: 2 # 每次更新 2 個容器
delay: 10s # 每一批更新完,休息 10 秒再做下一批
order: start-first # [關鍵] 先啟動新的,確認成功後再殺舊的 (達成零停機)
failure_action: rollback # 如果更新失敗,自動回滾到上個版本
# 錯誤重啟策略 (Restart Policy)
restart_policy:
condition: on-failure # 只有非正常退出時才重啟
delay: 5s # 重啟前等待 5 秒
max_attempts: 3 # 最多重試 3 次
# 部署位置限制 (Placement Constraints)
# 例如:只把此服務部署在標籤為 "region=asia" 的 Worker 節點上
placement:
constraints:
- 'node.role == worker'
# - "node.labels.region == asia"
# 健康檢查 (Health Check) - 零停機的核心!
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 30s # 每 30 秒檢查一次
timeout: 10s # 超過 10 秒沒回應算失敗
retries: 3 # 連續失敗 3 次才判定為不健康 (Unhealthy)
start_period: 40s # 給予 40 秒的寬限期讓程式初始化 (這段期間失敗不算數)
# 網路設定
networks:
- app-net
# 綁定 Secrets (敏感資料) 與 Configs (設定檔)
secrets:
- db_password
configs:
- source: my_nginx_conf
target: /etc/nginx/conf.d/default.conf
ports:
- target: 80 # 容器內部的埠口
published: 3000 # 對外公開(宿主機)的埠口
protocol: tcp # 協議 (tcp 或 udp)
mode: ingress # 模式 (ingress 或 host)
# 定義網路
networks:
app-net:
driver: overlay # Swarm 必須使用 overlay 網路才能跨主機通訊
# 定義 Secrets (需先在 Manager 建立:echo "12345" | docker secret create db_password -)
secrets:
db_password:
# 這 secret 已經事先建立好了,請直接引用,不要試圖建立它
external: true
# 定義 Configs
configs:
my_nginx_conf:
file: ./nginx.conf
Stack 管理常用指令
一旦寫好了 yaml 檔,管理就變得非常簡單:
# 1. 部署或更新 Stack (宣告式)
# 如果 Stack 不存在則建立;如果已存在,Swarm 會自動比對差異並進行更新 (Rolling Update)
docker stack deploy -c docker-compose.yaml my-stack-name
# 2. 查看所有 Stacks
docker stack ls
# 3. 查看 Stack 裡面有哪些服務
docker stack services my-stack-name
# 4. 查看 Stack 的具體任務 (在哪個節點跑、狀態如何)
docker stack ps my-stack-name
# 5. 移除整個 Stack (危險操作:會移除所有服務與網路)
docker stack rm my-stack-name
如何更新 Stack? (Day 2 Operations)
許多人會問:「我有新的 Docker Image 或是改了設定,怎麼更新?」
答案很簡單:只要再次執行 docker stack deploy 指令即可。
- 修改你的
docker-compose.yaml(例如將 image tag 改為v2.1)。 - 再次執行
docker stack deploy -c docker-compose.yaml my-stack-name。 - Swarm 會自動偵測差異,並依照你設定的
update_config(滾動更新策略) 來優雅地更新服務。
Swarm vs. Kubernetes (K8s)
| 特性 | Docker Swarm | Kubernetes (K8s) |
|---|---|---|
| 安裝與學習 | 極低,與 Docker 完全一致 | 高,有大量的專有名詞與組件 |
| 功能性 | 基本編排功能紮實,適合 90% 場景 | 極其強大,適合極大規模微服務 |
| 生態系統 | 較小 | 巨大 (CNCF 生態圈) |
| 跨雲支援 | 需手動管理各雲端節點 | 各大雲端皆有 Managed Service (EKS, GKE, AKS) |
總結
Docker Swarm 是進入容器編排世界的最佳起點。它對於中小型企業或需要快速部署的場景來說,提供了極高的投資報酬率。如果你已經熟悉 Docker Compose,轉移到 Docker Swarm 只需幾分鐘的時間,卻能讓你的系統具備真正的運維彈性。