基于 Docker 1.12 Swarm 的集群管理開發實踐
Mesos/Marathon 折騰久了,我們一直希望有機會深入到 Swarm 內部一探究竟。 另外, Mesos 這一套東西雖然是久經企業級考驗的, 但是安裝、部署和使用相對復雜,上手有門檻。同時,在今年的 DockerCon 上,內置了Swarm 功能的 Docker 1.12 發布。基于以上背景,數人云計劃圍繞 Docker 1.12 Swarm 開發一版輕量級的集群管理工具,也借此與 Mesos/Marathon 對比下。目前,我們第一版數人云容器管理面板 Crane 已經開發完畢,過程也是磕磕絆絆,這里趁機總結幾篇技術分享。
正文開始前先八卦一下,關注 Docker 技術的小伙伴們應該清楚 Docker 1.12 的 Swarm mode 頗受爭議:首先是有人認為 Docker 公司 Market Drive Develop,違背了 Linux 信徒恪守的哲學——一個工具只干一件事情;其次, 有人認為 Swarm mode 的功能不及 Mesos 和 K8S,還不適合生產環境使用,這一點我倒認為穩定性而不是功能才是 Swarm 目前不適合生產環境的原因;最后, Docker 的向后兼容性不足也引來口水無數,畢竟 Docker 還在 active develop。其它的像容器網絡標準的爭議, Runc 的爭議這些都把 Docker 推到了風口浪尖。當然,不辯不明,相信 Docker 給我們提供的不止眼前這些。
我們首先從 應用編排( Application Stack) 談起,應用編排是 Docker 1.12 引入的概念,目前還是 experimental 的功能,必須得安裝 experimental 的包才可以嘗試。除去編排 (stack), Docker 1.12 還引入了 服務 (service) 和任務 (task) 的概念, Docker 借此重新闡述了應用與容器 (container) 之間的關系。上述幾個概念的關系如下圖所示:
即:一個應用編排代表一組有依賴關系的服務,服務之間可以相互發現(后面詳細介紹),每個服務由多個任務組成,任務的數量可以擴縮 (scale),而任務則物化為一個具體的 Docker 容器及其配置。
Docker 通過擴展名為 dab( Distributed application bundles) 的文件來描述一個應用編排,下圖是一個帶有兩個服務的 dab 文件例子:
這里有dab文件的詳細介紹:
https://github.com/docker/docker/blob/master/experimental/docker-stacks-and-bundles.md
其中 Image 這里推薦使用 image@digest 而不是 image:tag,原因是為了避免這種情況 : 在集群中部署服務時, image:tag 無法保證鏡像是全局一致的,本地的 image:tag 可能與鏡像倉庫里面的 image:tag 數據不一致,這個問題在跨機環境中被放大。而 image@digest 這種方式通過中心化倉庫設置全局唯一的 digest 值,避免了上述問題。
除此之外,還有下述幾個關鍵特性值得分享:
-
滾動更新: 服務的鏡像更新是一個基本訴求。 Docker 可以通過關鍵詞 update-parallelism 和 update-delay 來控制并行更新的頻率。 Marathon 也提供了類似的功能。這個特性很關鍵,如果無法控制更新頻率,成百上千的鏡像拉取和任務調度會導致嚴重的資源波峰。 Docker 文檔還聲稱支持更新失敗回滾,嘗試了下,目前沒發現怎么玩,還沒來得及看底層代碼。
-
服務模式 (service mode): Docker 1.12 提供了兩種方式控制任務數量—— replicated 和 global,在 replicated 方式下,我們需要提供期望的任務數量, Swarm 將一直嘗試維護這個任務數;而在 global 方式下, Swarm 嘗試在每個節點上啟動一個任務,這種方式特別適合向每個節點下發 agent 的場景。
-
stop-grace-period 參數: 在服務縮容時,我們比較關心容器被強制 kill 而帶來的事務 (transaction) 問題,配合該參數 stop-grace-period( 強制殺死容器前的等待時間 ) 和容器內部的退出信號監聽,可以達到容忍程序友好退出和快速回收資源之間的平衡。 Mesos / Marathon 也采用了類似的策略來解決這個問題。
-
with-registry-auth 參數: 在服務創建時,該參數聲明將管理節點 (Swarm manager) 上的 registry authentication 信息帶到工作節點 (worker node) 上,從而為工作節點提供從 registry 拉取鏡像的認證信息。在 Docker 的非 Swarm 場景下, Docker-client 負責 registry 認證信息的管理,但在 Swarm 方式下,不再是 Docker-client 觸發鏡像拉取動作,所以服務無法使用工作節點本地的 registry 認證信息了,必須要通過上述方式從管理節點分發認證信息。同時節點間的加密通信也保證了認證信息傳輸的安全性。
-
任務的生命周期: 在容器的生命周期之上, Docker 1.12 引入了任務 (task) 的生命周期。某任務下的容器異常退出時,帶有同樣任務編號 (slot) 的新容器將會被嘗試啟動。不同于容器的生命周期只囿于一臺固定主機上,任務的生命周期是與主機無關的,我們可以依此對容器的日志進行聚合得到任務的日志。這一點正好是 Mesos / Marathon 所欠缺的。
-
重啟策略: Docker 1.12 提供了三種重啟條件 -any, none, on-failure,其中 none 指的是退出不重啟, on-failure 指的是失敗( exit code 不是零)時重啟,而 any 指的是無論任務正常或是異常退出,都重啟。 any 方式配合參數重啟間隔 (restart-delay) 可以滿足定時任務的場景; none 方式則可以滿足批處理任務場景。另外參數評估間隔 (restart-window) +參數嘗試次數 (restart-max-attempts) 可以控制在一段時間內的任務重啟次數,避免異常任務頻繁重啟帶來的集群資源失控。
當然,Swarm mode 還有很多問題亟待解決:
-
dab 文件的表達能力有限: 當前版本的 dab 文件只有寥寥數個關鍵詞,服務 (service) 創建的諸多參數 dab 都無法支持。在我們的實踐中,為了解決這個問題, team 不得不二次開發引入服務的其它參數,以期對服務的參數全量支持。
-
容器回收問題: 按照目前的設計,只要服務 (service) 還在,退出的容器是不會被自動回收掉的,在某些場景下,這會導致集群失控,各主機的文件描述符被耗盡。
-
容器的健康檢查 (healthcheck): 在我看來,這是 Swarm mode 之外, Docker 1.12 引入的最關鍵功能,有了 healthcheck 這個 feature,我們可以將業務內部真正的健康狀況暴露出來了。這個功能落后于 Marathon 足足一年的時間。但可惜的是,服務( service)創建目前還不支持這個關鍵詞,這就限制了服務 (service) 異常重啟的能力,從而間接降低了服務容錯能力。這一點 Marathon 做的特別好。
-
資源控制: 1.12 目前支持單個任務 CPU / mem 的資源控制。還無法像 Mesos 那樣,自由的配置管理磁盤, GPU,端口等資源。
-
無法使用主機網絡: 通過服務( service)啟動的容器是無法使用主機網絡的 (network host is not eligible for Docker services),但同時按照網絡上 Percona 提供的壓測結果, overlay 網絡相較于主機網絡有 60% 的性能損耗。這嚴重局限了 Swarm 的應用場景,我們可以認為這是編排功能帶來的架構副作用。而 Mesos 從資源維度管理集群很好的規避了這個問題。
接下來讓我們看看下一層: Docker 是怎樣在 stack, service, task container 之間建立聯系的?同一個 stack 內的 service 又是如何相互發現的?
第一個問題很好回答, service label 和 container name, Docker 通過在 service 上添加 label: com.Docker.stack.namespace=XXX 來標示這個 service 屬于哪一個 stack。我們可以 inspect 一個 service 看下:
Docker 通過特定格式的 container name 標示這個 container 隸屬于哪一個 service 下面,如下圖所示:
容器名稱 merryfox_mysql.1.by842qrj7xhsne93yzfpjp367 代表該容器是服務 merryfox_mysql 的任務 1 的容器。Docker 在很多地方使用了這種技巧來處理數據。而第二個問題就引出了我們下面的——服務發現。
服務發現
在談服務發現之前,我們簡單討論下 Docker overlay 網絡的性能問題,根據 https://www.percona.com/blog/2016/08/03/testing-Docker-multi-host-network-performance/ 的網絡壓測結果, 相較于 host 網絡, overlay 有 60% 的網絡性能損耗 ,問題主要出在多 CPU 下網絡負載不均。同時容器無法在 Swarm 編排模式下使用 host 網絡,這帶來的問題就是: 在 Docker 1.12 Swarm mode 下網絡性能損耗無法避免 。
與 Marathon / Mesos 的 Mesos-DNS、 bamboo 類似, Swarm 的服務發現也分為內部服務發現 (internal service discovery) 與外部服務發現 (ingress service discovery),這兩種服務發現使用了不同的技術。
如果想讓一個服務暴露到集群之外,我們需要借助 service create 的參數 publish,該參數顯式的聲明將集群特定端口 PORT_N(集群端口 PORT_N 代表集群中所有主機的端口 PORT_N)分配給這個服務。這樣無論該服務的容器運行在哪臺主機上,我們都可以通過集群中任何主機的 PORT_N 端口訪問這個服務了。下圖可以形象的描述這個場景:
接下來,我們就可以把集群中部分或所有主機的 IP 加上述端口配置到我們的前置負載均衡器上對公網提供服務了。一般稱這種分布式的負載均衡策略為 routing mesh,在 Calico 網絡方案中也提到了類似的概念。由于沒有了中心化的負載均衡器,集群不會因某臺機器異常而導致整個服務對外不可用,很好的避免了單點問題,同時也帶了可擴展性。關于routing mesh這個概念的詳細介紹可以參考該鏈接( https://en.wikipedia.org/wiki/Mesh_networking )。
這里摘抄一個簡短解釋:
A mesh network is a network topology in which each node relays data for the network. All mesh nodes cooperate in the distribution of data in the network.
上述就是 Swarm 的外部負載均衡(也可以稱為routing mesh),那么 Docker 在底層做了什么來實現上述功能的呢?如下圖所示:
-
在 Swarm 集群初始化時, Docker 會創建一個 overlay 網絡 ingress,同時在每個節點上創建與 ingress 關聯的 sandbox 網絡命名空間,這樣集群中的每個主機都變為了 ingress 網絡的一部分;
-
當我們創建 service 申請一個 publish port 時, Docker 會通過 Iptables rules 建立 主機 IP:Port 到 sandbox IP:Port 間的映射,即 : 將對應端口的包轉發給 ingress 網絡;
-
同時在 sandbox 網絡命名空間內, Docker 通過 Iptables rules + IPVS 控制對應 端口/ Port 的包負載均衡到不同的容器。這里 IPVS(IP virtual server) 的功能與 HAproxy 類似,承擔著 Swarm 集群內部的負載均衡工作。
對于只供集群內部訪問的服務,無需使用上述 routing mesh,Swarm 還提供了一套內部服務發現 + 負載均衡。如下圖所示(這里我們只討論基于 VIP 的負載均衡方法):
-
manager 會為該 service 分配一個 VIP(Virtual IP),并在內部 DNS 上建立一條 VIP 與 service name 的映射記錄。注意這里的 DNS server 也是分布式的,每一個主機上都有一個 DNS server ;
-
Iptables 配合 IPVS 將 VIP 請求負載到 service 下面的一個具體容器上。
另外,主機間 routing mesh,load balancing rule 等信息是利用gossip協議進行數據同步的,限于篇幅,這里不再深究這個問題。
最后友情提示幾個雷區:
Q1:為什么我的機器無法加入 (join) 到 Swarm 集群中?終端報錯:加入集群超時。
A1:這個問題極有可能是主機間時鐘不同步導致的,建議開啟 ntp 服務,同步主機時間。
Q2:為什么我在 manager A 上通過命令 Docker network create 的 overlay 網絡無法在集群另外的機器 B 上通過 Docker network ls 發現?
A2: 這有可能是正常的。按 Swarm 當前的設計,只有使用相應網絡的容器被調度到了機器 B 上, overlay 網絡信息才會更新到機器 B 上去。
Q3: 為什么我的服務的 publish port 在有些機器上不生效?我使用 netstat – lnp 方式看不到端口監聽。
A3: 與問題 1 一樣,這也可能是時鐘不同步導致的問題。
來自:http://mp.weixin.qq.com/s?__biz=MzA3MDg4Nzc2NQ==&mid=2652134137&idx=1&sn=cea9f4f5cf604670c3868e9a94f3e363&scene=4