Docker Swarm 管理節(jié)點高可用分析

1.簡介

1.1 swarm是什么

Swarm 是使用 SwarmKit 構(gòu)建的 Docker 引擎內(nèi)置(原生)的集群管理和編排工具。其主要作用是把若干臺Docker主機抽象為一個整體,并且通過一個入口統(tǒng)一管理這些Docker主機上的各種Docker資源。Swarm和Kubernetes比較類似,但是更加輕量,具有的功能也較kubernetes更少一些。

Docker v1.12 是一個非常重要的版本,Docker 重新實現(xiàn)了集群的編排方式。在此之前,提供集群功能的 Docker Swarm 是一個單獨的軟件,而且依賴外部數(shù)據(jù)庫(比如 Consul、etcd 或 Zookeeper)。從 v1.12 開始,Docker Swarm 的功能已經(jīng)完全與 Docker Engine 集成,要管理集群,只需要啟動 Swarm Mode。安裝好 Docker,Swarm 就已經(jīng)在那里了,服務(wù)發(fā)現(xiàn)也在那里了(不需要安裝 Consul 等外部數(shù)據(jù)庫)。

Docker的Swarm(集群)模式,集成很多工具和特性,比如:跨主機上快速部署服務(wù),服務(wù)的快速擴展,集群的管理整合到docker引擎,這意味著可以不可以不使用第三方管理工具。分散設(shè)計,聲明式的服務(wù)模型,可擴展,狀態(tài)協(xié)調(diào)處理,多主機網(wǎng)絡(luò),分布式的服務(wù)發(fā)現(xiàn),負載均衡,滾動更新,安全(通信的加密)等等。

docker-swarm-logo.png

官方網(wǎng)站:https://docs.docker.com/engine/swarm/

1.2 swarm基本概念

1.2.1 節(jié)點

運行 Docker 的主機可以主動初始化一個 Swarm 集群或者加入一個已存在的 Swarm 集群,這樣這個運行 Docker 的主機就成為一個 Swarm 集群的節(jié)點 (node) 。

節(jié)點分為管理 (manager) 節(jié)點和工作 (worker) 節(jié)點。

管理節(jié)點用于 Swarm 集群的管理,docker swarm 命令基本只能在管理節(jié)點執(zhí)行(節(jié)點退出集群命令 docker swarm leave 可以在工作節(jié)點執(zhí)行)。一個 Swarm 集群可以有多個管理節(jié)點,但只有一個管理節(jié)點可以成為 leaderleader 通過 raft 協(xié)議實現(xiàn)。

工作節(jié)點是任務(wù)執(zhí)行節(jié)點,管理節(jié)點將服務(wù) (service) 下發(fā)至工作節(jié)點執(zhí)行。管理節(jié)點默認也作為工作節(jié)點。也可以通過配置讓服務(wù)只運行在管理節(jié)點。

Docker 官網(wǎng)的這張圖片形象的展示了集群中管理節(jié)點與工作節(jié)點的關(guān)系。

swarm-node.png

1.2.2 服務(wù)和任務(wù)

任務(wù) (Task)是 Swarm 中的最小的調(diào)度單位,目前來說就是一個單一的容器

服務(wù) (Services) 是指一組任務(wù)的集合,服務(wù)定義了任務(wù)的屬性。服務(wù)有兩種模式:

  • replicated services 按照一定規(guī)則在各個工作節(jié)點上運行指定個數(shù)的任務(wù)
  • global services 每個工作節(jié)點上運行一個任務(wù)

兩種模式通過 docker service create--mode 參數(shù)指定。

1.2.3 負載均衡

Swarm Manager使用 ingress load balancing 來對外公開集群提供的服務(wù)。manager可以自動為服務(wù)分配端口,也可以手動配置。如果未指定端口,則swarm集群管理器會為服務(wù)分配30000-32767范圍內(nèi)的端口。

外部組件(例如云負載平衡器)可以訪問集群中任何節(jié)點的PublishedPort上的服務(wù),無論該節(jié)點當前是否正在運行該服務(wù)的任務(wù)。集群中的所有節(jié)點都將入口連接到正在運行的任務(wù)實例。

Swarm模式有一個內(nèi)部DNS組件,可以自動為swarm中的每個服務(wù)分配一個DNS條目。集群管理器使用內(nèi)部負載平衡來根據(jù)服務(wù)的DNS名稱,在集群內(nèi)的服務(wù)之間分發(fā)請求。

1.3 swarm特性

  • 集群管理與Docker Engine集成:使用Docker Engine CLI來創(chuàng)建一個你能部署應(yīng)用服務(wù)到Docker Engine的swarm集群。你不需要其他編排軟件來創(chuàng)建或管理swarm集群。

  • 分散式設(shè)計:你可以使用Docker Engine部署兩種類型的節(jié)點:managerworker,在swarm集群運行期間,可以對其作出任何改變,實現(xiàn)對集群的擴容和縮容等,如添加manager節(jié)點,如刪除worker節(jié)點,而做這些操作不需要暫停或重啟當前的Swarm集群服務(wù)

  • 聲明性服務(wù)模型:Docker Engine使用了一種聲明的方式,讓我們可以定義我們所期望的各種服務(wù)的狀態(tài),例如,我們創(chuàng)建了一個應(yīng)用服務(wù)棧:一個Web前端服務(wù)、一個后端數(shù)據(jù)庫服務(wù)、Web前端服務(wù)又依賴于一個消息隊列服務(wù)

  • 伸縮性:對于每個服務(wù),你可以聲明要運行的任務(wù)數(shù)。當你需要伸縮時,只需通過命令行指定Docker容器數(shù)量即可,Swarm集群運行時便能自動地、靈活地進行調(diào)整

  • 協(xié)調(diào)預(yù)期狀態(tài)與實際狀態(tài)的一致性:Swarm Manager持續(xù)監(jiān)控集群狀態(tài),協(xié)調(diào)集群狀態(tài)使得我們預(yù)期狀態(tài)和實際狀態(tài)保持一致。 例如,運行擁有10個副本的服務(wù),這時 worker 節(jié)點中的兩個副本崩潰,管理器將創(chuàng)建兩個新副本以替換已崩潰的副本。 swarm管理器將新副本分配給正在運行和可用的worker節(jié)點

  • 多主機網(wǎng)絡(luò):你可以為服務(wù)指定覆蓋網(wǎng)絡(luò)(overlay network),當swarm管理器初始化或更新應(yīng)用程序時,它會自動為容器在覆蓋網(wǎng)絡(luò)(overlay network)中分配IP地址。

  • 服務(wù)發(fā)現(xiàn):Swarm Manager 為swarm中的每個服務(wù)分配唯一的DNS名稱,對運行中的Docker容器進行負載均衡??梢?通過Swarm集群內(nèi)置的DNS Server,查詢Swarm集群中運行的Docker容器狀態(tài)

  • 負載平衡:在Swarm內(nèi)部,可以指定如何在各個Node之間分發(fā)服務(wù)容器(Service Container),實現(xiàn)負載均衡。如果想要使用Swarm集群外部的負載均衡器,可以將服務(wù)容器的端口暴露到外部

  • 安全通信:swarm中的每個節(jié)點強制執(zhí)行TLS相互驗證和加密,以保護其自身與所有其他節(jié)點之間的通信。 你可以選擇使用自簽名根證書或來自自定義根CA的證書

  • 滾動更新:對于服務(wù)需要更新的場景,我們可以在多個Node上進行增量部署更新,Swarm Manager支持通過使用Docker CLI設(shè)置一個delay時間間隔,實現(xiàn)多個服務(wù)在多個Node上依次進行部署。這樣可以非常靈活地控制,如果有一個服務(wù)更新失敗,則暫停后面的更新操作,重新回滾到更新之前的版本

2.架構(gòu)分析

2.1 基本架構(gòu)

Docker Swarm提供了基本的集群能力,能夠使多個Docker Engine組合成一個group,提供多容器服務(wù)。Swarm使用標準的Docker API,啟動容器可以直接使用docker run命令。Swarm更核心的則是關(guān)注如何選擇一個主機并在其上啟動容器,最終運行服務(wù)。 Docker Swarm基本架構(gòu),如下圖所示:

docker-swarm-architecture.png

如上圖所示,Swarm Node表示加入Swarm集群中的一個Docker Engine實例,基于該Docker Engine可以創(chuàng)建并管理多個Docker容器。其中,最開始創(chuàng)建Swarm集群的時候,Swarm Manager便是集群中的第一個Swarm Node。在所有的Node中,又根據(jù)其職能劃分為Manager Node和Worker Node。

2.1 Manager Node

Manger 節(jié)點,顧名思義,是進行 Swarm 集群的管理工作的,它的管理工作集中在如下部分,

  • 維護集群的狀態(tài);
  • 對 Services 進行調(diào)度;
  • 為 Swarm 集群提供外部可調(diào)用的 API 接口;

Manager 節(jié)點需要時刻維護和保存當前 Swarm 集群中各個節(jié)點的一致性狀態(tài),這里主要是指各個 tasks 的執(zhí)行的狀態(tài)和其它節(jié)點的狀態(tài);因為 Swarm 集群是一個典型的分布式集群,在保證一致性上,Manager 節(jié)點采用 Raft 協(xié)議來保證分布式場景下的數(shù)據(jù)一致性;

通常為了保證 Manager 節(jié)點的高可用,Docker 建議采用奇數(shù)個 Manager 節(jié)點,這樣的話,你可以在 Manager 失敗的時候不用關(guān)機維護,我們給出如下的建議:

  • 3 個 Manager 節(jié)點最多可以同時容忍 1 個 Manager 節(jié)點失效的情況下保證高可用;
  • 5 個 Manager 節(jié)點最多可以同時容忍 2 個 Manager 節(jié)點失效的情況下保證高可用;
  • N 個 Manager 節(jié)點最多可以同時容忍 (N?1)/2個 Manager 節(jié)點失效的情況下保證高可用;
  • Docker 建議最多最多的情況下,使用 7 個 Manager 節(jié)點就夠了,否則反而會降低集群的性能了。

2.2 Worker Node

worker 節(jié)點接收由manager節(jié)點調(diào)度并指派的task,啟動Docker容器來運行指定的服務(wù),并且worker 節(jié)點需要向manager節(jié)點匯報被指派的task的執(zhí)行狀態(tài)。

2.3 更換角色

通過 docker node promote命令將一個worker節(jié)點提升為manager節(jié)點,通常情況下,該命令使用在維護的過程中。若需將manager節(jié)點暫時下線進行維護操作,同樣可以使用docker node demote將某個 manager 節(jié)點降級為 worker 節(jié)點。

2.2 設(shè)計架構(gòu)

swarm-architecture-1.jpg

從前文可知, Swarm 集群的管理工作是由manager節(jié)點實現(xiàn)。如上圖所示,manager節(jié)點實現(xiàn)的功能主要包括:節(jié)點發(fā)現(xiàn),調(diào)度,集群管理等。同時,為了保證Manager節(jié)點的高可用,Manager節(jié)點需要時刻維護和保存當前 Swarm 集群中各個節(jié)點的一致性狀態(tài)。在保證一致性上,Manager節(jié)點采用 Raft 協(xié)議來保證分布式場景下的數(shù)據(jù)一致性;

Docker Swarm內(nèi)置Raft一致性算法,可以保證分布式系統(tǒng)的數(shù)據(jù)保持一致性同步。Etcd, Consul等高可用鍵值存儲系統(tǒng)也是采用了這種算法。這個算法的作用簡單點說就是隨時保證集群中有一個Leader,由Leader接收數(shù)據(jù)更新,再同步到其他各個Follower節(jié)點。在Swarm中的作用表現(xiàn)為當一個Leader 節(jié)點 down掉時,系統(tǒng)會立即選取出另一個Leader節(jié)點,由于這個節(jié)點同步了之前節(jié)點的所有數(shù)據(jù),所以可以無縫地管理集群。

Raft的詳細解釋可以參考《The Secret Lives of Data--Raft: Understandable Distributed Consensus》

2.2.1 跨主機容器通信

Docker Swarm 內(nèi)置的跨主機容器通信方案是overlay網(wǎng)絡(luò),這是一個基于vxlan協(xié)議的網(wǎng)絡(luò)實現(xiàn)。vxlan將二層數(shù)據(jù)封裝到 UDP 進行傳輸,vxlan 提供與 vlan 相同的以太網(wǎng)二層服務(wù),但是擁有更強的擴展性和靈活性。 overlay 通過虛擬出一個子網(wǎng),讓處于不同主機的容器能透明地使用這個子網(wǎng)。所以跨主機的容器通信就變成了在同一個子網(wǎng)下的容器通信,看上去就像是同一主機下的bridge網(wǎng)絡(luò)通信。

為支持容器跨主機通信,Docker 提供了 overlay driver,使用戶可以創(chuàng)建基于 vxlan 的 overlay 網(wǎng)絡(luò)。其實,docker 會創(chuàng)建一個 bridge 網(wǎng)絡(luò) “docker_gwbridge”,為所有連接到 overlay 網(wǎng)絡(luò)的容器提供訪問外網(wǎng)的能力。

查看網(wǎng)絡(luò)信息:docker network inspect docker_gwbridge

overlay-gwbridge.jpg

下面我們討論下overlay 網(wǎng)絡(luò)的具體實現(xiàn):

docker 會為每個 overlay 網(wǎng)絡(luò)創(chuàng)建一個獨立的 network namespace,其中會有一個 linux bridge br0,endpoint 還是由 veth pair 實現(xiàn),一端連接到容器中(即 eth0),另一端連接到 namespace 的 br0 上。

br0 除了連接所有的 endpoint,還會連接一個 vxlan 設(shè)備,用于與其他 host 建立 vxlan tunnel。容器之間的數(shù)據(jù)就是通過這個 tunnel 通信的。邏輯網(wǎng)絡(luò)拓撲結(jié)構(gòu)如圖所示:

overlay.jpg

2.2.2 服務(wù)發(fā)現(xiàn)

docker Swarm mode下會為每個節(jié)點的docker engine內(nèi)置一個DNS server,各個節(jié)點間的DNS server通過control plane的gossip協(xié)議互相交互信息。此DNS server用于容器間的服務(wù)發(fā)現(xiàn)。swarm mode會為每個 --net=自定義網(wǎng)絡(luò)的service分配一個DNS entry。目前必須是自定義網(wǎng)絡(luò),比如overaly。而bridge和routing mesh的service,是不會分配DNS的。

那么,下面就來詳細介紹服務(wù)發(fā)現(xiàn)的原理。

每個Docker容器都有一個DNS解析器,它將DNS查詢轉(zhuǎn)發(fā)到docker engine,該引擎充當DNS服務(wù)器。docker 引擎收到請求后就會在發(fā)出請求的容器所在的所有網(wǎng)絡(luò)中,檢查域名對應(yīng)的是不是一個容器或者是服務(wù),如果是,docker引擎就會從存儲的key-value建值隊中查找這個容器名、任務(wù)名、或者服務(wù)名對應(yīng)的IP地址,并把這個IP地址或者是服務(wù)的虛擬IP地址返回給發(fā)起請求的域名解析器。

由上可知,docker的服務(wù)發(fā)現(xiàn)的作用范圍是網(wǎng)絡(luò)級別,也就意味著只有在同一個網(wǎng)絡(luò)上的容器或任務(wù)才能利用內(nèi)置的DNS服務(wù)來相互發(fā)現(xiàn),不在同一個網(wǎng)絡(luò)里面的服務(wù)是不能解析名稱的,另外,為了安全和性能只有當一個節(jié)點上有容器或任務(wù)在某個網(wǎng)絡(luò)里面時,這個節(jié)點才會存儲那個網(wǎng)絡(luò)里面的DNS記錄。

如果目的容器或服務(wù)和源容器不在同一個網(wǎng)絡(luò)里面,Docker引擎會把這個DNS查詢轉(zhuǎn)發(fā)到配置的默認DNS服務(wù) 。

service-dns.png

在上面的例子中,總共有兩個服務(wù)myservice和client,其中myservice有兩個容器,這兩個服務(wù)在同一個網(wǎng)里面。在客戶端里針對docker.com和myservice各執(zhí)行了一個curl操作,下面時執(zhí)行的流程:

  • 為了客戶端解析docker.com和myservice,DNS查詢進行初始化
  • 容器內(nèi)建的解析器在127.0.0.11:53攔截到這個DNS查詢請求,并把請求轉(zhuǎn)發(fā)到docker引擎的DNS服務(wù)
  • myservice被解析成服務(wù)對應(yīng)的虛擬IP(10.0.0.3),在接下來的內(nèi)部負載均衡階段再被解析成一個具體任務(wù)的IP地址。如果是容器名稱這一步直接解析成容器對應(yīng)的IP地址(10.0.0.4或者10.0.0.5)。
  • docker.com在mynet網(wǎng)絡(luò)上不能被解析成服務(wù),所以這個請求被轉(zhuǎn)發(fā)到配置好的默認DNS服務(wù)器(8.8.8.8)上。

2.2.3 負載均衡

負載均衡分為兩種:Swarm集群內(nèi)的service之間的相互訪問需要做負載均衡,稱為內(nèi)部負載均衡(Internal LB);從Swarm集群外部訪問服務(wù)的公開端口,也需要做負載均衡,稱外部部負載均衡(Exteral LB or Ingress LB)。

  • Internal LB

內(nèi)部負載均衡就是我們在上一段提到的服務(wù)發(fā)現(xiàn),集群內(nèi)部通過DNS訪問service時,Swarm默認通過VIP(virtual IP)、iptables、IPVS轉(zhuǎn)發(fā)到某個容器。

intelnal-lb.jpg

當在docker swarm集群模式下創(chuàng)建一個服務(wù)時,會自動在服務(wù)所屬的網(wǎng)絡(luò)上給服務(wù)額外的分配一個虛擬IP,當解析服務(wù)名字時就會返回這個虛擬IP。對虛擬IP的請求會通過overlay網(wǎng)絡(luò)自動的負載到這個服務(wù)所有的健康任務(wù)上。這個方式也避免了客戶端的負載均衡,因為只有單獨的一個IP會返回給客戶端,docker會處理虛擬IP到具體任務(wù)的路由,并把請求平均的分配給所有的健康任務(wù)。

# 創(chuàng)建overlay網(wǎng)絡(luò):mynet 
$ docker network create -d overlay mynet  
a59umzkdj2r0ua7x8jxd84dhr 
# 利用mynet網(wǎng)絡(luò)創(chuàng)建myservice服務(wù),并復(fù)制兩份  
$ docker service create --network mynet --name myservice --replicas 2 busybox ping localhost  
78t5r8cr0f0h6k2c3k7ih4l6f5
# 通過下面的命令查看myservice對應(yīng)的虛擬IP 
$ docker service inspect myservice  
...
"VirtualIPs": [ 
    {  
     "NetworkID": "a59umzkdj2r0ua7x8jxd84dhr",  
                "Addr": "10.0.0.3/24"  
      },  
]   

注:swarm中服務(wù)還有另外一種負載均衡技術(shù)可選,DNS round robin (DNS RR) (在創(chuàng)建服務(wù)時通過--endpoint-mode配置項指定),在DNSRR模式下,docker不再為服務(wù)創(chuàng)建VIP,docker DNS 服務(wù)直接利用輪詢的策略把服務(wù)名稱直接解析成其中一個容器的IP地址。

  • Exteral LB(Ingress LB 或者 Swarm Mode Routing Mesh)

看名字就知道,這個負載均衡方式和前面提到的Ingress網(wǎng)絡(luò)有關(guān)。Swarm 網(wǎng)絡(luò)要提供對外訪問的服務(wù)就需要打開公開端口,并映射到宿主機。Ingress LB 就是外部通過公開端口訪問集群時做的負載均衡。

當創(chuàng)建或更新一個服務(wù)時,你可以利用 --publish 選項把一個服務(wù)暴露到外部,在 docker swarm 模式下發(fā)布一個端口意味著在集群中的所有節(jié)點都會監(jiān)聽這個端口,當訪問一個監(jiān)聽了端口但是并沒有對應(yīng)任務(wù)運行在其上的節(jié)點會發(fā)生什么呢?

接下來就該我們的路由網(wǎng)(routing mesh)出場了,路由網(wǎng)是 docker1.12 引入的一個新特性,它結(jié)合了 IPVS 和 iptables 創(chuàng)建了一個強大的集群范圍的4層負載均衡,它使所有節(jié)點接收來自服務(wù)暴露端口的請求成為可能。當任意節(jié)點接收到針對某個服務(wù)暴露的 TCP/UDP 端口的請求時,這個節(jié)點會利用預(yù)先定義過的 Ingress overlay 網(wǎng)絡(luò),將請求轉(zhuǎn)發(fā)給服務(wù)對應(yīng)的虛擬IP。ingress 網(wǎng)絡(luò)和其他的 overlay 網(wǎng)絡(luò)一樣,只是它的目的是為了轉(zhuǎn)換來自客戶端到集群的請求,它也是利用我們前一小節(jié)介紹過的基于 VIP 的負載均衡技術(shù)。

啟動服務(wù)后,你可以為應(yīng)用程序創(chuàng)建外部DNS記錄,并將其映射到任何或所有 Docker swarm 節(jié)點。你無需擔心你的容器具體運行在那個節(jié)點上,因為有了路由網(wǎng)這個特性后,你的集群看起來就像是單獨的一個節(jié)點一樣。

#在集群中創(chuàng)建一個副本數(shù)量為2的服務(wù),并暴露在8000端口  
$ docker service create --name app --replicas 2 --network appnet --publish 8000:80 nginx  
external-routing-mesh.png

上圖展示路由網(wǎng)是怎么工作的:

  • 服務(wù)(app)擁有兩個副本,并把端口映射到外部端口的8000
  • 路由網(wǎng)在集群中的所有節(jié)點上都暴露出8000
  • 外部對服務(wù)app的請求可以是任意節(jié)點,在本例子中外部的負載均衡器將請求轉(zhuǎn)發(fā)到了沒有 app 服務(wù)的主機上
  • docker swarm 的 IPVS 利用ingress overlay 網(wǎng)絡(luò)將請求重新轉(zhuǎn)發(fā)到運行著 app 服務(wù) task 的容器中

注:以上服務(wù)發(fā)現(xiàn)和負載均衡參考文檔 https://success.docker.com/article/ucp-service-discovery

2.3 Services 架構(gòu)

在微服務(wù)部署的過程中,通常將某一個微服務(wù)封裝為 service 在 Swarm 中部署執(zhí)行,通常你需要通過指定容器 image 以及需要在容器中執(zhí)行的 commands 來創(chuàng)建你的 service,除此之外,通常還需要配置如下選項,

  • 指定可在 Swarm 外部可以被訪問的服務(wù)端口號 port
  • 指定加入某個 overlay 網(wǎng)絡(luò)以便 service 與 service 之間可以建立連接并通訊
  • 指定該 service 所要使用的 CPU 和 內(nèi)存的大小
  • 指定一個滾動更新的策略(Rolling Update Policy)
  • 指定 service 的副本數(shù)量(replicas)

2.3.1 服務(wù)和任務(wù)

services-diagram.png

services,tasks 和 containers 之間的關(guān)系可以用上圖來描述。來看這張圖的邏輯,表示用戶想要通過 manager 節(jié)點部署一個副本數(shù)為3的 nginx 的 service,manager 節(jié)點接收到用戶的 service definition 后,便開始對該 service 進行調(diào)度,將會在可用的 worker(或者manager )節(jié)點中啟動相應(yīng)的 tasks 以及相關(guān)的副本;可以看到,service 實際上是 task 的定義,而 task 則是執(zhí)行在節(jié)點上的程序。

task 是什么呢?其實就是一個 container,只是在 Swarm 中,每個 task 都有自己的名字和編號,如圖,比如 nginx.1、nginx.2 和 nginx.3,這些 container 各自運行在各自的節(jié)點上,當然,一個節(jié)點可以運行多個 container;

2.3.2 任務(wù)調(diào)度

Docker swarm mode 模式的底層技術(shù)實際上就是指的是調(diào)度器( scheduler )和編排器( orchestrator );下面這張圖展示了 Swarm mode 如何從一個 service 的創(chuàng)建請求到成功將該 service 分發(fā)到兩個 worker 節(jié)點上執(zhí)行的過程。

swarm-service-lifecycle.png
  • 首先,看上半部分 Swarm manager

    1. 用戶通過 Docker Engine Client 使用命令 docker service create 提交 service definition
    2. 根據(jù) service definition 創(chuàng)建相應(yīng)的 task
    3. 為 task 分配 IP 地址(注意,這是分配運行在 Swarm 集群中 container 的 IP 地址,該 IP 地址最佳的分配地點是在這里,因為 manager 節(jié)點上保存有最新最全的 tasks 的狀態(tài)信息,為了保證不與其他的 Task 分配到相同的 IP,所以在這里就將 IP 地址給初始化好)
    4. 將task 分發(fā)到 worker 節(jié)點或者manager 節(jié)點
    5. worker 節(jié)點進行相應(yīng)的初始化使得它可以執(zhí)行 task
  • 接著,看下半部分 Swarm Worker
    該部分就相對簡單許多

    1. 首先連接 manager 的分配器( scheduler)檢查該 task
    2. 驗證通過以后,便開始通過 worker 節(jié)點上的執(zhí)行器( executor )執(zhí)行

注意,上述 task 的執(zhí)行過程是一種單向機制,它會按順序的依次經(jīng)歷 assigned, prepared 和 running 等執(zhí)行狀態(tài),不過在某些特殊情況下,在執(zhí)行過程中,某個 task 失敗了( fails ),編排器( orchestrator )直接將該 task 以及它的 container 刪除掉,然后在其它節(jié)點上另外創(chuàng)建并執(zhí)行該 task;

2.3.3 調(diào)度策略

Swarm 在 scheduler 節(jié)點(leader節(jié)點)運行容器的時候,會根據(jù)指定的策略來計算最適合運行容器的節(jié)點,目前支持的策略有:spread, binpack, random

1)Random 顧名思義,就是隨機選擇一個節(jié)點來運行容器,一般用作調(diào)試用,Spread 和 Binpack 策略會根據(jù)各個節(jié)點的可用的CPU, RAM以及正在運 行的容器的數(shù)量來計算應(yīng)該運行容器的節(jié)點

2)Spread 在同等條件下,Spread 策略會選擇存活容器數(shù)量最少的那臺節(jié)點來運行新的容器,Binpack策略會選擇運行容器最集中的那臺機器來運行新的節(jié)點。 使用Spread策略會使得容器會均衡的分布在集群中的各節(jié)點上,一個節(jié)點掛掉了只會損失少部分的容器

3)Binpack 策略最大化的避免容器碎片化,就是說 Binpack 策略盡可能的把還未使用的節(jié)點留給需要更大空間的容器運行,盡可能的把容器運行在 一個節(jié)點上面

2.3.4 服務(wù)副本與全局服務(wù)

Docker Swarm支持服務(wù)的擴容縮容,Swarm 通過 --mode 選項設(shè)置服務(wù)類型;提供了兩種模式:一種是 replicated,我們可以指定服務(wù) task 的數(shù)量(也就是需要創(chuàng)建幾個冗余副本),是Swarm默認使用的服務(wù)類型;第二種是 global,會在Swarm集群的每個節(jié)點上都創(chuàng)建一個服務(wù) task。如下圖所示(出自Docker官網(wǎng)),是一個包含replicated和global模式的Swarm集群:

docker-swarm-replicated-vs-global.png

上圖中,黃色表示的 replicated 模式下的 service replicas,灰色表示 global 模式下 service 的分布。

在Swarm mode下使用 Docker,可以部署服務(wù)、服務(wù)擴容縮容、刪除服務(wù)、滾動更新,下面我們詳細說明

3.集群搭建

3.1 環(huán)境配置

虛擬機信息

IP hostname 配置 角色
172.21.111.56 swarm01 4CPU/6G Manager
172.21.111.57 swarm02 4CPU/6G Manager
172.21.111.58 swarm03 4CPU/6G Manager

為驗證后續(xù)的swarm manager 高可用,將這三臺節(jié)點都配置成為Manager節(jié)點。

3.2 前置準備

3.2.1 修改主機名

 hostnamectl --static --transient set-hostname swarm01

修改/etc/hosts 。三臺主機上均設(shè)置一致

[root@swarm01 ~]# cat /etc/hosts
127.0.0.1   swarm-manager localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.21.111.56 swarm01
172.21.111.57 swarm02
172.21.111.58 swarm03

3.2.2 配置互信

ssh-keygen -t rsa
ssh-copy-id -i .ssh/id_rsa.pub 172.21.111.57
ssh-copy-id -i .ssh/id_rsa.pub 172.21.111.58

3.2.3 關(guān)閉防火墻和SELinux

systemctl disable firewalld
systemctl stop firewalld

修改 /etc/selinux/config ,修改配置項 SELINUX=enforcing 為 disabled

3.2 安裝Docker服務(wù)

從 v1.12 開始,Docker Swarm 的功能已經(jīng)完全與 Docker Engine 集成,要管理集群,只需要啟動 Swarm Mode。安裝好 Docker,Swarm 就已經(jīng)在那里了,服務(wù)發(fā)現(xiàn)也在那里了(不需要安裝 Consul 等外部數(shù)據(jù)庫)。配置好yum 源之后,便可安裝 docker-ce。

yum install -y docker-ce
systemctl enable docker
systemctl start docker

3.3 集群初始化

3.2.1 初始化主節(jié)點

使用命令 docker swarm init 初始化當前節(jié)點為主節(jié)點,如果主機上有多張網(wǎng)卡的時候,需指定網(wǎng)卡 docker swarm init --advertise-addr [IP] --listen-addr [IP:PORT]

節(jié)點自動解鎖:docker swarm init --autolock=false

[root@swarm01 ~]# docker swarm init --autolock=false  --advertise-addr 172.21.111.56 --listen-addr 172.21.111.56:2377 

Swarm initialized: current node (epoic1y0vv830vnwbc6nnacjc) is now a manager.
To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5t9jv2o6z3ep9mk5c2ae41bpqojla1g8cfjo3qlsuj2sxi934l-af2x3lh8vs6dnpepy66kdw5mv 172.21.111.56:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

添加集群有兩種方式,一種是以 manager 節(jié)點的方式添加,一種則是以 worker 節(jié)點方式添加。默認輸出只給出了添加worker 的命令,添加 manager 命令則需要通過 docker swarm join-token manager 獲取。

[root@swarm01 ~]# docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5t9jv2o6z3ep9mk5c2ae41bpqojla1g8cfjo3qlsuj2sxi934l-eszh52z00ktao9j7lovfvjgqy 172.21.111.56:2377

3.2.2 添加manager 節(jié)點

分別登陸 swarm02、swarm03,執(zhí)行上一節(jié)得到的命令。

[root@swarm03 ~]#docker swarm join --token SWMTKN-1-5t9jv2o6z3ep9mk5c2ae41bpqojla1g8cfjo3qlsuj2sxi934l-eszh52z00ktao9j7lovfvjgqy 172.21.111.56:2377
This node joined a swarm as a manager.

注:不要在節(jié)點上配置http代理,否則會添加節(jié)點失敗。

3.2.3 離開集群

如果想退出集群,可以在該節(jié)點上直接退出。

[root@swarm03 ~]# docker swarm leave
[root@swarm03 ~]# docker swarm leave --force 

3.2.4 查看集群節(jié)點信息

登陸任意一臺Manager節(jié)點,都可以查看當前集群的節(jié)點信息

[root@swarm02 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc     swarm01             Ready               Active              Leader              18.03.1-ce
nsqqv181e0r7mu77azixtqhzl *   swarm02             Ready               Active              Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c     swarm03             Ready               Active              Reachable           18.03.1-ce

上面信息中,AVAILABILITY 表示 Swarm Scheduler 是否可以向集群中的某個節(jié)點指派 task,對應(yīng)有如下三種狀態(tài):

  • Active:集群中該節(jié)點可以被指派task
  • Pause:集群中該節(jié)點不可以被指派新的task,但是已存在的 task 將被保留
  • Drain:集群中該節(jié)點不可以被指派新的task,Swarm Scheduler 停掉已存在的 task,并將它們調(diào)度到可用的節(jié)點

另外,可以看到Manager Status狀態(tài)也有分類,分別是Leader、Reacheable、Unreachable。

  • Leader:對應(yīng) Raf t算法的 Leader 節(jié)點
  • Reacheable:對應(yīng) Raft 算法的 Flower 節(jié)點,網(wǎng)絡(luò)可達、可用的 Flower 節(jié)點
  • Unreachable:對應(yīng) Raft 算法的 Flower 節(jié)點,網(wǎng)絡(luò)不可達、不可用的 Flower 節(jié)點

查看單個節(jié)點詳細信息

[root@swarm02 ~]# docker node inspect swarm01

4.高可用實踐

4.1 應(yīng)用服務(wù)管理

docker swarm 還可以使用 stack 這一模型,用來部署管理和關(guān)聯(lián)不同的服務(wù)。stack 功能比 service 還要強大,因為它具備多服務(wù)編排部署的能力。stack file 是一種 yaml 格式的文件,類似于 docker-compose.yml 文件,它定義了一個或多個服務(wù),并定義了服務(wù)的環(huán)境變量、部署標簽、容器數(shù)量以及相關(guān)的環(huán)境特定配置等。

以下實驗僅利用stack 創(chuàng)建單一服務(wù)

4.1.1 創(chuàng)建服務(wù)

服務(wù)配置文件docker-compose.yml ,內(nèi)容如下

version: '3'
services:
  web:
    image: "httpd:v1"
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
      restart_policy:
        condition: on-failure
    ports:
     - "7080:80"
    volumes:
     - /var/www/html:/usr/local/apache2/htdocs

image: "httpd:v1" :httpd 是 docker hub 下載的鏡像。提供簡單的 httpd 服務(wù),此處用來做服務(wù)高可用驗證

replicas: 3 表示該服務(wù)副本數(shù)量為3,可對服務(wù)副本進行擴縮容,后續(xù)會講到。

/var/www/html:/usr/local/apache2/htdocs :通過掛載的方式,將宿主機的 /var/www/html 目錄掛載到容器中的 /usr/local/apache2/htdocs 目錄

在 docker-compose.yml 同級目錄下,執(zhí)行命令

[root@swarm01 swarm]# docker stack deploy -c docker-compose.yml myservice
Creating network myservice_default
Creating service myservice_web

4.1.2 查看服務(wù)

查看服務(wù)創(chuàng)建是否成功。

[root@swarm01 swarm]# docker stack ls
NAME                SERVICES
myservice           1
[root@swarm01 swarm]# docker stack services myservice
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
kom8x49f7qv2        myservice_web       replicated          3/3                 httpd:latest        *:7080->80/tcp
[root@swarm01 swarm]# docker stack ps myservice
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
sybn1dur1wmw        myservice_web.1     httpd:latest        swarm03             Running             Running 45 seconds ago
mlcb5r9vzv6z        myservice_web.2     httpd:latest        swarm03             Running             Running 45 seconds ago
fw21lcu3zp83        myservice_web.3     httpd:latest        swarm01             Running             Running 45 seconds ago

可以看到 swarm01 上面運行了1個容器,swarm03 上面運行了2個,而 swarm02 上面一個容器都沒有,這是咋回事呢?哦,原來我們在創(chuàng)建集群過程中,對 swarm02 做了狀態(tài)變更的操作,docker node update --availability drain swarm02

[root@swarm01 swarm]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc *   swarm01             Ready               Active              Leader              18.03.1-ce
nsqqv181e0r7mu77azixtqhzl     swarm02             Ready               Drain               Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c     swarm03             Ready               Active              Reachable           18.03.1-ce

此時打開瀏覽器,輸入HTTP URL。任意swarm集群節(jié)點都可以訪問httpd服務(wù)。

由于3個宿主機掛載的 /var/www/html 路徑未做共享存儲,訪問到此 service 中不同容器會顯示不同的網(wǎng)頁內(nèi)容

4.1.3 服務(wù)擴縮容

修改docker-commpose.yml 配置字段 replicas: 5 ,將容器個數(shù)提升到5個。由于實驗環(huán)境資源有限,因此這里將swarm02狀態(tài)變更為可以狀態(tài),用以分配容器。

docker node update --availability active swarm02

重新執(zhí)行部署命令

docker stack deploy -c docker-compose.yml myservice

也可以通過命令直接對服務(wù)進行操作,但是不推薦

docker service scale 服務(wù)ID=N
docker service update --replicas N 服務(wù)ID

[root@swarm01 swarm]# docker stack ps myservice
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                    ERROR               PORTS
unwepav5z9nz        myservice_web.1     httpd:v1            swarm02             Running             Running less than a second ago
3j5o1tktcg1l        myservice_web.2     httpd:v1            swarm01             Running             Preparing 3 seconds ago
wl1unt6lynft        myservice_web.3     httpd:v1            swarm03             Running             Running less than a second ago
[root@swarm01 swarm]# vi docker-compose.yml
[root@swarm01 swarm]# docker stack deploy -c docker-compose.yml myservice
Updating service myservice_web (id: 85quzwi5k0btkn5wlht4jbco9)
image httpd:v1 could not be accessed on a registry to record
its digest. Each node will access httpd:v1 independently,
possibly leading to different nodes running different
versions of the image.

[root@swarm01 swarm]# docker stack ps myservice
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                    ERROR               PORTS
unwepav5z9nz        myservice_web.1     httpd:v1            swarm02             Running             Running 24 seconds ago
3j5o1tktcg1l        myservice_web.2     httpd:v1            swarm01             Running             Running 31 seconds ago
wl1unt6lynft        myservice_web.3     httpd:v1            swarm03             Running             Running 24 seconds ago
ujedjxbuffxp        myservice_web.4     httpd:v1            swarm01             Running             Running less than a second ago
ml9qvjh2add7        myservice_web.5     httpd:v1            swarm02             Running             Running less than a second ago

4.1.4 刪除服務(wù)

例如,刪除 myredis 應(yīng)用服務(wù),執(zhí)行 docker service rm myredis,則應(yīng)用服務(wù) myredis 的全部副本都會被刪除

本例使用的是 stack ,刪除 stack ,即可刪除其下的服務(wù)

docker stack rm myservice

需要注意的是,對swarm上面服務(wù)的操作,都必須在 manager 節(jié)點上運行

4.1.5 服務(wù)更新

創(chuàng)建服務(wù),在 manager 節(jié)點上執(zhí)行如下命令:

docker service create --replicas 3  --name redis --update-delay 10s redis:3.0.6

上面通過指定 --update-delay 選項,更新服務(wù)時,成功部署一個 task,等待10秒鐘,然后再部署下一個 task。如果某個服務(wù)更新失敗,則 Swarm 的調(diào)度器就會暫停本次服務(wù)的部署更新。

另外,也可以更新已部署服務(wù)的 image 版本,執(zhí)行如下命令:

將 redis 服務(wù)對應(yīng)的 image 版本3.0.6 更新為 3.0.7,同樣,如果更新失敗,則暫停本次更新。

那么,stack file 如何定義滾動更新呢?

      update_config:
        parallelism: 2
        delay: 10s

修改docker-compose.yml 如下

version: '3'
services:
  web:
    image: "httpd:v2"
    deploy:
      replicas: 3
      update_config:
        parallelism: 2
        delay: 30s
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
      restart_policy:
        condition: on-failure
    ports:
     - "7080:80"
    volumes:
     - /var/www/html:/usr/local/apache2/htdocs

parallelism 表示同時升級的個數(shù),delay 則表示間隔多長時間升級,同時將httpd 由v1 升級到v2,這里我們使用同一個鏡像,只是tag 修改了一下

查看服務(wù)狀態(tài)

[root@swarm01 swarm]# docker stack ps myservice
ID                  NAME                  IMAGE               NODE                DESIRED STATE       CURRENT STATE                 ERROR               PORTS
qk8pvvc9nmv0        myservice_web.1       httpd:v2            swarm01             Running             Running 56 seconds ago
unwepav5z9nz         \_ myservice_web.1   httpd:v1            swarm02             Shutdown            Shutdown 48 seconds ago
uyj0pm1df9hl        myservice_web.2       httpd:v2            swarm02             Running             Running 46 seconds ago
3j5o1tktcg1l         \_ myservice_web.2   httpd:v1            swarm01             Shutdown            Shutdown about a minute ago
qdjov5zf5alb        myservice_web.3       httpd:v2            swarm03             Running             Running 10 seconds ago
wl1unt6lynft         \_ myservice_web.3   httpd:v1            swarm03             Shutdown            Shutdown 12 seconds ago

可以看到同時只有2個容器在升級,同時第3個容器也是在間隔了30s 之后,才開始升級

--filter 過濾條件

docker stack ps --filter "desired-state=running" myservice

4.2 應(yīng)用服務(wù)高可用

在前面創(chuàng)建的myservice的基礎(chǔ)上,進行容器應(yīng)用的高可用驗證。

4.2.1 模擬容器故障

查看當前服務(wù)狀態(tài)

[root@swarm01 swarm]# docker stack ps  --filter  "desired-state=running"  myservice
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
qk8pvvc9nmv0        myservice_web.1     httpd:v2            swarm01             Running             Running 16 minutes ago
uyj0pm1df9hl        myservice_web.2     httpd:v2            swarm02             Running             Running 16 minutes ago
qdjov5zf5alb        myservice_web.3     httpd:v2            swarm03             Running             Running 15 minutes ago

swarm01-03 分別運行1個容器?,F(xiàn)在登陸swam03節(jié)點,將其容器進程Kill 掉。模擬容器意外故障。

將stack ps 得到的swarm03節(jié)點的ID qdjov5zf5alb 代入命令inspect。

[root@swarm01 swarm]# docker inspect qdjov5zf5alb
[
    {
        "ID": "qdjov5zf5albdm1o7vhuwoxjt",
...
        "Spec": {
            "ContainerSpec": {
                "Image": "httpd:v2",
                "Labels": {
                    "com.docker.stack.namespace": "myservice"
                },
                "Privileges": {
                    "CredentialSpec": null,
                    "SELinuxContext": null
                },
                "Mounts": [
                    {
                        "Type": "bind",
                        "Source": "/var/www/html",
                        "Target": "/usr/local/apache2/htdocs"
                    }
                ],
                "Isolation": "default"
            },
            "Resources": {
                "Limits": {
                    "NanoCPUs": 500000000,
                    "MemoryBytes": 268435456
                }
            },
....
        "ServiceID": "85quzwi5k0btkn5wlht4jbco9",
        "Slot": 3,
        "NodeID": "mkkxaeqergz8xw21bbkd47q1c",
        "Status": {
            "Timestamp": "2018-08-07T09:02:25.674376822Z",
            "State": "running",
            "Message": "started",
            "ContainerStatus": {
                "ContainerID": "fa7d228e0c1981bfc78bca0b81363d7ca44cac99844c2996afe528ae3ae9a129",
                "PID": 8852,
                "ExitCode": 0
            },
            "PortStatus": {}
        },

登陸swarm03節(jié)點,查看容器fa7d22 對應(yīng)的進程。得到其進程號,并將其殺死。

[root@swarm03 /]# ps -ef|grep fa7d228e0c19
root      8832  1129  0 05:02 ?        00:00:00 docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fa7d228e0c1981bfc78bca0b81363d7ca44cac99844c2996afe528ae3ae9a129 -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runc
root      9989  1090  0 05:32 pts/0    00:00:00 grep --color=auto fa7d228e0c19
[root@swarm03 /]# kill -9 8832

4.2.2 故障查看

立即查看服務(wù)狀態(tài)。

[root@swarm03 /]# docker stack ps myservice
ID                  NAME                  IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR                         PORTS
qk8pvvc9nmv0        myservice_web.1       httpd:v2            swarm01             Running             Running 32 minutes ago
unwepav5z9nz         \_ myservice_web.1   httpd:v1            swarm02             Shutdown            Shutdown 32 minutes ago
uyj0pm1df9hl        myservice_web.2       httpd:v2            swarm02             Running             Running 32 minutes ago
3j5o1tktcg1l         \_ myservice_web.2   httpd:v1            swarm01             Shutdown            Shutdown 32 minutes ago
hx741f0kye5a        myservice_web.3       httpd:v2            swarm03             Running             Running 29 seconds ago
qdjov5zf5alb         \_ myservice_web.3   httpd:v2            swarm03             Shutdown            Failed 36 seconds ago     "task: non-zero exit (137)"
wl1unt6lynft         \_ myservice_web.3   httpd:v1            swarm03             Shutdown            Shutdown 31 minutes ago

可以看到,swarm03上面的容器 myservice_web.3 立即被重啟,故障即刻恢復(fù)

4.2.3 結(jié)論

通過以上實驗我們得出,在 docker swarm 集群服務(wù)中,任意容器出現(xiàn)故障意外死亡,都會被重啟。滿足應(yīng)用服務(wù)高可用的需求。但是,可能會有同學(xué)有疑問,你這個只是kill掉進程,模擬的是進程級別的故障,如果是主機宕機呢,swarm是否也能滿足呢?答案是肯定的!

就讓我們繼續(xù)模擬主機級別的故障,驗證服務(wù)的高可用。同時,由于我們環(huán)境配置的均是 manager 節(jié)點,我們也可以同時驗證 Swarm Manager 的高可用

4.3 Swarm Manager 高可用

查看當前swarm集群節(jié)點信息

[root@swarm01 swarm]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc *   swarm01             Ready               Active              Leader              18.03.1-ce
nsqqv181e0r7mu77azixtqhzl     swarm02             Ready               Active              Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c     swarm03             Ready               Active              Reachable           18.03.1-ce

swarm01 為swarm 集群管理節(jié)點。倘若 swarm01 突然宕機關(guān)機會怎樣呢,整個swarm 集群會失效,陷入癱瘓嗎?其上運行的容器服務(wù)是否會全部掛掉,無法繼續(xù)提供服務(wù)?

4.3.1 模擬主機宕機

將虛擬機swarm01 直接關(guān)機。

4.3.2 故障查看

登陸swarm03 ,查看swarm集群狀態(tài)。

[root@swarm03 /]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc     swarm01             Unknown             Active              Unreachable         18.03.1-ce
nsqqv181e0r7mu77azixtqhzl     swarm02             Ready               Active              Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c *   swarm03             Ready               Active              Leader              18.03.1-ce

在經(jīng)歷短暫的間隙后,可看到 swarm 集群認定 swarm01 現(xiàn)在處于Unknown 狀態(tài),MANAGER STATUS 處于 Unreachable 狀態(tài)。同時,swarm 集群基于 raft 算法,重新推舉出新的 Leader 為 swarm03。

再過一段時間,swarm01 狀態(tài)變成 Down

[root@swarm03 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc     swarm01             Down                Active              Unreachable         18.03.1-ce
nsqqv181e0r7mu77azixtqhzl     swarm02             Ready               Active              Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c *   swarm03             Ready               Active              Leader              18.03.1-ce

查看服務(wù)狀態(tài)

[root@swarm03 /]# docker stack ps myservice
ID                  NAME                  IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR                         PORTS
zfbvcz5iu8g7        myservice_web.1       httpd:v2            swarm03             Running             Running 2 minutes ago
qk8pvvc9nmv0         \_ myservice_web.1   httpd:v2            swarm01             Shutdown            Running about an hour ago
unwepav5z9nz         \_ myservice_web.1   httpd:v1            swarm02             Shutdown            Shutdown about an hour ago
uyj0pm1df9hl        myservice_web.2       httpd:v2            swarm02             Running             Running 3 minutes ago
3j5o1tktcg1l         \_ myservice_web.2   httpd:v1            swarm01             Shutdown            Shutdown about an hour ago
hx741f0kye5a        myservice_web.3       httpd:v2            swarm03             Running             Running 3 minutes ago
qdjov5zf5alb         \_ myservice_web.3   httpd:v2            swarm03             Shutdown            Failed 21 minutes ago        "task: non-zero exit (137)"
wl1unt6lynft         \_ myservice_web.3   httpd:v1            swarm03             Shutdown            Shutdown about an hour ago

可以看到容器服務(wù)個數(shù)依然維持在3個,原先在 swarm01 上面運行的容器,被分配到 swarm02 和 swarm03 上面?,F(xiàn)在對服務(wù)進行擴容操作,以驗證此時swarm集群是否對服務(wù)還有管理能力。

[root@swarm03 swarm]# docker stack deploy -c docker-compose.yml myservice
Updating service myservice_web (id: 85quzwi5k0btkn5wlht4jbco9)
image httpd:v2 could not be accessed on a registry to record
its digest. Each node will access httpd:v2 independently,
possibly leading to different nodes running different
versions of the image.

[root@swarm03 swarm]# docker stack ps  myservice
ID                  NAME                  IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR                         PORTS
zfbvcz5iu8g7        myservice_web.1       httpd:v2            swarm03             Running             Running 6 minutes ago
qk8pvvc9nmv0         \_ myservice_web.1   httpd:v2            swarm01             Shutdown            Running about an hour ago
unwepav5z9nz         \_ myservice_web.1   httpd:v1            swarm02             Shutdown            Shutdown about an hour ago
uyj0pm1df9hl        myservice_web.2       httpd:v2            swarm02             Running             Running 6 minutes ago
3j5o1tktcg1l         \_ myservice_web.2   httpd:v1            swarm01             Shutdown            Shutdown about an hour ago
hx741f0kye5a        myservice_web.3       httpd:v2            swarm03             Running             Running 6 minutes ago
qdjov5zf5alb         \_ myservice_web.3   httpd:v2            swarm03             Shutdown            Failed 25 minutes ago        "task: non-zero exit (137)"
wl1unt6lynft         \_ myservice_web.3   httpd:v1            swarm03             Shutdown            Shutdown about an hour ago
6nifzsbysug5        myservice_web.4       httpd:v2            swarm02             Running             Running 20 seconds ago
uwgnl2usv59a        myservice_web.5       httpd:v2            swarm02             Running             Running 20 seconds ago

可以看到,此時 swarm 集群依然具有管理能力(服務(wù)擴容能力),也從側(cè)面驗證 swarm manager 的高可用能力。

此時重啟 swarm01 ,看看 swarm01 節(jié)點是否會自動加入集群。

登陸swarm01 ,查看集群狀態(tài)

[root@swarm01 ~]# docker node ls
Error response from daemon: Swarm is encrypted and needs to be unlocked before it can be used. Please use "docker swarm unlock" to unlock it.

可以看到,此時swarm01 處于被鎖的狀態(tài)。需要解鎖。

[root@swarm01 ~]# docker swarm unlock
Please enter unlock key:
Error response from daemon: invalid key string

那么 unlock key 從哪里取值呢?答案就是,需要在可用的集群管理節(jié)點,比如 swarm03 上面執(zhí)行命令

[root@swarm03 ~]# docker swarm unlock-key
To unlock a swarm manager after it restarts, run the `docker swarm unlock`
command and provide the following key:

    SWMKEY-1-796L7+nU55V/qGCMPgBvKS/5PyotvNaKPS4SJ0AUe+g

Please remember to store this key in a password manager, since without it you
will not be able to restart the manager.

通過該 unlock key : SWMKEY-1-796L7+nU55V/qGCMPgBvKS/5PyotvNaKPS4SJ0AUe+g ,重新加入集群。

此時在 swarm01 上面查看集群狀態(tài),即可看到已經(jīng)重新加入集群。

[root@swarm01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc *   swarm01             Ready               Active              Reachable           18.03.1-ce
nsqqv181e0r7mu77azixtqhzl     swarm02             Ready               Active              Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c     swarm03             Ready               Active              Leader              18.03.1-ce

但是如果再把 swarm03 關(guān)掉,即同時宕掉2個節(jié)點,則此時3個管理節(jié)點,只剩下一個節(jié)點,不滿足 Raft 協(xié)議的節(jié)點個數(shù)要求(>N/2),集群功能就會失效,不能再管理集群,但是集群上的服務(wù)還能夠繼續(xù)運行。

繼續(xù)我們的腳步,將swarm03,swarm01 都關(guān)機。

登陸swarm02,查看集群狀態(tài)

[root@swarm02 ~]# docker node ls
Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.
[root@swarm02 ~]# docker stack ps myservice
Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.

可以看到,此時集群功能已經(jīng)失效,不能通過 swarm 命令查看集群和管理集群了。那么運行其上的任務(wù)呢?

[root@swarm02 ~]# docker container ls
CONTAINER ID        IMAGE                COMMAND              CREATED             STATUS              PORTS                    NAMES
2cc1b76be2cf        httpd:v2             "httpd-foreground"   2 hours ago         Up 2 hours          80/tcp                   myservice_web.5.uwgnl2usv59aygb5e9a8cig2y
96034bff8902        httpd:v2             "httpd-foreground"   2 hours ago         Up 2 hours          80/tcp                   myservice_web.4.6nifzsbysug5medburznrrr7h
82beb10e4cac        httpd:v2             "httpd-foreground"   3 hours ago         Up 3 hours          80/tcp                   myservice_web.2.uyj0pm1df9hlx9vdvck36ilbl
ea9f879cdf12        composetest_web:v1   "python app.py"      3 weeks ago         Up 5 hours          0.0.0.0:5000->5000/tcp   elegant_borg

還好還好,通過 docker container ls 查看swarm02節(jié)點上的容器,可以看到仍然有3個容器在運行,這些都是之前已經(jīng)分配好的。顯然,swarm集群即使失效了,也不會影響其上運行的容器服務(wù)正常運行,只是失去了管理和調(diào)度的能力了。

此時,如果再次打開 swarm01 和 swarm03 的電源,他們會重新組建集群嗎?從上文中我們知道,離線的 manager 節(jié)點要重新加入集群需要解鎖,而鑰匙需要通過原集群運行 docker swarm unlock-key 獲取。那么顯然,原集群已經(jīng)失效了。那此時會怎樣呢?

不幸的是,確實swarm01 和 swarm03 都無法自動加入集群,確實需要通過docker swarm unlock進行解鎖。但是幸運的是,unlock key 是不變的,跟剛才 swarm01 宕機時重新加入的 key 是一樣的。分別在 swarm01 和 swarm03 執(zhí)行解鎖操作。集群最終恢復(fù)正常。因此需要在創(chuàng)建集群后,第一時間保存 unlock key

[root@swarm03 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc     swarm01             Ready               Active              Reachable           18.03.1-ce
nsqqv181e0r7mu77azixtqhzl     swarm02             Ready               Active              Leader              18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c *   swarm03             Ready               Active              Reachable           18.03.1-ce

注:在創(chuàng)建集群的時候,可設(shè)置自動解鎖,即主機重啟時,不用輸入密鑰,即可重新加入集群

docker swarm init --autolock=false
docker swarm update --autolock=false

4.3.3 結(jié)論

從以上實驗中也可以看出,只要管理節(jié)點正常,服務(wù)的高可用就能達到,但是管理節(jié)點的個數(shù)必須保持在總的管理節(jié)點個數(shù)的一半以上,即3個只允許宕機一臺,5臺只允許宕機2臺。而總的管理節(jié)點的個數(shù)一般不超過7個,如果太多的話,集群內(nèi)管理節(jié)點通信消耗會比較大。由于偶數(shù)性價比不高(因為4臺也只能宕機掉1臺跟3臺時是一樣的),所以管理節(jié)點的個數(shù)一般都是奇數(shù)。

管理節(jié)點的個數(shù)以及允許宕機的個數(shù)如下:

mannager node 允許宕機個數(shù) 服務(wù)運行狀態(tài)
3 1 正常
5 2 正常
7 3 正常

4.4 網(wǎng)絡(luò)故障轉(zhuǎn)移

4.4.1 模擬網(wǎng)絡(luò)分區(qū)

環(huán)境說明:仍然是3臺虛擬機組成的 swarm 管理集群

集群已使用 --autolock=false 重新初始化集群:

docker swarm init --autolock=false --advertise-addr 172.21.111.56 --listen-addr 172.21.111.56:2377

hd_hd-1服務(wù):運行4個副本,分別 swarm01 和 swarm02 各一個容器,swarm03 運行2個容器

將 swarm02 網(wǎng)卡拔掉,模擬網(wǎng)絡(luò)故障分區(qū)

4.4.2 觀察swarm node信息

查看集群信息和服務(wù)狀態(tài)

[root@swarm01 httpd]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
wk8jaypu23vr63082arpsl4ut *   swarm01             Ready               Active              Leader              18.03.1-ce
q9lcw91vus73wyly174iy72h3     swarm02             Down                Active              Unreachable         18.03.1-ce
1xelzfyr5kbn7kk70le45anei     swarm03             Ready               Active              Reachable           18.06.1-ce

[root@swarm01 httpd]# docker service ps hd_hd-1
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
t604bqtcj588        hd_hd-1.1           httpd:latest        swarm01             Running             Running 2 minutes ago
xfajbfz51nd9         \_ hd_hd-1.1       httpd:latest        swarm02             Shutdown            Running 8 minutes ago
8clt10jl1xyf        hd_hd-1.2           httpd:latest        swarm01             Running             Running 6 minutes ago
py5cavsog5mo        hd_hd-1.3           httpd:latest        swarm03             Running             Running 7 minutes ago
oigopryy1kro        hd_hd-1.4           httpd:latest        swarm03             Running             Running 7 minutes ago

我們可以發(fā)現(xiàn),集群認為 swarm02 現(xiàn)在已經(jīng)失聯(lián)(不論是宕機還是網(wǎng)絡(luò)端開)。現(xiàn)在 swarm02 上面的容器被遷移到swarm01 上面(調(diào)度策略應(yīng)該是 swarm01 上面目前運行的容器個數(shù)比 swarm03 少)。

通過虛擬機控制臺登陸 swarm02,驗證集群功能和容器是否在運行

swarm-network.png

我們可以發(fā)現(xiàn),由于斷網(wǎng),swarm02 當前是一個單獨節(jié)點,根據(jù) raft 算法,處于 swarm 集群功能不可用狀態(tài)。docker ps 查看容器,發(fā)現(xiàn)容器仍然在正常運行。得出結(jié)論,swarm 集群功能失效,但是不影響原先已經(jīng)運行的 docker 容器

綜合 swarm01/swarm03 上面的容器個數(shù)和 swarm02 上面的容器個數(shù),現(xiàn)在已經(jīng)有5個容器了。而我們預(yù)設(shè)的容器個數(shù)是4個。那么當 swarm02 重新加入集群,會發(fā)生什么事情呢?

4.4.3 網(wǎng)絡(luò)故障恢復(fù)

把 swarm02 網(wǎng)卡重新接上,swarm02 會自動加入集群,查看集群信息和服務(wù)信息。

[root@swarm01 httpd]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
lkivi02vx5z939z0x7lacuarl *   swarm01             Ready               Active              Leader              18.03.1-ce
pdp0awtf34o4x60x812h07a6e     swarm02             Ready               Active              Reachable           18.03.1-ce
qavrh9e6pjdq2aoxpucl6nohm     swarm03             Ready               Active              Reachable           18.06.1-ce
[root@swarm01 httpd]# docker service ps hd_hd-1
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
pepxi7bmzi8r        hd_hd-1.1           httpd:latest        swarm01             Running             Running 2 minutes ago
yf42dr6fqvl5         \_ hd_hd-1.1       httpd:latest        swarm02             Shutdown            Shutdown 2 minutes ago
j6iq7tpsnwb0        hd_hd-1.2           httpd:latest        swarm03             Running             Running 2 minutes ago
k23a3ezbocit        hd_hd-1.3           httpd:latest        swarm03             Running             Running 2 minutes ago
bqra1k9m0ojc        hd_hd-1.4           httpd:latest        swarm01             Running             Running 2 minutes ago

我們發(fā)現(xiàn) swarm02 已經(jīng)正常加入集群。登陸 swarm02,查看 swarm02 上面原先的容器也已經(jīng)停掉了,符合我們的預(yù)期

swarm-network-2.png

5.附錄

5.1參考文章:

docker swarm介紹:

http://shiyanjun.cn/archives/1625.html

https://jiayi.space/post/docker-swarmrong-qi-ji-qun-guan-li-gong-ju

http://www.shangyang.me/2018/02/01/docker-swarm-03-architect/

http://blog.51cto.com/cloudman/1952873

https://www.cnblogs.com/bigberg/p/8761047.html

服務(wù)發(fā)現(xiàn):https://success.docker.com/article/ucp-service-discovery

raft協(xié)議:http://thesecretlivesofdata.com/raft/

5.2 Swarm Node 節(jié)點操作

5.2.1 狀態(tài)變更

前面我們已經(jīng)提到過,節(jié)點的 AVAILABILITY 有三種狀態(tài):Active、Pause、Drain,對某個節(jié)點進行變更,可以將其AVAILABILITY 值通過 Docker CLI 修改為對應(yīng)的狀態(tài)即可,下面是常見的變更操作:

  • 設(shè)置 manager 節(jié)點只具有管理功能
  • 對服務(wù)進行停機維護,可以修改 AVAILABILITYDrain 狀態(tài)
  • 暫停一個節(jié)點,然后該節(jié)點就不再接收新的 task
  • 恢復(fù)一個不可用或者暫停的節(jié)點

例如,將 manager 節(jié)點的 AVAILABILITY 值修改為Drain 狀態(tài),使其只具備管理功能,執(zhí)行如下命令:

[root@swarm02 ~]# docker node update  --availability drain  swarm02
swarm02
[root@swarm02 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
epoic1y0vv830vnwbc6nnacjc     swarm01             Ready               Active              Leader              18.03.1-ce
nsqqv181e0r7mu77azixtqhzl *   swarm02             Ready               Drain               Reachable           18.03.1-ce
mkkxaeqergz8xw21bbkd47q1c     swarm03             Ready               Active              Reachable           18.03.1-ce

這樣,manager 節(jié)點不能被指派 task,也就是不能部署實際的Docker容器來運行服務(wù),而只是作為管理節(jié)點的角色。

5.2.2 添加標簽

每個節(jié)點的主機配置情況可能不同,比如有的適合運行 CPU 密集型應(yīng)用,有的適合運行 IO 密集型應(yīng)用,Swarm 支持給每個節(jié)點添加標簽元數(shù)據(jù),這樣可以根據(jù)節(jié)點的標簽,來選擇性地調(diào)度某個服務(wù)部署到期望的一組節(jié)點上。 給 Swarm 集群中的某個 worker 節(jié)點添加標簽,執(zhí)行如下命令格式如下:

[root@swarm02 ~]# docker node update  --label-add  app=dev swarm02
swarm02

5.2.3 提權(quán)/降權(quán)

改變節(jié)點的角色,worker 節(jié)點可以變?yōu)?manager 節(jié)點,manager 節(jié)點也可以變worker 節(jié)點,對應(yīng)操作分別是:

# 將一個工作節(jié)點升為管理節(jié)點
docker node demote swarm02

# 將一個管理節(jié)點降為工作節(jié)點
docker node promote swarm02

5.3 網(wǎng)絡(luò)管理

5.3.1 添加overlay 網(wǎng)絡(luò)

overlay 網(wǎng)絡(luò)是 swarm 中默認的跨主機網(wǎng)絡(luò)模型,在 Swarm 集群中可以使用 overlay 網(wǎng)絡(luò)來連接到一個或多個服務(wù)。首先,我們需要創(chuàng)建在 manager 節(jié)點上創(chuàng)建一個 overlay 網(wǎng)絡(luò),執(zhí)行如下命令:

docker network create --driver overlay my-network

創(chuàng)建完 overlay 網(wǎng)絡(luò) my-network 以后,Swarm集群中所有的 manager 節(jié)點都可以訪問該網(wǎng)絡(luò)。然后,我們在創(chuàng)建服務(wù)的時候,只需要指定使用的網(wǎng)絡(luò)為已存在的 overlay 網(wǎng)絡(luò)即可,如下命令所示:

docker service create --replicas 3 --network my-network --name myweb nginx

這樣,如果Swarm集群中其他節(jié)點上的 Docker 容器也使用 my-network 這個網(wǎng)絡(luò),那么處于該 overlay 網(wǎng)絡(luò)中的所有容器之間,通過網(wǎng)絡(luò)可以連通。

5.4 API

$ curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" \
  -d '{"Image": "alpine", "Cmd": ["echo", "hello world"]}' \
  -X POST http:/v1.24/containers/create
{"Id":"1c6594faf5","Warnings":null}

$ curl --unix-socket /var/run/docker.sock -X POST http:/v1.24/containers/1c6594faf5/start

$ curl --unix-socket /var/run/docker.sock -X POST http:/v1.24/containers/1c6594faf5/wait
{"StatusCode":0}

$ curl --unix-socket /var/run/docker.sock "http:/v1.24/containers/1c6594faf5/logs?stdout=1"
hello world

https://docs.docker.com/develop/sdk/#sdk-and-api-quickstart

https://www.programmableweb.com/api/docker-swarm

5.5 Docker 三劍客

  • docker-compose 負責組織應(yīng)用,應(yīng)用由哪些服務(wù)組成,每個服務(wù)由多少個容器,均在該配置文件里面配置。
  • docker-machine 負責提供虛擬機,該虛擬機具備docker daemon服務(wù)能力。創(chuàng)建的虛擬機,可以用來組成docker swarm集群。docker-machine 并不是必須的功能。完全可以手動安裝docker 服務(wù)。
  • docker swarm 負責組織docker集群,組織跨主機的docker集群管理能力。

轉(zhuǎn)自
https://zhoujinl.github.io/
https://docs.docker.com/engine/swarm/
根據(jù)自己的理解在原文基礎(chǔ)上修改調(diào)整,喜歡請點個贊,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容