- 容器即服務:從零構建企業級容器集群
- 林帆
- 4036字
- 2019-09-09 16:31:52
2.2 使用SwarmKit
2.2.1 SwarmKit綜述
SwarmKit是Docker現代集群的基礎,它的代碼開源在GitHub的獨立倉庫中,主要提供了基于Docker容器構建集群并進行節點管理和服務管理的能力。
在節點管理方面,SwarmKit能夠將普通的虛擬機賦予集群角色,并承擔節點增加、退出以及故障失聯時自動處理的職責,確保在高可用的SwarmKit集群里任意一個節點故障都不會影響集群的整體功能。這個能力得益于在SwarmKit中使用了Etcd項目的Raft模塊。
Raft是一種分布式一致性協議,目的在于解決在不可信任的網絡集群中進行狀態確認的問題。Raft協議把集群中的節點分為三種角色:Leader、Follower、Candidate。
在任何時刻,集群中只存在一個Leader角色的節點,它保存整個集群的完整狀態,其余節點都作為Follower角色,從Leader節點同步狀態信息。在集群剛剛創建時,第一個進入集群的節點通常會將自己標記為Leader,此后進入的節點都是Follower。這樣的角色劃分并沒有什么奇特之處,不過Raft協議的高明之處在于,集群中的角色并非是一成不變的。Leader節點依靠定時向所有Follower發送心跳數據包來保持其地位,當Follower節點在一定的時間周期內沒有收到來自Leader節點的心跳數據包時(通常是發生了網絡分區或是節點故障),就會發起新的一輪“Leader選舉”。此時,當前在Raft集群中的所有正常節點都將進入Candidate角色,并在一段隨機的延時后向其他節點發出“給我投票”的請求,每個處于Candidate角色的節點都會把票投給第一個向自己發送“投票”請求的節點。若有Candidate節點在此輪投票中獲得總節點數量一半以上的投票,則它會將自己標記為新的Leader。否則重新進入下一輪投票,直到有節點獲得半數以上的票數。每成功選舉一次,新Leader的Term(任屆)值都會比之前Leader的增加1。當集群中由于網絡或其他原因的故障出現分裂又重新合并時,集群中可能會出現多于一個的Leader節點,此時,Term值更高的一個節點將成為真正的Leader。Raft集群中的角色轉換如圖2-2所示。

圖2-2 Raft三種角色的轉換
在服務管理方面,SwarmKit能夠將用戶創建的服務以Task為單元,依據當前整個集群各節點的負荷情況,自動地分配到適當的節點上運行,并對這些服務的運行狀態進行持續跟蹤。所有的這些信息都將存儲到Raft集群中,確保了服務狀態不受節點故障的影響。除了服務的調度,SwarmKit還提供了服務路由、負載均衡、故障處理和在線升級等能力。通常來說,在集群里創建的服務單元,其后端可以由多個運行在各個節點上的同種容器組成分擔負載壓力的邏輯單元,如圖2-3所示。當有外部請求該服務時,SwarmKit負責將這些請求均勻地分擔到每個執行業務的容器里。當因部分容器意外崩潰而無法提供正常服務時,SwarmKit會檢測到這些問題,并自動地創建新的容器以確保每個服務后端的容器數量與預期保持一致。

圖2-3 SwarmKit集群中的服務
SwarmKit依賴Raft協議保持集群狀態的高可用,在Raft協議中,為了確保集群的狀態和信息一致性,Leader節點每次對存儲的數據進行修改時,都需要告知所有Follower節點,并在獲得半數以上Follower確認后,才能夠將數據的修改持久化。隨著節點數量的增加,確認消息的成本將成倍增長。因此,SwarmKit又將所有節點劃分成了Manager和Worker兩種角色類型,這兩類節點都會參與服務的調度,但只有Manager節點真正保存集群信息,并且參與Raft的選舉過程。
這里的兩種“角色”十分容易被混淆,它們在各自的術語中都被稱為“Role”,因此需要通過上下文來識別。
·SwarmKit(以及Swarm Mode)集群的角色:用來區分Manager或Worker,它們的關鍵差異在于是否存儲Raft數據以及是否接收用戶的操作請求。
·Raft集群的角色:指的是Leader、Follower或Candidate,它們只存在于SwarmKit的Manager角色節點,各角色的關鍵差異在于是否主導Raft協議中的數據一致性協商。
如未特別說明,本書以下內容中提到的“角色”一般都是指SwarmKit(以及Swarm Mode)集群中的角色。
SwarmKit節點作為Manager還是Worker,是在節點進入集群時人為指定的,它同樣可以在節點創建之后再進行轉換,只不過這種轉換不會自動發生,需要人工指派。下個小節會介紹這個過程。
2.2.2 創建SwarmKit集群
SwarmKit沒有發布編譯過的二進制版本,因為通常用戶都會用Docker的Swarm Mode來間接地使用它。如果想直接使用SwarmKit工具,最直接的方式就是獲取源代碼進行編譯。首先通過Git下載它的代碼倉庫,如下所示。
$ git clone https://github.com/docker/swarmkit.git Cloning into 'swarmkit'... ... ...
編譯SwarmKit需要一個Golang的SDK和相應的開發工具鏈,若手頭上沒有這樣的環境,一種比較簡便的辦法是使用提供了這種環境的Docker鏡像,例如官方的golang:1.8鏡像。執行以下命令將SwarmKit代碼目錄掛載到Docker容器里進行構建。
$ docker run --rm -it \ -v 'pwd'/swarmkit:/go/src/github.com/docker/swarmkit \ -w /go/src/github.com/docker/swarmkit/ golang:1.8 \ make binaries bin/swarmd bin/swarmctl bin/swarm-bench bin/protoc-gen-gogoswarm binaries
構建完成的文件存放在SwarmKit代碼目錄的“bin”文件夾內,將其中的swarmctl和swarmd兩個文件拷貝到系統PATH變量指定的目錄中,例如“/usr/local/bin”,如下所示。
$ sudo mv swarmkit/bin/swarmctl swarmkit/bin/swarmd /usr/local/bin/
在SwarmKit項目中,swarmd是負責管理和維護集群的后臺進程,swarmctl是用戶進行集群交互式操作的工具。不帶任何參數的swarmd命令將用默認配置創建一個集群,并將當前節點作為集群的第一個Manager節點。默認配置會使用用戶當前的目錄存放SwarmKit運行過程中產生的各種數據文件,這并不是一種推薦的做法,下面這個命令會在系統后臺啟動swarmd進程,并指定集群的狀態數據存放目錄、監聽的Socket文件位置、節點名字以及日志文件位置。
$ swarmd --state-dir /tmp/node-mgmt-01--listen-control-api\ /tmp/mgmt-01.sock --hostname mgmt-01 >/tmp/mgmt-01.log 2>&1 &
這樣就得到了只有一個Manager節點的最小化SwarmKit集群。從嚴格意義來說,它還算不上一個真正的集群,但在這個單節點集群中已經可以執行SwarmKit的各種管理操作,并且它也是創建更大規模集群的基礎。
此時,使用swarmctl命令可以查看集群的信息。為了讓swarmctl能夠連接到SwarmKit集群,需要使用--socket參數或SWARM_SOCKET環境變量指定通信的Socket文件位置。swarmctl node ls命令將列出集群節點的信息,如下所示。
$ export SWARM_SOCKET=/tmp/mgmt-01.sock $ swarmctl node ls ID Name Membership Status Availability Manager Status -- ---- ---------- ------ ------------ -------------- ea0b612... ACCEPTED READY ACTIVE REACHABLE *
為了向集群中添加更多的節點,還需要查詢出當前Manager節點的IP地址和集群的Join Token,如下所示。集群的Join Token相當于新節點加入集群的密碼,只有提供了正確Token的請求才會被接受。此外,Join Token還可被用來區分新節點角色是Manager還是Worker。
$ ip addr show eth0 eth0: <BROADCAST, MULTICAST, UP, LOWER_UP> mtu 9001 ... inet 172.31.27.16/20 brd 172.31.31.255 scope global eth0 ... ... $ swarmctl cluster inspect default ID : fe9vsdtfjco9vxjipwor1nj8x Name : default Orchestration settings: Task history entries: 5 Dispatcher settings: Dispatcher heartbeat period: 5s Certificate Authority settings: Certificate Validity Duration: 2160h0m0s Join Tokens: Worker: SWMTKN-1-...-29kxz34bcz8gvzdwjpimutxvp Manager: SWMTKN-1-...-34npzkvzoxss2s6tx7q1ss11s
注意swarmctl cluster inspect default這個命令的輸出,它顯示了名為“default(SwarmKit的默認集群名字)”集群的兩個“Join Token”。這兩個Token分別對應了兩種節點角色。
接下來向集群添加新的節點。將先前編譯出來的swarmctl和swarmd文件拷貝到其他需要加入SwarmKit集群的節點上,同樣放到PATH環境變量的目錄里。這次在啟動swarmd進程時,除了指定狀態數據目錄等信息,還需要提供--join-addr和--join-token參數表示加入已有集群(而不是新建集群),這里使用集群的Worker Join Token將該節點作為Worker角色。
$ MANAGER_IP=<Manager節點IP> $ JOIN_TOKENS=<Worker Join Token> $ swarmd --state-dir /tmp/node-work-01--hostname work-01\ --join-addr ${MANAGER_IP}:4242 \ --join-token ${JOIN_TOKENS} >/tmp/work-01.log 2>&1 &
重復該過程,添加更多節點到集群中,然后回到任意一個Manager節點上使用swarmctl node ls命令查看集群的節點信息,如下所示。
$ swarmctl node ls ID Name Membership Status Availability Manager Status -- ---- ---------- ------ ------------ -------------- ea0b612... ACCEPTED READY ACTIVE REACHABLE * lnxa1hn... ACCEPTED READY ACTIVE bhk2f1r... ACCEPTED READY ACTIVE ... ...
需要注意的是,所有swarmctl命令必須連接Manager節點的Socket文件才能使用,通常來說這也意味著只能在Manager節點上使用swarmctl命令。這是因為只有Manager節點才真正存儲了集群的狀態信息,而Worker節點僅僅用于執行服務容器。
使用swarmctl node promote和swarmctl node demote命令可以對節點的角色進行轉換,它們的參數都是節點的ID,前者將一個Worker節點提升為Manager節點,后者反之。
此外,SwarmKit還為節點提供了“停機維護”的功能,命令是swarmctl node drain,如下所示。
$ swarmctl node drain <節點ID>
被指定的節點可用狀態會被標記為“DRAIN”,此時如果該節點上運行有SwarmKit托管的容器服務,將被自動遷移到其他節點上運行,如下所示。
$ swarmctl node ls ID Name Membership Status Availability Manager Status -- ---- ---------- ------ ------------ -------------- ea0b612... ACCEPTED READY ACTIVE REACHABLE * lnxa1hn... ACCEPTED READY ACTIVE bhk2f1r... ACCEPTED READY DRAIN
使用swarmctl node activate可將節點恢復為正常運行狀態,如下所示。
$ swarmctl node activate <Node-ID>
2.2.3 在SwarmKit集群上運行服務
作為Docker的集群組件,SwarmKit最重要的能力之一便是對服務的調度和管理。這些功能大多可以通過swarmctl service這個命令來操作。
創建服務的操作是swarmctl service create,這個命令會通過SwarmKit的后臺進程API在集群中創建使用指定鏡像部署的服務,并根據當前集群各節點資源狀態和服務的其他約束條件,將服務運行的Task調度到最合適的地方執行,如下所示。
$ swarmctl service create --name nginx --image nginx:1.11.1-alpine t2neubvbyyqc8rynt09drjhl9
在SwarmKit集群中,默認情況下所有節點都會參與服務的調度,包括Manager和Worker節點,這一點與過去的Swarm以及后面幾章介紹的Kubernetes、Mesos等都不同。
使用swarmctl service ls命令可以查看當前集群運行的所有服務,如下所示。
$ swarmctl service ls ID Name Image Replicas -- ---- ----- -------- t2neubvbyyqc8rynt09drjhl9 nginx nginx:1.11.1-alpine 1/1
查看單個服務的詳細信息可以使用swarmctl service inspect,如下所示。
$ swarmctl service inspect nginx ID : t2neubvbyyqc8rynt09drjhl9 Name : nginx Replicas : 1/1 Template Container Image : nginx:1.11.1-alpine Task ID Service Slot Image Desired State... Node ------- ------- ---- ----- ------------- ... ---- rrk1vhg... nginx 1 nginx:... RUNNING ... mgmt-01
服務部署以后,還可以通過swarmctl service update命令來更新它的一些屬性,比如容器副本的個數,如下所示。
$ swarmctl service update nginx --replicas 6 t2neubvbyyqc8rynt09drjhl9
查看更新后的服務狀況,可以發現名稱是nginx的這個服務副本數變成了“6/6”,表示目標副本數量是6,當前實際運行的副本數也是6,如下所示。
$ swarmctl service ls ID Name Image Replicas -- ---- ----- -------- t2neubvbyyqc8rynt09drjhl9 nginx nginx:1.11.1-alpine 6/6
使用swarmctl service inspect nginx命令將列出其中每個容器副本所對應的Task信息,如下所示。
$ swarmctl service inspect nginx ... ... Task ID Service Slot Image Desired State ... Node ------- ------- ---- ----- ------------- ---- rrk1vhg... nginx 1 nginx:... RUNNING mgmt-01 fmfgcqm... nginx 2 nginx:... RUNNING mgmt-01 5f2vi8d... nginx 3 nginx:... RUNNING work-01 w0s07ie... nginx 4 nginx:... RUNNING work-01 qndf2cv... nginx 5 nginx:... RUNNING work-02 xt2pm9j... nginx 6 nginx:... RUNNING work-02
如果在其中一個節點上執行docker container ps命令,如下所示,會看到在該節點上運行的那些容器。
$ docker container ps CONTAINER ID IMAGE COMMAND ... ... NAMES 1863bb173d97 nginx:... "..." ... ... nginx.1.rrk1vhg... 2ab1d45d0a3b nginx:... "..." ... ... nginx.2.fmfgcqm...
使用swarmctl service update -help可以看到在SwarmKit中允許動態更新的內容,其中--image這個參數經常被使用,它可以用來替換容器的鏡像,這實際上可以用于升級服務的版本。例如下面這個命令可以將nginx服務的版本升級到1.11.3-alpine。
$ swarmctl service update nginx --image nginx:1.11.3-alpine t2neubvbyyqc8rynt09drjhl9
執行完下面這個命令,服務的容器會被重啟并替換成指定的新鏡像。
$ swarmctl service ls ID Name Image Replicas -- ---- ----- -------- t2neubvbyyqc8rynt09drjhl9 nginx nginx:1.11.1-alpine 6/6
還可以加上更多的參數來控制服務升級的過程,例如下面這個命令會每隔四秒鐘升級一部分服務,每次并行升級兩個容器,直到所有容器都升級到新的鏡像版本。
$ swarmctl service update nginx --image nginx:1.11.3-alpine \ --update-parallelism 2--update-delay 4s
2.2.4 SwarmKit集群的其他功能
除了已經介紹的節點和服務管理功能,SwarmKit還提供了Task、Secret以及集群網絡的管理。
$ swarmctl --help ... ... Available Commands: node Node management service Service management task Task management version Print version number of swarm network Network management cluster Cluster management secret Secrets management
Task是SwarmKit的最小管理單元,在當前版本里,一個Task實際上就對應一個容器。SwarmKit抽象出Task的概念主要是為了未來能夠讓這個模型用在除容器外的集群資源管理,比如直接接管虛擬機,甚至是Unikernel的實例。Secret是SwarmKit中對用戶密鑰信息的封裝,這部分功能在講解Swarm Mode的相應部分時再做介紹。
最后簡單說一下SwarmKit中的network命令,它和docker network比較類似,不過在SwarmKit中只能看到后者的一部分網絡。實際上,在這個命令中所能創建和管理的正是Docker網絡中scope值為swarm的那些網絡,它們通常都是建立在SDN之上的跨節點的虛擬網絡,例如使用swarmctl network ls命令只顯示驅動類型是overlay的那個網絡,而不會看到本地節點上驅動類型為bridge的其他網絡,如下所示。
$ swarmctl network ls ID Name Driver -- ---- ------ xegwekbeisau53lmr6lyz2gak ingress overlay