在現代雲端原生應用程式開發的浪潮中,容器編排技術已經成為建構彈性、可擴展系統架構的核心基石。Docker Swarm 作為 Docker 原生的叢集管理與編排解決方案,以其簡潔的設計理念與強大的功能特性,為開發團隊提供了一套完整的容器化應用程式管理機制。相較於其他複雜的編排工具,Docker Swarm 透過宣告式的管理方式,讓技術團隊能夠在分散式環境中輕鬆部署、更新與擴展容器化應用程式,同時確保服務的高可用性、負載平衡與故障自動恢復能力。
本文將深入探討 Docker Swarm 服務生命週期管理的各個層面,從基礎的環境變數調整、映像檔版本更新,到進階的資源配置最佳化、服務擴展策略規劃,以及健康檢查與自動修復機制的設計。我們將透過豐富的實際操作範例與詳盡的程式碼註解,協助讀者建立完整的容器編排工作流程,掌握企業級維運所需的核心技能。無論您是剛接觸容器編排的開發者,還是希望深化 Docker Swarm 應用知識的維運工程師,本文都將提供實用的技術洞察與實戰經驗。
Docker Swarm 服務更新機制的架構設計
Docker Swarm 的服務更新機制建立在宣告式管理(Declarative Management)的設計哲學之上。在這種設計模式下,管理者不需要明確指示系統應該執行哪些具體步驟,而是描述期望達到的目標狀態,由 Swarm 協調器自動計算並執行達成目標所需的操作序列。這種方式大幅降低了系統管理的複雜度,同時提供了更好的可靠性與一致性保證。
當管理者透過 docker service update 命令對服務進行修改時,這個操作會被轉換為對服務規格(Service Specification)的變更請求。Swarm Manager 節點上的 API Server 接收到這個請求後,會更新儲存在 Raft 共識資料庫中的服務目標狀態。接著,協調器(Orchestrator)元件會持續監控當前實際狀態與目標狀態之間的差異,並透過狀態調和(State Reconciliation)程序逐步將系統調整至目標狀態。這個過程稱為控制迴路(Control Loop),是確保系統自我修復與維持期望狀態的核心機制。
在狀態調和的過程中,排程器(Scheduler)元件扮演著關鍵角色。它負責決定新的任務應該在哪些節點上執行,考慮的因素包括節點的可用資源、現有的約束條件、放置偏好設定,以及負載平衡策略。排程決策完成後,Manager 節點會將任務指派給對應的 Worker 節點,由 Worker 節點上的 Agent 負責實際的容器生命週期管理,包括拉取映像檔、建立容器、啟動程序,以及監控運作狀態。
@startuml
!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
actor "系統管理者" as Admin
participant "API Server" as API
participant "Raft 共識層" as Raft
participant "協調器" as Orchestrator
participant "排程器" as Scheduler
participant "Worker Node" as Worker
Admin -> API : 發送更新指令
API -> Raft : 更新目標狀態
Raft -> Orchestrator : 觸發狀態調和
Orchestrator -> Scheduler : 計算任務分配
Scheduler -> Worker : 派發更新任務
Worker -> Worker : 執行容器更新
Worker -> Orchestrator : 回報任務狀態
Orchestrator -> Orchestrator : 驗證達成目標
@enduml這個架構設計確保了服務更新過程的可靠性與一致性。即使在更新過程中發生節點故障或網路分區,Swarm 的協調器也能夠偵測到狀態偏離,並自動採取修正措施。這種自我修復能力是容器編排系統的重要特性,讓維運團隊能夠專注於業務邏輯的開發與最佳化,而不需要過度擔心基礎設施層的穩定性問題。
環境變數的動態管理與最佳實踐
環境變數是容器化應用程式組態管理的基礎機制之一。在傳統的部署模式中,應用程式的組態通常固化在設定檔案中,需要修改組態時必須重新建置映像檔或重新部署容器。Docker Swarm 則提供了更加靈活的環境變數管理方式,允許在服務執行期間動態調整環境變數設定,無需重新建立服務定義或重新建置映像檔。
當使用 docker service update 命令搭配 --env-add 參數新增環境變數時,Swarm 會觸發該服務的滾動更新程序。這意味著協調器會依序重啟服務的各個副本,讓新的環境變數設定在容器啟動時生效。在滾動更新過程中,Swarm 會確保同時保持足夠數量的健康副本來維持服務可用性,避免造成服務中斷。這種機制特別適合需要頻繁調整組態參數的應用場景,例如調整資料庫連線池大小、修改快取逾時時間,或是啟用特定的功能開關。
以下範例展示如何為一個 MySQL 資料庫服務新增必要的環境變數。在實際的企業環境中,這些環境變數可能來自於組態管理系統或密碼管理服務,並透過自動化腳本動態注入到服務中:
# 為 MySQL 服務新增資料庫初始化所需的環境變數
# 這個命令會同時新增多個環境變數,並觸發滾動更新
docker service update \
--env-add 'MYSQL_DATABASE=production_db' \
--env-add 'MYSQL_USER=app_user' \
--env-add 'MYSQL_PASSWORD=secure_password_123' \
--env-add 'MYSQL_ROOT_PASSWORD=root_secure_456' \
mysql
# 說明:
# MYSQL_DATABASE:指定容器啟動時自動建立的資料庫名稱
# MYSQL_USER:建立一個具有該資料庫完整權限的使用者帳號
# MYSQL_PASSWORD:設定該使用者的登入密碼
# MYSQL_ROOT_PASSWORD:設定 MySQL root 帳號的密碼
#
# 執行此命令後,Swarm 會按照更新策略(預設為一次更新一個副本)
# 依序重啟服務的所有副本,每個新啟動的容器都會包含這些環境變數
除了新增環境變數,移除不再需要的環境變數同樣重要。在開發流程中,某些組態參數可能因為功能變更而過時,或是在測試階段使用的除錯相關設定需要在正式環境中移除。使用 --env-rm 參數可以安全地移除指定的環境變數,這個操作同樣會觸發滾動更新以確保所有副本的組態一致性:
# 移除不再使用的環境變數
# 只需要提供環境變數的名稱,不需要提供值
docker service update \
--env-rm 'MYSQL_PASSWORD' \
--env-rm 'DEPRECATED_CONFIG' \
mysql
# 說明:
# 移除 MYSQL_PASSWORD 環境變數(例如改用 Docker Secrets)
# 移除 DEPRECATED_CONFIG 過時的組態參數
#
# 這個操作會觸發滾動更新,確保所有容器都不再包含這些環境變數
# 如果應用程式依賴這些環境變數,移除後可能造成功能異常
# 因此在執行前應確認應用程式已不再需要這些設定
在實務應用中,環境變數的管理需要遵循安全性與可維護性的最佳實踐。首先,敏感資訊如資料庫密碼、API 金鑰、憑證等,不應直接以環境變數的形式儲存,而應使用 Docker Secrets 或外部密碼管理服務。Docker Secrets 會將敏感資料加密儲存在 Swarm 的 Raft 資料庫中,並以記憶體檔案系統的形式掛載到容器內,提供更好的安全保護。其次,環境變數的命名應該遵循一致的慣例,例如使用全大寫字母並以底線分隔,或是採用特定的前綴來標識變數的用途。最後,建議建立完整的環境變數清單文件,記錄每個變數的用途、預設值,以及變更歷史,方便團隊成員理解與維護。
檢視服務目前的環境變數設定是維運工作中的常見需求。使用 docker service inspect 命令可以取得服務的完整組態資訊,包含所有環境變數的當前設定。由於輸出為 JSON 格式,建議搭配 jq 工具進行格式化與篩選,提升資訊的可讀性:
# 檢視服務的完整組態資訊
# 這個命令會輸出 JSON 格式的詳細服務規格
docker service inspect mysql
# 使用 jq 工具提取環境變數資訊
# 透過 JSON Path 表達式定位到環境變數欄位
docker service inspect mysql | jq '.[0].Spec.TaskTemplate.ContainerSpec.Env'
# 輸出範例:
# [
# "MYSQL_DATABASE=production_db",
# "MYSQL_USER=app_user",
# "MYSQL_ROOT_PASSWORD=root_secure_456"
# ]
# 更進階的查詢:篩選特定前綴的環境變數
docker service inspect mysql | jq '.[0].Spec.TaskTemplate.ContainerSpec.Env | map(select(startswith("MYSQL_")))'
# 說明:
# .[0]:取得陣列的第一個元素(服務資訊)
# .Spec.TaskTemplate.ContainerSpec.Env:導航到環境變數陣列
# map(select(...)):過濾符合條件的元素
映像檔版本管理與智慧回滾策略
映像檔版本更新是服務生命週期管理中最核心的操作之一。無論是部署新功能、修復程式缺陷、套用安全性修補程式,還是進行效能最佳化,都需要透過更新服務所使用的 Docker 映像檔來實現。Docker Swarm 提供了完善的映像檔更新機制,包括可設定的滾動更新策略、即時的健康狀態監控,以及自動化的失敗回滾功能,這些特性共同確保了更新過程的安全性與可靠性。
使用 docker service update 命令搭配 --image 參數可以更新服務的映像檔版本。Swarm 會自動執行滾動更新程序,依據預先定義的更新策略參數,依序更新各個服務副本。在更新過程中,Swarm 會持續監控新版本容器的健康狀態,只有當前一批次的副本成功啟動並通過健康檢查後,才會繼續更新下一批次。這種機制確保了服務在整個更新過程中持續可用,最大程度降低了對使用者的影響:
# 將 nginx 服務的映像檔更新至指定版本
# 使用明確的版本標籤確保部署的可重現性
docker service update --image nginx:1.25.3 nginx
# 說明:
# --image nginx:1.25.3:指定新的映像檔名稱與版本標籤
# nginx:要更新的服務名稱
#
# Swarm 會依據服務的更新策略設定執行滾動更新
# 預設情況下,每次更新一個副本,等待該副本就緒後再更新下一個
# 這種保守的策略確保了最大的穩定性,但更新速度較慢
# 注意:避免使用 latest 標籤
# 在正式環境中應該使用具體的版本號,例如 1.25.3
# 這樣可以確保部署的可重現性,並方便追蹤版本變更歷史
docker service update --image nginx:latest nginx # 不建議在正式環境使用
為了在更新速度與服務穩定性之間取得適當平衡,Docker Swarm 提供了豐富的更新策略參數。這些參數允許管理者精細控制更新的行為,包括每批次同時更新的副本數量、批次之間的等待時間、失敗處理策略,以及可接受的失敗比例等。透過合理配置這些參數,可以根據服務的重要性、複雜度,以及可用資源來設計最適合的更新策略:
# 進階映像檔更新策略配置
# 這個範例展示了如何精細控制滾動更新的各個面向
docker service update \
--image nginx:1.25.3 \
--update-parallelism 2 \
--update-delay 10s \
--update-failure-action rollback \
--update-max-failure-ratio 0.25 \
nginx
# 參數說明:
# --update-parallelism 2
# 每次同時更新 2 個副本,提升更新速度
# 適用於副本數量較多且服務容錯能力較強的場景
# 需要確保叢集有足夠資源同時執行新舊版本的容器
#
# --update-delay 10s
# 每批次更新之間等待 10 秒,讓新版本容器有時間穩定
# 這個間隔時間可以用來觀察新版本的行為,發現潛在問題
# 建議根據應用程式的啟動時間與健康檢查週期來調整
#
# --update-failure-action rollback
# 當更新失敗時自動執行回滾操作,恢復到前一個版本
# 失敗的判定條件包括:容器啟動失敗、健康檢查失敗、超過最大失敗比例
# 這個設定提供了自動化的失敗恢復機制,減少人工介入的需求
#
# --update-max-failure-ratio 0.25
# 允許最多 25% 的更新失敗,超過這個比例就認定整體更新失敗
# 例如有 10 個副本,允許最多 2~3 個副本更新失敗
# 這個參數需要根據服務的容錯能力與可用性要求來調整
當更新過程中發生問題,或是新版本在正式環境中出現預期外的行為時,快速回滾至前一個穩定版本是至關重要的能力。Docker Swarm 支援手動觸發的服務回滾操作,使用 docker service rollback 命令可以將服務恢復到更新前的狀態。回滾操作會使用儲存在服務規格中的前一個版本資訊,重新部署所有副本,整個過程同樣遵循滾動更新的策略設定:
# 手動回滾服務至前一個版本
# 這個命令會撤銷最近一次的服務更新
docker service rollback nginx
# 說明:
# Swarm 會讀取服務規格中儲存的前一個版本資訊
# 包括映像檔版本、環境變數、資源限制等所有設定
# 然後使用滾動更新的方式將所有副本恢復到舊版本
#
# 回滾操作會遵循與更新相同的策略參數
# 包括 update-parallelism、update-delay 等設定
# 因此回滾的速度與穩定性也受到這些參數的影響
# 檢視服務的任務歷史
# 可以看到新舊版本的任務狀態變化
docker service ps nginx
# 輸出範例:
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# abc123def456 nginx.1 nginx:1.25.2 worker-1 Running Running 1 minute ago
# xyz789ghi012 \_nginx.1 nginx:1.25.3 worker-1 Shutdown Failed 2 minutes ago
#
# 說明:
# Running 的任務是回滾後的新任務,使用舊版本 1.25.2
# Shutdown/Failed 的任務是更新失敗的任務,使用新版本 1.25.3
# 任務名稱前的 \_ 符號表示這是被替換的舊任務
在實際的維運實踐中,建議建立完整的映像檔版本管理流程。首先,所有的映像檔都應該使用語意化版本號(Semantic Versioning)進行標記,例如 1.2.3 的格式,其中主版本號表示不相容的 API 變更,次版本號表示向下相容的新功能,修訂版本號表示向下相容的問題修正。其次,應該避免在正式環境中使用 latest 標籤,因為 latest 的內容會隨時間變化,導致部署結果不可預測。第三,配合持續整合與持續部署(CI/CD)流程,自動化映像檔的建置、測試與部署過程,確保每個版本都經過充分驗證。最後,建立映像檔的版本追蹤機制,記錄每個版本的變更內容、測試結果,以及部署歷史,方便問題追溯與回滾決策。
@startuml
!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
state "版本 1.0\n穩定運作" as V1
state "版本 1.1\n滾動更新中" as V11
state "版本 1.1\n更新完成" as V11_done
state "版本 1.2\n滾動更新中" as V12
state "版本 1.2\n發現問題" as V12_fail
state "版本 1.1\n回滾完成" as V11_rollback
V1 --> V11 : 開始更新
V11 --> V11_done : 更新成功\n所有副本健康
V11_done --> V12 : 開始新版本更新
V12 --> V12_fail : 健康檢查失敗\n超過失敗比例
V12_fail --> V11_rollback : 自動回滾
V11_rollback --> V11_rollback : 恢復穩定狀態
@enduml容器標籤管理與中繼資料應用
容器標籤(Container Labels)是 Docker 提供的中繼資料管理機制,允許使用者為容器附加任意的鍵值對(Key-Value Pairs)資訊。這些標籤不會影響容器的執行行為,但可以被外部工具讀取與使用,為容器管理、監控、日誌收集等功能提供了豐富的元資料支援。在 Docker Swarm 環境中,服務層級的容器標籤可以動態更新,讓管理者能夠靈活地為服務附加各種描述性資訊或操作指令。
使用 docker service update 命令搭配 --container-label-add 參數可以為服務的容器新增標籤。這些標籤會自動附加到服務建立的每個容器上,包括後續透過擴展或替換操作建立的新容器。容器標籤的命名建議遵循反向網域名稱(Reverse Domain Name)的慣例,例如 com.example.key,這樣可以避免不同系統或工具之間的標籤名稱衝突:
# 為服務新增容器標籤
# 使用反向網域名稱命名法確保標籤的唯一性
docker service update \
--container-label-add 'com.example.version=2.1.0' \
--container-label-add 'com.example.environment=production' \
--container-label-add 'com.example.team=backend' \
--container-label-add 'com.example.cost-center=engineering' \
mysql
# 說明:
# com.example.version:記錄應用程式的版本資訊
# com.example.environment:標記部署環境(開發、測試、正式)
# com.example.team:標記負責的團隊或部門
# com.example.cost-center:用於成本分攤與資源使用追蹤
#
# 這些標籤會附加到服務的所有容器上
# 監控系統可以根據這些標籤自動分類與聚合指標
# 日誌收集系統可以根據標籤路由日誌到不同的儲存目的地
# 成本管理工具可以根據標籤統計各團隊的資源使用情況
# 移除不再需要的容器標籤
# 使用 --container-label-rm 參數指定要移除的標籤鍵名
docker service update \
--container-label-rm 'com.example.deprecated' \
--container-label-rm 'com.example.temporary-flag' \
mysql
# 說明:
# 只需要提供標籤的鍵名,不需要提供值
# 移除操作會觸發滾動更新,確保所有容器的標籤一致
# 移除標籤不會影響容器的執行行為,是安全的操作
容器標籤在實務應用中有廣泛的用途。在監控領域,Prometheus 等時序資料庫可以根據標籤自動發現服務端點,並將標籤轉換為指標的維度標籤,方便進行多維度的查詢與聚合。在日誌管理領域,Fluentd 或 Logstash 可以讀取容器標籤,根據標籤內容決定日誌的處理方式與儲存目的地。在部署策略領域,標籤可以用於實作藍綠部署或金絲雀發布,透過標籤區分不同版本的服務副本,並使用負載平衡器的路由規則控制流量分配。在成本管理領域,標籤可以標記服務的成本歸屬,方便進行資源使用的追蹤與分攤。
檢視服務的容器標籤設定可以使用 docker service inspect 命令。由於輸出為 JSON 格式,建議使用 --format 參數直接提取感興趣的欄位,或是搭配 jq 工具進行格式化與轉換:
# 使用 Go template 格式化輸出容器標籤
# 直接提取 Labels 欄位並以 JSON 格式顯示
docker service inspect \
--format '{{json .Spec.TaskTemplate.ContainerSpec.Labels}}' \
mysql | jq .
# 輸出範例:
# {
# "com.example.version": "2.1.0",
# "com.example.environment": "production",
# "com.example.team": "backend",
# "com.example.cost-center": "engineering"
# }
# 列出所有標籤的鍵名
docker service inspect mysql | \
jq '.[0].Spec.TaskTemplate.ContainerSpec.Labels | keys'
# 輸出範例:
# [
# "com.example.cost-center",
# "com.example.environment",
# "com.example.team",
# "com.example.version"
# ]
# 查詢特定前綴的標籤
docker service inspect mysql | \
jq '.[0].Spec.TaskTemplate.ContainerSpec.Labels | with_entries(select(.key | startswith("com.example.")))'
# 說明:
# with_entries:對每個鍵值對進行處理
# select(.key | startswith(...)):只保留鍵名符合條件的項目
# 這個查詢可以過濾出特定命名空間的標籤
需要特別注意的是,容器標籤(Container Labels)與服務標籤(Service Labels)是兩個不同的概念。容器標籤附加在服務建立的每個容器上,主要用於容器層級的管理與監控。服務標籤則附加在服務物件本身,用於服務層級的分類、篩選與管理。服務標籤可以使用 --label-add 與 --label-rm 參數進行管理。在實務應用中,應該根據標籤的用途選擇適當的標籤類型。例如,用於監控的標籤應該是容器標籤,因為監控系統需要收集每個容器的指標。用於服務分類的標籤可以是服務標籤,因為這些資訊屬於服務的整體屬性。
資源限制與保留的精細化管理
在多租戶環境或資源受限的基礎設施中,合理配置容器的資源使用限制是確保系統整體穩定性與效能的關鍵。Docker Swarm 允許管理者為服務設定 CPU 與記憶體的資源限制(Limits)與資源保留(Reservations),透過這兩種機制的搭配使用,可以實現資源的彈性分配與嚴格控制。資源限制定義了容器可以使用的最大資源量,防止單一容器過度消耗資源影響其他容器。資源保留則定義了排程器在部署容器時必須確保節點具有的最小可用資源,確保容器能夠獲得足夠的資源正常運作。
使用 docker service update 命令可以動態調整服務的資源配置。這個操作會觸發滾動更新,讓新的資源設定在容器重啟時生效。需要注意的是,修改資源限制與保留不會改變容器的映像檔或環境變數,但可能會影響容器的效能表現與排程結果:
# 完整的資源限制與保留設定
# 同時配置 CPU 與記憶體的限制與保留值
docker service update \
--limit-cpu 2 \
--limit-memory 4G \
--reserve-cpu 0.5 \
--reserve-memory 512M \
mysql
# 參數詳細說明:
# --limit-cpu 2
# 限制容器最多使用 2 個 CPU 核心的運算能力
# 當容器嘗試使用超過此限制的 CPU 時,會被作業系統限流(Throttle)
# 限流會導致容器的程序執行速度變慢,但不會被終止
# 適用於防止 CPU 密集型應用程式過度消耗資源
#
# --limit-memory 4G
# 限制容器最多使用 4GB 的記憶體
# 當容器嘗試分配超過此限制的記憶體時,會觸發 OOM (Out Of Memory) Killer
# OOM Killer 會終止容器內消耗記憶體最多的程序
# 這可能導致容器崩潰,因此需要謹慎設定
#
# --reserve-cpu 0.5
# 保留 0.5 個 CPU 核心給此服務
# 排程器在選擇節點時,會確保節點至少有 0.5 個 CPU 核心的可用資源
# 如果沒有符合條件的節點,任務會處於 Pending 狀態等待資源
# 這個設定不會限制容器實際使用的 CPU,只影響排程決策
#
# --reserve-memory 512M
# 保留 512MB 記憶體給此服務
# 排程器會確保節點有至少 512MB 的可用記憶體才部署任務
# 同樣地,這個設定只影響排程,不限制實際使用量
# 實務建議:
# 1. 限制值應該高於保留值,提供一定的彈性空間
# 2. 保留值應該接近應用程式的基本需求,確保效能
# 3. 限制值應該考慮尖峰負載,但也要避免過度寬鬆造成資源浪費
# 4. 定期監控實際資源使用情況,根據數據調整設定
CPU 資源的設定使用浮點數表示 CPU 核心數。例如,0.5 表示半個 CPU 核心的運算能力,2 表示兩個完整的 CPU 核心,0.25 表示四分之一個 CPU 核心。這種表示方式讓資源分配更加精細,可以根據應用程式的實際需求進行微調。記憶體資源可以使用多種單位表示,包括 B(位元組)、K(千位元組)、M(百萬位元組)、G(十億位元組)等。建議使用 M 或 G 作為單位,提升可讀性。
在設定資源限制時,需要深入瞭解應用程式的資源使用特性。對於 CPU 密集型應用程式,如影片轉碼、機器學習訓練等,應該提供較高的 CPU 限制與保留。對於記憶體密集型應用程式,如資料庫、快取服務等,應該提供足夠的記憶體資源。對於 I/O 密集型應用程式,如網頁伺服器、API 閘道等,資源需求相對較低,可以設定較小的限制與保留值。此外,也要考慮應用程式的負載特性,如果負載波動較大,應該在限制與保留之間保留較大的差距,提供足夠的彈性空間。
檢視服務的資源設定可以使用 docker service inspect 命令。資源相關的資訊位於服務規格的 TaskTemplate.Resources 欄位中,包含了完整的限制與保留設定:
# 檢視服務的資源配置
# 使用 Go template 提取 Resources 欄位
docker service inspect \
--format '{{json .Spec.TaskTemplate.Resources}}' \
mysql | jq .
# 輸出範例:
# {
# "Limits": {
# "NanoCPUs": 2000000000,
# "MemoryBytes": 4294967296
# },
# "Reservations": {
# "NanoCPUs": 500000000,
# "MemoryBytes": 536870912
# }
# }
# 欄位說明:
# NanoCPUs:以奈秒為單位的 CPU 配額
# 1000000000 NanoCPUs = 1 個 CPU 核心
# 因此 2000000000 = 2 個核心,500000000 = 0.5 個核心
# MemoryBytes:以位元組為單位的記憶體大小
# 4294967296 bytes = 4 GB
# 536870912 bytes = 512 MB
# 計算資源使用比例
docker service inspect mysql | jq '
.[0].Spec.TaskTemplate.Resources |
{
cpu_reserve_ratio: (.Reservations.NanoCPUs / .Limits.NanoCPUs),
memory_reserve_ratio: (.Reservations.MemoryBytes / .Limits.MemoryBytes)
}
'
# 輸出範例:
# {
# "cpu_reserve_ratio": 0.25,
# "memory_reserve_ratio": 0.125
# }
#
# 說明:
# CPU 保留值是限制值的 25%
# 記憶體保留值是限制值的 12.5%
# 這個比例可以反映資源配置的彈性程度
@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
rectangle "節點總資源池" as NodePool {
rectangle "CPU: 8 核心\nRAM: 32GB" as TotalRes
}
rectangle "服務 A 資源配置" as SvcA {
rectangle "CPU 保留: 1 核心\nCPU 限制: 2 核心" as CpuA
rectangle "RAM 保留: 2GB\nRAM 限制: 4GB" as MemA
}
rectangle "服務 B 資源配置" as SvcB {
rectangle "CPU 保留: 0.5 核心\nCPU 限制: 1 核心" as CpuB
rectangle "RAM 保留: 1GB\nRAM 限制: 2GB" as MemB
}
rectangle "服務 C 資源配置" as SvcC {
rectangle "CPU 保留: 2 核心\nCPU 限制: 4 核心" as CpuC
rectangle "RAM 保留: 4GB\nRAM 限制: 8GB" as MemC
}
rectangle "剩餘可用資源" as Avail {
rectangle "可排程 CPU: 4.5 核心\n可排程 RAM: 25GB" as AvailRes
}
NodePool --> SvcA : 分配資源
NodePool --> SvcB : 分配資源
NodePool --> SvcC : 分配資源
NodePool --> Avail : 剩餘資源
note right of Avail
排程器依據保留值決定
任務是否可部署到此節點
實際使用可達限制值
但不會影響其他服務
end note
@enduml服務擴展的概念與實作策略
在雲端原生架構的設計理念中,應用程式的水平擴展能力(Horizontal Scalability)是實現高可用性與彈性伸縮的核心機制。Docker Swarm 透過服務副本(Service Replicas)的概念,提供了簡潔而強大的水平擴展功能。管理者只需要指定期望的副本數量,Swarm 協調器就會自動在叢集中的可用節點上建立、排程與管理這些副本,無需手動在每個節點上操作容器。
在傳統的 Docker Engine 環境中,當需要增加應用程式的執行實例時,管理者必須登入每個目標節點,手動執行 docker run 命令來啟動容器。這種方式在管理少量容器時尚可接受,但當應用程式規模擴大到數十甚至數百個容器時,手動操作不僅耗時費力,而且極易出錯。此外,手動管理也缺乏集中化的監控與控制能力,難以確保所有容器都使用相同的組態設定,也無法快速回應負載變化進行動態調整。
Docker Swarm 的服務抽象層徹底改變了這種局面。在 Swarm 模式下,服務(Service)代表應用程式的邏輯概念,定義了容器應該如何執行,包括使用的映像檔、環境變數、資源限制、網路連接等。任務(Task)則是服務的具體執行實例,每個任務對應一個實際執行的 Docker 容器。協調器會根據服務規格中定義的副本數量,在叢集中建立對應數量的任務,並透過排程器將這些任務分配到最適合的節點上執行。
@startuml
!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
package "傳統 Docker Engine 管理模式" {
actor "管理者" as Admin1
node "Node 1" as N1_old {
[docker run nginx] as C1_old
}
node "Node 2" as N2_old {
[docker run nginx] as C2_old
}
node "Node 3" as N3_old {
[docker run nginx] as C3_old
}
Admin1 ..> N1_old : SSH 登入執行命令
Admin1 ..> N2_old : SSH 登入執行命令
Admin1 ..> N3_old : SSH 登入執行命令
}
note bottom of N3_old
缺點:
• 需要逐一登入每個節點
• 缺乏統一管理介面
• 難以確保組態一致性
• 無法自動化擴展
end note
@enduml@startuml
!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
actor "管理者" as Admin2
component "Swarm Manager" as Mgr {
[API Server] as API
[協調器] as Orch
[排程器] as Sched
}
node "Worker Node 1" as W1 {
[容器 1] as C1
[容器 2] as C2
}
node "Worker Node 2" as W2 {
[容器 3] as C3
[容器 4] as C4
}
node "Worker Node 3" as W3 {
[容器 5] as C5
[容器 6] as C6
}
Admin2 --> API : docker service scale\nnginx=6
API --> Orch : 更新目標狀態
Orch --> Sched : 計算排程方案
Sched --> W1 : 分配任務
Sched --> W2 : 分配任務
Sched --> W3 : 分配任務
note bottom of W3
優點:
• 單一命令完成擴展
• 自動負載平衡
• 統一組態管理
• 自動故障恢復
end note
@endumlDocker Swarm 支援兩種服務模式:複製模式(Replicated Mode)與全域模式(Global Mode)。複製模式是預設且最常用的模式,管理者可以明確指定服務應該執行的副本數量,Swarm 會在叢集中的可用節點上平均分散這些副本。這種模式適合無狀態的應用程式服務,如網頁伺服器、API 服務、背景工作程序等。全域模式則會在叢集的每個節點上都執行一個服務副本,當新節點加入叢集時會自動部署副本,當節點離開時副本也會自動移除。這種模式適合需要在每個節點上執行的系統級服務,如監控代理、日誌收集器、安全掃描器等。
需要特別注意的是,只有複製模式的服務可以透過 docker service scale 命令進行擴展或縮減。全域模式的服務副本數量完全由叢集的節點數量決定,無法手動調整。當嘗試對全域模式的服務執行擴展操作時,Docker 會回傳錯誤訊息,提示該服務不支援擴展操作。
服務擴展的實務操作與管理
瞭解了服務擴展的基本概念後,接下來將透過詳細的實際操作範例,展示如何在 Docker Swarm 中建立、擴展、監控與管理服務。這些操作構成了日常維運工作的基礎,熟練掌握這些命令與技巧,對於有效管理容器化應用程式至關重要。
建立服務是所有操作的起點。以下範例展示如何使用 docker service create 命令建立一個複製模式的 MySQL 資料庫服務,並設定必要的環境變數與持久化儲存:
# 建立複製模式的 MySQL 服務
# 這是一個完整的服務建立範例,涵蓋常用的配置選項
docker service create \
--name mysql \
--replicas 1 \
--env MYSQL_ROOT_PASSWORD='secure_root_password_change_me' \
--env MYSQL_DATABASE='application_database' \
--env MYSQL_USER='app_user' \
--env MYSQL_PASSWORD='app_password_change_me' \
--mount type=volume,source=mysql-data,target=/var/lib/mysql \
--network backend-network \
mysql:8.0
# 參數詳細說明:
# --name mysql
# 指定服務的名稱為 mysql,這個名稱用於後續的管理操作
# 服務名稱在叢集中必須是唯一的
#
# --replicas 1
# 初始副本數設定為 1,表示只啟動一個容器實例
# 稍後可以根據負載需求進行擴展
#
# --env MYSQL_ROOT_PASSWORD
# 設定 MySQL root 帳號的密碼
# 警告:在正式環境中應使用 Docker Secrets 管理密碼
#
# --env MYSQL_DATABASE
# 指定容器啟動時自動建立的資料庫名稱
# 應用程式可以直接連接到這個資料庫
#
# --env MYSQL_USER 與 MYSQL_PASSWORD
# 建立一個擁有 MYSQL_DATABASE 完整權限的使用者
# 應用程式應該使用這個帳號而非 root 帳號連接資料庫
#
# --mount type=volume,source=mysql-data,target=/var/lib/mysql
# 使用 Docker Volume 提供持久化儲存
# type=volume:使用 Docker 管理的 Volume
# source=mysql-data:Volume 的名稱,如果不存在會自動建立
# target=/var/lib/mysql:MySQL 的資料目錄
# 這確保了即使容器重啟,資料也不會遺失
#
# --network backend-network
# 將服務連接到名為 backend-network 的 Overlay 網路
# 這個網路應該事先建立,用於後端服務之間的通訊
#
# mysql:8.0
# 使用 MySQL 8.0 版本的官方映像檔
# 建議使用具體的版本號而非 latest 標籤
服務建立完成後,使用 docker service ls 命令可以檢視叢集中所有服務的概覽資訊。這個命令會顯示每個服務的基本狀態,包括服務名稱、模式、副本數量、映像檔版本,以及發布的連接埠等資訊:
# 列出叢集中的所有服務
# 這個命令提供了服務的概覽資訊
docker service ls
# 輸出範例:
# ID NAME MODE REPLICAS IMAGE PORTS
# abc123def456 mysql replicated 1/1 mysql:8.0
#
# 欄位說明:
# ID:服務的唯一識別碼(簡短版本)
# NAME:服務的名稱
# MODE:服務模式(replicated 或 global)
# REPLICAS:當前執行的副本數 / 期望的副本數
# 1/1 表示期望 1 個副本,目前也有 1 個副本在執行
# 如果顯示 2/3,表示期望 3 個副本,但目前只有 2 個在執行
# IMAGE:使用的映像檔名稱與版本
# PORTS:發布到外部的連接埠對應(如果有)
# 檢視服務的詳細任務狀態
# 這個命令會顯示每個任務(容器)的詳細資訊
docker service ps mysql
# 輸出範例:
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
# xyz789ghi012 mysql.1 mysql:8.0 worker-node-1 Running Running 3 minutes ago
#
# 欄位說明:
# ID:任務的唯一識別碼
# NAME:任務名稱,格式為 服務名稱.副本編號
# IMAGE:使用的映像檔
# NODE:任務執行所在的節點
# DESIRED STATE:期望的狀態(Running、Shutdown 等)
# CURRENT STATE:當前的實際狀態與持續時間
# ERROR:如果任務失敗,這裡會顯示錯誤訊息
當應用程式的負載增加時,可以使用 docker service scale 命令擴展服務的副本數量。這個命令會更新服務規格中的副本數設定,觸發協調器建立新的任務並分派到叢集中的可用節點上:
# 將 mysql 服務從 1 個副本擴展到 3 個副本
# Swarm 會在叢集中選擇適當的節點啟動 2 個新的容器
docker service scale mysql=3
# 輸出:
# mysql scaled to 3
# overall progress: 3 out of 3 tasks
# 1/3: running [==================================================>]
# 2/3: running [==================================================>]
# 3/3: running [==================================================>]
# verify: Service converged
# 說明:
# Swarm 保持原有的 1 個副本繼續執行
# 同時在叢集中建立 2 個新的副本
# 新副本會使用相同的映像檔、環境變數與資源設定
# 排程器會考慮節點的可用資源、約束條件與放置偏好
# 選擇最適合的節點來執行新任務
# 檢視擴展後的服務狀態
# 此時應該可以看到 3 個執行中的任務
docker service ps mysql
# 輸出範例:
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# xyz789ghi012 mysql.1 mysql:8.0 worker-node-1 Running Running 10 minutes ago
# abc456def789 mysql.2 mysql:8.0 worker-node-2 Running Running 45 seconds ago
# ghi012jkl345 mysql.3 mysql:8.0 worker-node-3 Running Running 45 seconds ago
#
# 說明:
# 3 個副本分散在不同的節點上,實現了負載平衡
# 原有的 mysql.1 保持執行,不受擴展操作影響
# 新建立的 mysql.2 和 mysql.3 剛啟動約 45 秒
除了 docker service scale 命令,也可以使用 docker service update 命令搭配 --replicas 參數來調整服務的副本數量。這兩種方式在功能上是等效的,但 update 命令的優勢在於可以同時修改其他服務設定,實現一次性的批量更新:
# 使用 update 命令擴展服務
# 這種方式允許同時修改多個服務屬性
docker service update --replicas=5 mysql
# 說明:
# 服務副本數從 3 擴展到 5
# Swarm 會建立 2 個新的任務
# 整個擴展過程是增量的,不會影響現有的副本
# 同時擴展服務並調整資源配置
# 這個範例展示了一次性更新多個設定的能力
docker service update \
--replicas=10 \
--limit-memory 2G \
--reserve-memory 512M \
--env-add 'MAX_CONNECTIONS=200' \
mysql
# 說明:
# 這個命令執行了三項操作:
# 1. 將副本數擴展到 10
# 2. 調整記憶體限制與保留值
# 3. 新增環境變數
#
# 所有變更會透過滾動更新的方式套用
# 每個副本會依序重啟以套用新的設定
# 這確保了變更過程中服務的持續可用性
Docker Swarm 也支援在單一命令中同時擴展多個服務。這在需要協調多個相關服務的擴展時非常有用,例如當網頁伺服器的流量增加時,可能需要同時擴展應用程式伺服器與背景工作程序:
# 一次性擴展多個服務
# 可以在一個命令中指定多個服務的目標副本數
docker service scale web=5 api=10 worker=20
# 說明:
# web 服務擴展到 5 個副本(前端網頁伺服器)
# api 服務擴展到 10 個副本(後端 API 服務)
# worker 服務擴展到 20 個副本(背景任務處理程序)
#
# 這三個擴展操作會同時進行
# 可以確保相關服務的容量同步增長
# 避免某個服務成為效能瓶頸
# 驗證所有服務的擴展結果
docker service ls
# 輸出範例:
# ID NAME MODE REPLICAS IMAGE
# abc123def456 web replicated 5/5 nginx:1.25
# def789ghi012 api replicated 10/10 api-server:2.1
# jkl345mno678 worker replicated 20/20 worker:1.5
服務縮減與資源最佳化
服務縮減是擴展的反向操作,透過減少服務的副本數量來釋放叢集資源。在負載降低的時段,適時縮減不必要的服務副本可以有效降低資源消耗,這在按使用量計費的雲端環境中尤其重要。Docker Swarm 的縮減操作使用與擴展相同的命令,只需要指定較小的目標副本數即可。
當執行縮減操作時,Swarm 協調器會選擇部分任務進行終止。被選中終止的任務會先收到 SIGTERM 訊號,讓容器內的應用程式有機會執行優雅關閉(Graceful Shutdown)程序,例如完成正在處理的請求、儲存狀態資訊、關閉資料庫連線等。如果容器在指定的逾時時間內沒有自行終止,Swarm 會發送 SIGKILL 訊號強制終止容器:
# 將 mysql 服務從 10 個副本縮減到 3 個
# Swarm 會選擇 7 個任務進行終止
docker service scale mysql=3
# 說明:
# 協調器會評估所有任務的狀態
# 通常會優先終止較新的任務或處於異常狀態的任務
# 被終止的任務會執行優雅關閉程序
#
# 對於資料庫服務,縮減操作需要格外謹慎
# 確保不會因為終止副本而遺失重要資料
# 建議使用外部持久化儲存並實施完整的備份策略
# 只檢視執行中的任務
# 使用過濾器排除已終止的任務
docker service ps -f desired-state=running mysql
# 說明:
# -f desired-state=running:只顯示期望狀態為 Running 的任務
# 這個過濾器會排除 Shutdown、Failed 等其他狀態的任務
# 輸出將只包含目前正在執行的 3 個副本
# 檢視所有任務包括歷史記錄
# 不加過濾器可以看到完整的任務歷史
docker service ps mysql
# 輸出範例:
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# xyz789ghi012 mysql.1 mysql:8.0 node-1 Running Running 30 minutes ago
# abc456def789 mysql.2 mysql:8.0 node-2 Running Running 30 minutes ago
# ghi012jkl345 mysql.3 mysql:8.0 node-3 Running Running 30 minutes ago
# mno678pqr901 \_mysql.4 mysql:8.0 node-1 Shutdown Shutdown 2 minutes ago
# stu234vwx567 \_mysql.5 mysql:8.0 node-2 Shutdown Shutdown 2 minutes ago
# ...
#
# 名稱前的 \_ 符號表示這是已被替換或終止的舊任務
# 這些任務的歷史記錄會保留一段時間,方便問題追溯
在某些特殊情況下,可能需要將服務完全縮減到零個副本。這種操作會終止服務的所有任務,但保留服務的定義與組態設定。這在需要暫時停止服務但保留其組態以便日後恢復時非常有用,例如在維護期間或是資源極度緊張的情況下:
# 將服務縮減到零個副本
# 服務定義仍然存在,但不會有任何執行中的容器
docker service scale mysql=0
# 說明:
# 所有任務都會被終止
# 服務的組態設定(映像檔、環境變數、資源限制等)會完整保留
# 網路、Volume 等相關資源也會保留
#
# 這個操作適用於以下場景:
# 1. 計劃性維護期間暫時停止服務
# 2. 資源緊張時釋放不必要的服務
# 3. 測試環境中不使用時節省資源
# 確認服務已沒有執行中的任務
docker service ps -f desired-state=running mysql
# 輸出應該為空,因為沒有執行中的任務
# 檢視服務列表確認服務定義仍存在
docker service ls
# 輸出範例:
# ID NAME MODE REPLICAS IMAGE PORTS
# abc123def456 mysql replicated 0/0 mysql:8.0
#
# REPLICAS 顯示為 0/0,表示期望 0 個副本,當前也是 0 個
# 稍後恢復服務到指定的副本數
# 服務會使用保留的組態設定重新啟動
docker service scale mysql=3
# 說明:
# Swarm 會重新建立 3 個任務
# 所有的組態設定(環境變數、資源限制等)都會套用
# 如果使用 Volume,資料會從持久化儲存中恢復
# 服務恢復的速度取決於映像檔拉取與容器啟動時間
需要特別注意的是,對於有狀態的應用程式,縮減操作可能會造成資料遺失或服務中斷。例如,如果資料庫服務正在處理寫入操作時被終止,可能會造成資料不一致。對於這類服務,建議實施以下保護措施:第一,使用外部持久化儲存(如 Docker Volume、NFS、雲端儲存服務)確保資料不會隨容器終止而遺失。第二,實施完整的備份策略,定期備份關鍵資料。第三,在縮減前先將服務設定為唯讀模式或停止寫入,確保沒有進行中的關鍵操作。第四,監控服務的連線數與活動任務數,選擇負載最低的副本進行終止。
服務移除與資源清理策略
當服務不再需要時,可以使用 docker service rm 命令完全移除服務。這個操作會執行以下動作:終止服務的所有任務,刪除服務的定義與組態,從 Swarm 的內部資料庫中移除服務記錄。需要注意的是,服務移除是不可逆的操作,執行前應該確認服務確實不再需要,並已經完成必要的資料備份:
# 移除 mysql 服務
# 這個操作會終止所有副本並刪除服務定義
docker service rm mysql
# 輸出:
# mysql
# 說明:
# 命令輸出服務名稱表示移除成功
# 所有執行中的任務會被立即終止
# 服務的定義從 Swarm 管理資料庫中刪除
#
# 警告:這是不可逆的操作
# 移除後無法透過回滾或其他方式恢復服務定義
# 如果需要重新建立服務,必須重新執行 service create 命令
# 確認服務已被移除
docker service ls
# mysql 服務不會出現在列表中
# 嘗試檢視已移除服務的任務會得到錯誤
docker service ps mysql
# 輸出:
# Error: No such service: mysql
# 同時移除多個服務
# 可以在一個命令中指定多個服務名稱
docker service rm mysql redis mongodb
# 說明:
# 這個命令會依序移除三個服務
# 所有服務的任務都會被終止
# 適用於批量清理測試環境或廢棄的服務
服務移除後,Docker 會自動清理一些相關資源,但某些外部建立的資源需要手動處理。具體來說,與服務關聯的容器會被自動刪除,服務專用的網路端點也會被移除。但是,如果服務使用了外部建立的 Volume、Network、Secret 或 Config,這些資源不會被自動刪除,因為它們可能被其他服務使用。管理者需要根據實際情況決定是否清理這些資源:
# 清理未使用的 Volume
# 移除所有未被任何容器掛載的 Volume
docker volume prune -f
# 說明:
# -f 或 --force:不顯示確認提示,直接執行清理
# 這個命令會掃描所有 Volume
# 刪除那些沒有被任何容器(執行中或停止中)使用的 Volume
#
# 警告:如果 Volume 包含重要資料,應該先備份再清理
# 被刪除的 Volume 無法恢復
# 清理未使用的網路
# 移除所有未被容器連接的自訂網路
docker network prune -f
# 說明:
# 預設的 bridge、host、none 網路不會被刪除
# 只有使用者自訂的網路才會被清理
# Swarm 建立的 Overlay 網路如果沒有服務使用也會被刪除
# 全面清理系統資源
# 這是最激進的清理方式,會刪除多種類型的資源
docker system prune -f
# 說明:
# 這個命令會清理:
# 1. 所有停止的容器
# 2. 所有未使用的網路
# 3. 所有懸空的映像檔(dangling images)
# 4. 所有建置快取
#
# 警告:這個命令會刪除大量資源
# 可能會影響其他正在執行的容器或服務
# 建議先不加 -f 參數執行,檢視將被刪除的資源清單
# 更激進的清理:包含所有未使用的映像檔
docker system prune -af
# 說明:
# -a 或 --all:同時刪除所有未被容器使用的映像檔
# 不只是懸空的映像檔
# 這會大幅減少磁碟空間使用,但下次啟動容器時需要重新拉取映像檔
# 檢視資源使用情況
# 在清理前後執行這個命令可以瞭解釋放了多少空間
docker system df
# 輸出範例:
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Images 15 5 2.5GB 1.8GB (72%)
# Containers 8 3 125MB 82MB (65%)
# Local Volumes 12 2 1.2GB 1.1GB (91%)
# Build Cache 45 0 850MB 850MB (100%)
#
# 說明:
# TOTAL:該類型資源的總數量
# ACTIVE:正在使用中的資源數量
# SIZE:該類型資源佔用的總空間
# RECLAIMABLE:可以透過清理釋放的空間與百分比
為了避免意外刪除重要資源,建議在執行清理操作前先確認將被刪除的內容。可以省略 -f 參數,讓命令在執行前顯示確認提示,並列出將被刪除的資源清單。對於重要的正式環境,建議建立定期的備份機制,並實施嚴格的資源標記與管理策略,確保重要資源有明確的標示,避免被誤刪。
全域服務的特性與典型應用場景
全域服務(Global Service)是 Docker Swarm 提供的一種特殊服務模式,其運作機制與複製模式有著本質的差異。全域服務會在叢集中的每個節點上都執行一個任務,包括 Manager 節點與 Worker 節點。當新節點加入叢集時,Swarm 會自動在該節點上部署全域服務的任務。當節點離開叢集或被標記為無法排程(drain)時,該節點上的全域服務任務也會自動終止。這種特性使得全域服務特別適合部署需要在每個節點上執行的基礎設施服務。
建立全域服務使用 docker service create 命令搭配 --mode global 參數。與複製模式不同,全域服務不需要指定副本數量,因為副本數量完全由叢集的節點數量決定:
# 建立全域模式的監控代理服務
# 這個範例部署 Prometheus Node Exporter 收集系統指標
docker service create \
--name node-exporter \
--mode global \
--mount type=bind,source=/proc,target=/host/proc,readonly \
--mount type=bind,source=/sys,target=/host/sys,readonly \
--mount type=bind,source=/,target=/rootfs,readonly \
--publish mode=host,published=9100,target=9100 \
prom/node-exporter:latest \
--path.procfs=/host/proc \
--path.sysfs=/host/sys \
--collector.filesystem.mount-points-exclude="^/(sys|proc|dev|host|etc)($|/)"
# 參數詳細說明:
# --mode global
# 指定服務為全域模式,在每個節點上都執行一個任務
# 這確保了每個節點的系統指標都能被收集
#
# --mount type=bind,source=/proc,target=/host/proc,readonly
# 將主機的 /proc 目錄掛載到容器內的 /host/proc
# /proc 包含了程序與系統的即時資訊
# readonly 確保容器無法修改主機的 /proc 內容
#
# --mount type=bind,source=/sys,target=/host/sys,readonly
# 掛載主機的 /sys 目錄,包含裝置與核心的資訊
#
# --mount type=bind,source=/,target=/rootfs,readonly
# 掛載主機的根目錄,用於讀取檔案系統資訊
#
# --publish mode=host,published=9100,target=9100
# 使用 host 模式發布連接埠
# 繞過 Swarm 的路由網格,每個節點的 9100 連接埠直接對應到本機容器
# 這是全域服務常用的連接埠發布方式
#
# --path.procfs=/host/proc
# 告訴 node-exporter 從 /host/proc 讀取程序資訊
#
# --path.sysfs=/host/sys
# 告訴 node-exporter 從 /host/sys 讀取系統資訊
#
# --collector.filesystem.mount-points-exclude
# 排除虛擬檔案系統的掛載點,避免收集無意義的指標
# 檢視全域服務的任務分佈
docker service ps node-exporter
# 輸出範例(假設叢集有 5 個節點):
# ID NAME IMAGE NODE
# abc123def456 node-exporter.xyz789ghi012 prom/node-exporter:... manager-1
# def789ghi012 node-exporter.abc456def789 prom/node-exporter:... worker-1
# ghi012jkl345 node-exporter.def012ghi345 prom/node-exporter:... worker-2
# jkl345mno678 node-exporter.ghi678jkl901 prom/node-exporter:... worker-3
# mno678pqr901 node-exporter.jkl234mno567 prom/node-exporter:... worker-4
#
# 說明:
# 每個節點上都有一個 node-exporter 任務
# 任務數量等於叢集的節點數量
# 即使是 Manager 節點也會執行全域服務的任務
全域服務無法使用 docker service scale 命令進行手動擴展或縮減。嘗試對全域服務執行擴展操作會得到錯誤訊息,因為全域服務的副本數量完全由叢集的拓撲結構決定:
# 嘗試擴展全域服務會失敗
docker service scale node-exporter=10
# 輸出錯誤:
# node-exporter: cannot scale a global service
#
# 說明:
# 全域服務的副本數量由節點數量自動決定
# 無需也無法手動調整副本數量
#
# 如果需要調整全域服務的副本數量:
# 1. 新增節點到叢集:Swarm 會自動在新節點上部署任務
# 2. 移除節點:該節點上的任務會自動終止
# 3. 將節點設定為 drain:該節點不會執行新任務,現有任務會被遷移
全域服務的典型應用場景包括多個領域。在監控領域,如 Prometheus Node Exporter、Datadog Agent、New Relic Infrastructure Agent 等需要收集每個節點系統指標的服務。在日誌管理領域,如 Fluentd、Filebeat、Logstash Forwarder 等需要在每個節點上收集容器日誌的服務。在安全領域,如 Falco、Sysdig、Aqua Security Agent 等需要在每個節點上監控安全事件的服務。在網路領域,如 Calico Node、Weave Net、Flannel 等提供跨節點容器網路的服務。在儲存領域,如 GlusterFS Client、Ceph CSI Driver 等提供分散式儲存存取的服務。
@startuml
!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
package "Docker Swarm 叢集" {
node "Manager Node" as Mgr {
component "[node-exporter]" as NE_mgr
component "[fluentd]" as FD_mgr
}
node "Worker Node 1" as W1 {
component "[node-exporter]" as NE_w1
component "[fluentd]" as FD_w1
}
node "Worker Node 2" as W2 {
component "[node-exporter]" as NE_w2
component "[fluentd]" as FD_w2
}
node "Worker Node 3" as W3 {
component "[node-exporter]" as NE_w3
component "[fluentd]" as FD_w3
}
}
cloud "監控系統" as Monitor {
database "Prometheus" as Prom
}
cloud "日誌系統" as Logging {
database "Elasticsearch" as ES
}
NE_mgr --> Prom : 系統指標
NE_w1 --> Prom : 系統指標
NE_w2 --> Prom : 系統指標
NE_w3 --> Prom : 系統指標
FD_mgr --> ES : 日誌資料
FD_w1 --> ES : 日誌資料
FD_w2 --> ES : 日誌資料
FD_w3 --> ES : 日誌資料
note bottom of W3
全域服務特性:
• 每個節點一個副本
• 自動隨節點增減
• 適合基礎設施服務
• 無需手動擴展
end note
@enduml服務排程約束與節點親和性控制
Docker Swarm 的排程器在決定任務應該部署到哪個節點時,會綜合考慮多個因素,包括節點的可用資源、節點的健康狀態、服務的資源保留需求,以及管理者定義的排程約束條件。透過合理配置排程約束,可以實現更精細的任務分佈控制,例如將特定服務部署到配備高速 SSD 的節點,或是將服務分散到不同的可用區域以提升容錯能力。
排程約束使用 --constraint 參數定義,可以基於節點的多種屬性進行過濾。最常用的屬性包括節點標籤(Labels)、節點角色(Role)、節點主機名稱(Hostname),以及節點 ID 等。約束條件支援等於(==)、不等於(!=)等比較運算子:
# 在設定約束前,先為節點新增標籤
# 標籤是節點的自訂屬性,用於分類與識別
docker node update --label-add storage=ssd worker-node-1
docker node update --label-add storage=ssd worker-node-2
docker node update --label-add storage=hdd worker-node-3
# 說明:
# 為 worker-node-1 和 worker-node-2 標記為配備 SSD 儲存
# 為 worker-node-3 標記為使用 HDD 儲存
# 這些標籤可以用於排程約束
# 建立只在 SSD 節點上執行的資料庫服務
# 這確保了資料庫能夠獲得最佳的 I/O 效能
docker service create \
--name postgres-db \
--constraint 'node.labels.storage==ssd' \
--replicas 3 \
postgres:15
# 說明:
# --constraint 'node.labels.storage==ssd'
# 只有具有 storage=ssd 標籤的節點會被選擇
# 排程器會在 worker-node-1 和 worker-node-2 之間分配 3 個副本
# worker-node-3 不會執行這個服務的任務
#
# 如果沒有足夠的符合條件的節點:
# 部分任務會處於 Pending 狀態,等待合適的節點
# 例如,如果只有 1 個 SSD 節點,那麼其他 2 個任務無法部署
# 其他常用的約束條件範例
# 只在 Manager 節點上執行管理工具
docker service create \
--constraint 'node.role==manager' \
--name swarm-admin \
swarm-admin-tool:latest
# 說明:
# node.role 可以是 manager 或 worker
# 這種約束適用於需要存取 Swarm API 的管理工具
# 但要注意不要在 Manager 節點上執行過多服務,以免影響叢集管理效能
# 只在特定主機名稱的節點上執行
docker service create \
--constraint 'node.hostname==production-server-01' \
--name critical-service \
critical-app:latest
# 說明:
# 將關鍵服務固定在特定的節點上
# 適用於需要特殊硬體或組態的服務
# 但這會降低服務的可移動性與容錯能力
# 排除特定節點
docker service create \
--constraint 'node.hostname!=edge-server-01' \
--name backend-api \
api-server:2.0
# 說明:
# 使用 != 運算子排除特定節點
# 服務會在除了 edge-server-01 以外的所有節點上排程
# 適用於避開資源不足或組態特殊的節點
除了約束條件,Docker Swarm 還提供了放置偏好(Placement Preferences)機制。與約束條件的硬性限制不同,放置偏好是軟性的建議,排程器會嘗試遵循偏好設定,但如果無法滿足也不會導致任務無法部署。最常用的放置偏好是分散策略(Spread),它會嘗試將任務平均分散到具有不同標籤值的節點上:
# 先為節點設定可用區域標籤
# 模擬多可用區域的部署環境
docker node update --label-add zone=asia-east1-a worker-node-1
docker node update --label-add zone=asia-east1-a worker-node-2
docker node update --label-add zone=asia-east1-b worker-node-3
docker node update --label-add zone=asia-east1-b worker-node-4
docker node update --label-add zone=asia-east1-c worker-node-5
docker node update --label-add zone=asia-east1-c worker-node-6
# 說明:
# 模擬了一個跨三個可用區域的叢集
# 每個區域有 2 個節點
# 這種設定常見於雲端環境,用於提升服務的容錯能力
# 建立具有區域分散偏好的網頁應用服務
docker service create \
--name web-app \
--replicas 6 \
--placement-pref 'spread=node.labels.zone' \
nginx:latest
# 說明:
# --placement-pref 'spread=node.labels.zone'
# 排程器會嘗試將 6 個副本平均分散到 3 個區域
# 理想情況下,每個區域會有 2 個副本
#
# 如果某個區域的節點資源不足:
# 排程器會將任務分配到其他區域
# 確保所有任務都能成功部署
# 這就是「軟性」偏好的特性
# 檢視任務的實際分佈
docker service ps web-app
# 預期輸出範例:
# ID NAME NODE ZONE
# abc123 web-app.1 worker-node-1 asia-east1-a
# def456 web-app.2 worker-node-2 asia-east1-a
# ghi789 web-app.3 worker-node-3 asia-east1-b
# jkl012 web-app.4 worker-node-4 asia-east1-b
# mno345 web-app.5 worker-node-5 asia-east1-c
# pqr678 web-app.6 worker-node-6 asia-east1-c
#
# 可以看到副本平均分散在三個區域
# 即使單一區域完全失效,其他區域的副本仍可繼續服務
# 結合約束與偏好
# 可以同時使用多個排程策略
docker service create \
--name prod-api \
--replicas 9 \
--constraint 'node.labels.env==production' \
--placement-pref 'spread=node.labels.zone' \
--placement-pref 'spread=node.labels.rack' \
api-server:2.1
# 說明:
# 這個服務只會部署到標記為 production 環境的節點(硬性約束)
# 並且會嘗試在不同的 zone 和 rack 之間分散(軟性偏好)
# 這種組合可以實現既有明確的部署範圍,又有最佳的分散策略
更新服務的約束條件或放置偏好使用 docker service update 命令。這個操作會觸發任務的重新排程,排程器會根據新的約束條件重新選擇節點,可能會導致任務遷移到不同的節點:
# 新增約束條件
# 限制服務只能在正式環境節點上執行
docker service update \
--constraint-add 'node.labels.env==production' \
web-app
# 說明:
# 如果現有任務執行在不符合新約束的節點上
# 這些任務會被終止並在符合約束的節點上重新建立
# 這個過程遵循滾動更新策略
# 移除約束條件
# 放寬服務的排程限制
docker service update \
--constraint-rm 'node.labels.env==production' \
web-app
# 說明:
# 移除約束後,服務可以在更多節點上排程
# 現有任務不會受影響,只有新任務或替換任務會使用新的排程規則
# 新增放置偏好
docker service update \
--placement-pref-add 'spread=node.labels.datacenter' \
web-app
# 移除放置偏好
docker service update \
--placement-pref-rm 'spread=node.labels.datacenter' \
web-app
健康檢查機制與自動修復策略
Docker Swarm 的自動修復(Self-Healing)能力是確保服務高可用性的重要機制。當服務的任務失敗、變得不健康,或是執行任務的節點發生故障時,Swarm 協調器會自動啟動新的任務來替換失敗的任務,確保服務始終維持期望的副本數量。這個自動修復機制依賴於容器的健康檢查(Health Check)功能,透過定期執行檢查命令來評估容器的健康狀態。
健康檢查可以在建立或更新服務時使用 --health-* 系列參數來定義。一個完整的健康檢查配置包括檢查命令、檢查間隔、檢查逾時、重試次數,以及啟動寬限期等參數:
# 建立具有完整健康檢查配置的網頁服務
docker service create \
--name web-app \
--replicas 3 \
--health-cmd 'curl -f http://localhost:8080/health || exit 1' \
--health-interval 30s \
--health-timeout 10s \
--health-retries 3 \
--health-start-period 60s \
--publish published=80,target=8080 \
my-web-app:latest
# 參數詳細說明:
# --health-cmd 'curl -f http://localhost:8080/health || exit 1'
# 定義健康檢查命令
# curl -f:失敗時返回非零退出碼
# http://localhost:8080/health:應用程式的健康檢查端點
# || exit 1:如果 curl 失敗,明確返回退出碼 1
#
# 命令會在容器內執行,因此必須確保:
# 1. curl 工具已安裝在容器映像檔中
# 2. 健康檢查端點能夠正常回應
# 3. 檢查邏輯能夠真實反映應用程式的健康狀態
#
# --health-interval 30s
# 每 30 秒執行一次健康檢查
# 較短的間隔能更快發現問題,但會增加系統負擔
# 建議根據應用程式的特性調整,一般設定在 10s 到 60s 之間
#
# --health-timeout 10s
# 單次健康檢查的逾時時間為 10 秒
# 如果檢查命令在 10 秒內沒有完成,視為本次檢查失敗
# 應該設定為略大於檢查命令正常執行時間的值
#
# --health-retries 3
# 連續 3 次檢查失敗才判定容器為不健康狀態
# 這個設定可以避免因為偶發性的網路延遲或系統負載導致誤判
# 建議設定為 2 到 5 之間
#
# --health-start-period 60s
# 容器啟動後的前 60 秒為啟動寬限期
# 在這個期間內的健康檢查失敗不計入重試次數
# 這給了應用程式足夠的時間完成初始化
# 應該根據應用程式的啟動時間來設定
# 實務建議:
# 1. 健康檢查端點應該檢查應用程式的關鍵依賴
# 例如資料庫連線、快取服務連線等
# 2. 避免檢查邏輯過於複雜,影響效能
# 3. 確保檢查端點不需要認證,或使用專用的健康檢查憑證
# 4. 考慮使用專用的輕量級檢查端點,避免影響業務邏輯
當容器的健康檢查連續失敗超過指定的重試次數時,容器會被標記為不健康(Unhealthy)狀態。對於 Swarm 服務的任務,一旦被標記為不健康,協調器會終止該任務並啟動一個新的任務來替換。這個自動替換機制確保了服務能夠自我修復,從暫時性的故障中恢復:
# 檢視服務任務的健康狀態
# CURRENT STATE 欄位會顯示容器的健康狀態
docker service ps web-app
# 輸出範例(包含不健康的任務):
# ID NAME IMAGE NODE DESIRED CURRENT STATE
# abc123 web-app.1 my-web-app:... node-1 Running Running (healthy)
# def456 web-app.2 my-web-app:... node-2 Running Running (unhealthy)
# ghi789 web-app.3 my-web-app:... node-3 Running Running (healthy)
#
# web-app.2 顯示為 unhealthy 狀態
# Swarm 會很快終止這個任務並啟動新的替換任務
# 檢視任務的詳細資訊
# 可以看到健康檢查失敗的具體原因
docker inspect <任務ID或容器ID>
# 在輸出的 JSON 中查找 State.Health 欄位:
# "Health": {
# "Status": "unhealthy",
# "FailingStreak": 3,
# "Log": [
# {
# "Start": "2025-11-28T10:30:00Z",
# "End": "2025-11-28T10:30:10Z",
# "ExitCode": 1,
# "Output": "curl: (7) Failed to connect to localhost port 8080"
# }
# ]
# }
#
# 說明:
# Status:當前健康狀態
# FailingStreak:連續失敗的次數
# Log:最近幾次檢查的詳細記錄
# Output:檢查命令的輸出,可以幫助診斷問題
# 更新服務的健康檢查設定
# 可以在服務執行中調整健康檢查參數
docker service update \
--health-cmd 'wget -q -O /dev/null http://localhost:8080/health' \
--health-interval 15s \
web-app
# 說明:
# 將檢查命令改為使用 wget(某些基礎映像檔沒有 curl)
# 將檢查間隔縮短到 15 秒,更快發現問題
# 這個更新會觸發滾動更新,新的健康檢查設定會套用到所有副本
設計有效的健康檢查是確保自動修復機制正常運作的關鍵。健康檢查應該能夠真實反映應用程式的可用性,而不僅僅是檢查程序是否在執行。對於網頁應用程式,建議建立專用的健康檢查端點,這個端點可以執行以下檢查:驗證應用程式的核心功能是否正常,檢查資料庫連線是否可用,確認快取服務是否可達,驗證外部 API 依賴是否正常。健康檢查的回應時間應該快速,避免影響系統效能。同時,健康檢查端點不應該執行複雜的業務邏輯或資料庫查詢,以免造成不必要的系統負擔。
滾動更新策略的進階配置
Docker Swarm 的滾動更新(Rolling Update)機制是實現零停機部署的核心功能。預設的滾動更新策略採用保守的方式,一次只更新一個副本,等待其成功啟動並通過健康檢查後再更新下一個。這種策略確保了最大的穩定性,但在副本數量較多時,更新過程可能會較為耗時。透過調整更新策略的各項參數,可以在更新速度與服務穩定性之間找到最佳平衡點。
--update-order 參數決定了新舊任務的替換順序,這是一個容易被忽視但非常重要的設定。stop-first(預設值)會先終止舊任務再啟動新任務,這種方式可以避免資源競爭,但會短暫減少可用的副本數量。start-first 會先啟動新任務,等待其就緒後再終止舊任務,可以確保服務容量不減少,但需要叢集有足夠的額外資源來同時執行新舊任務:
# 建立具有進階滾動更新策略的服務
docker service create \
--name api-server \
--replicas 10 \
--update-parallelism 3 \
--update-delay 30s \
--update-failure-action rollback \
--update-max-failure-ratio 0.2 \
--update-monitor 60s \
--update-order start-first \
api-server:v1.0
# 參數詳細說明:
# --update-parallelism 3
# 每次同時更新 3 個副本
# 對於有 10 個副本的服務,整個更新過程會分為 4 個批次
# 批次 1:更新副本 1、2、3
# 批次 2:更新副本 4、5、6
# 批次 3:更新副本 7、8、9
# 批次 4:更新副本 10
#
# 設定建議:
# - 副本數量較多(>10)時,可以設定較大的並行度加快更新
# - 關鍵服務建議設定較小的並行度(1-2)確保穩定性
# - 考慮叢集的總資源容量,避免並行更新耗盡資源
#
# --update-delay 30s
# 每個批次之間等待 30 秒
# 這個延遲時間有幾個重要用途:
# 1. 讓新版本的副本有時間充分啟動與穩定
# 2. 提供觀察窗口,發現新版本的潛在問題
# 3. 避免更新速度過快導致系統負載激增
#
# 設定建議:
# - 應該大於應用程式的啟動時間
# - 考慮健康檢查的間隔與重試次數
# - 關鍵服務可以設定更長的延遲(1-5分鐘)
#
# --update-failure-action rollback
# 當更新失敗時自動執行回滾操作
# 失敗的判定條件包括:
# 1. 任務無法啟動(映像檔拉取失敗、容器啟動錯誤)
# 2. 健康檢查持續失敗
# 3. 失敗任務數量超過 max-failure-ratio
#
# 其他可選值:
# - pause:暫停更新,等待人工介入
# - continue:忽略失敗,繼續更新其他副本
#
# --update-max-failure-ratio 0.2
# 允許最多 20% 的任務更新失敗
# 對於 10 個副本的服務,允許最多 2 個任務失敗
# 超過這個比例就認定整體更新失敗
#
# 設定建議:
# - 副本數量較多時可以設定較高的容錯率(0.2-0.3)
# - 副本數量較少時應該更嚴格(0.1-0.15)
# - 關鍵服務建議設定為 0,不容許任何失敗
#
# --update-monitor 60s
# 在標記任務為成功前監控 60 秒
# 即使任務啟動成功且通過初始健康檢查
# 也要持續監控一段時間確保穩定性
#
# 在監控期間內發生的失敗會被計入 max-failure-ratio
# 這可以捕捉到啟動後才顯現的問題
#
# --update-order start-first
# 先啟動新任務再終止舊任務
# 確保服務容量在整個更新過程中不減少
#
# 適用場景:
# - 高流量服務,無法容忍容量短暫下降
# - 叢集有足夠的額外資源
# - 新舊版本可以安全地共存
#
# 注意事項:
# - 需要額外的資源來同時執行新舊任務
# - 可能會短暫超出資源限制
# - 確保新舊版本的相容性
# 執行映像檔更新
# 這會觸發滾動更新程序
docker service update --image api-server:v2.0 api-server
# 更新過程示意:
# 時間 T0:10 個 v1.0 副本全部執行中
# 時間 T1:啟動 3 個 v2.0 副本(批次 1)
# 時間 T2:v2.0 副本 1、2、3 就緒,終止 v1.0 副本 1、2、3
# 現在有 7 個 v1.0 + 3 個 v2.0 副本
# 時間 T3:等待 30 秒(update-delay)
# 時間 T4:啟動 3 個 v2.0 副本(批次 2)
# ... 重複直到所有副本都更新完成
# 即時監控更新進度
# 使用 watch 命令每 2 秒重新整理一次
watch -n 2 'docker service ps api-server | head -20'
# 輸出範例(更新進行中):
# ID NAME IMAGE NODE DESIRED CURRENT STATE
# abc123 api-server.1 api-server:v2.0 node-1 Running Running 2 minutes ago
# def456 \_api-server.1 api-server:v1.0 node-1 Shutdown Shutdown 2 minutes ago
# ghi789 api-server.2 api-server:v2.0 node-2 Running Preparing 30 seconds ago
# jkl012 api-server.3 api-server:v1.0 node-3 Running Running 30 minutes ago
# ...
#
# 可以觀察到:
# - api-server.1 已經更新到 v2.0 並執行中
# - 舊的 api-server.1 (v1.0) 已經被終止
# - api-server.2 正在啟動 v2.0 版本
# - api-server.3 還在使用 v1.0,等待更新
# 檢視更新的詳細狀態
docker service inspect --format='{{json .UpdateStatus}}' api-server | jq .
# 輸出範例:
# {
# "State": "updating",
# "StartedAt": "2025-11-28T10:00:00Z",
# "CompletedAt": null,
# "Message": "update in progress"
# }
瞭解並善用這些滾動更新參數,可以為不同類型的服務設計最適合的更新策略。對於關鍵業務服務,建議採用保守的策略,一次只更新一個副本,設定較長的監控時間與延遲,確保每個步驟都完全穩定後再繼續。對於非關鍵服務或開發測試環境,可以採用較激進的策略,增加並行度與縮短延遲時間,加快更新速度。對於有特殊需求的服務,如需要資料庫遷移或複雜初始化的服務,可能需要使用自訂的部署腳本,而不是依賴自動化的滾動更新。
總結與最佳實踐建議
Docker Swarm 提供了一套完整而強大的服務生命週期管理功能,涵蓋了從服務建立、組態更新、版本升級,到服務擴展與健康管理的各個面向。透過本文的深入探討,讀者應該已經掌握了容器編排的核心概念與實務技能,包括環境變數的動態管理、映像檔版本的控制與回滾、資源配置的精細化調整、服務副本的彈性擴展,以及健康檢查與自動修復機制的設計。
在實際的維運實踐中,建議建立標準化的服務管理流程與自動化工具鏈。首先,為不同類型的服務制定對應的部署範本,包含合適的資源配置、健康檢查設定、更新策略等。其次,整合監控系統持續追蹤服務的運作狀態、資源使用情況,以及效能指標,建立警示機制及時發現潛在問題。第三,實施完整的日誌管理與分析系統,集中收集所有容器的日誌,方便問題診斷與趨勢分析。第四,建立變更管理流程,所有的服務更新都應該經過完整的測試驗證,並準備好回滾計劃。
對於生產環境的關鍵服務,安全性與可靠性是首要考量。建議使用 Docker Secrets 管理敏感資訊而非環境變數,實施嚴格的映像檔簽章驗證機制,確保部署的映像檔來源可信。設定適當的資源限制與保留值,避免資源競爭影響服務穩定性。配置健康檢查與自動修復機制,讓服務具備自我恢復能力。使用標籤與約束條件實現多可用區域部署,提升容錯能力。定期執行災難恢復演練,驗證備份與恢復程序的有效性。
Docker Swarm 雖然在生態系統與進階功能上不如 Kubernetes 豐富,但其簡潔的設計與低學習曲線使其成為中小型專案的理想選擇。對於團隊規模有限、希望快速建立容器編排環境的情況,Docker Swarm 提供了足夠的功能來滿足大多數應用場景。隨著對 Docker Swarm 的深入理解與實踐經驗的累積,讀者可以進一步探索其進階功能,如 Stack 部署管理、Config 與 Secret 的統一管理、與 CI/CD 工具的深度整合,以及跨資料中心的叢集聯邦等,建立更加完善的 DevOps 工作流程,實現真正的雲端原生應用架構。