Docker Swarm架構、特性與基本實踐
Docker集群管理和編排的特性是通過SwarmKit進行構建的, 其中Swarm mode是Docker Engine內置支持的一種默認實現。Docker 1.12以及更新的版本,都支持Swarm mode,我們可以基于Docker Engine來構建Swarm集群,然后就可以將我們的應用服務(Application Service)部署到Swarm集群中。創建Swarm集群的方式很簡單,先初始化一個Swarm集群,然后將其他的Node加入到該集群即可。本文主要基于Docker Swarm官網文檔,學習總結。
基本特性
Docker Swarm具有如下基本特性:
- 集群管理集成進Docker Engine
使用內置的集群管理功能,我們可以直接通過Docker CLI命令來創建Swarm集群,然后去部署應用服務,而不再需要其它外部的軟件來創建和管理一個Swarm集群。
- 去中心化設計
Swarm集群中包含Manager和Worker兩類Node,我們可以直接基于Docker Engine來部署任何類型的Node。而且,在Swarm集群運行期間,我們既可以對其作出任何改變,實現對集群的擴容和縮容等,如添加Manager Node,如刪除Worker Node,而做這些操作不需要暫停或重啟當前的Swarm集群服務。
- 聲明式服務模型(Declarative Service Model)
在我們實現的應用棧中,Docker Engine使用了一種聲明的方式,讓我們可以定義我們所期望的各種服務的狀態,例如,我們創建了一個應用服務棧:一個Web前端服務、一個后端數據庫服務、Web前端服務又依賴于一個消息隊列服務。
- 服務擴容縮容
對于我們部署的每一個應用服務,我們可以通過命令行的方式,設置啟動多少個Docker容器去運行它。已經部署完成的應用,如果有擴容或縮容的需求,只需要通過命令行指定需要幾個Docker容器即可,Swarm集群運行時便能自動地、靈活地進行調整。
- 協調預期狀態與實際狀態的一致性
Swarm集群Manager Node會不斷地監控集群的狀態,協調集群狀態使得我們預期狀態和實際狀態保持一致。例如我們啟動了一個應用服務,指定服務副本為10,則會啟動10個Docker容器去運行,如果某個Worker Node上面運行的2個Docker容器掛掉了,則Swarm Manager會選擇集群中其它可用的Worker Node,并創建2個服務副本,使實際運行的Docker容器數仍然保持與預期的10個一致。
- 多主機網絡
我們可以為待部署應用服務指定一個Overlay網絡,當應用服務初始化或者進行更新時,Swarm Manager在給定的Overlay網絡中為Docker容器自動地分配IP地址,實際是一個虛擬IP地址(VIP)。
- 服務發現
Swarm Manager會給集群中每一個服務分配一個唯一的DNS名稱,對運行中的Docker容器進行負載均衡。我們可以通過Swarm內置的DNS Server,查詢Swarm集群中運行的Docker容器狀態。
- 負載均衡
在Swarm內部,可以指定如何在各個Node之間分發服務容器(Service Container),實現負載均衡。如果想要使用Swarm集群外部的負載均衡器,可以將服務容器的端口暴露到外部。
- 安全策略
在Swarm集群內部的Node,強制使用基于TLS的雙向認證,并且在單個Node上以及在集群中的Node之間,都進行安全的加密通信。我們可以選擇使用自簽名的根證書,或者使用自定義的根CA(Root CA)證書。
- 滾動更新(Rolling Update)
對于服務需要更新的場景,我們可以在多個Node上進行增量部署更新,Swarm Manager支持通過使用Docker CLI設置一個delay時間間隔,實現多個服務在多個Node上依次進行部署。這樣可以非常靈活地控制,如果有一個服務更新失敗,則暫停后面的更新操作,重新回滾到更新之前的版本。
基本架構
Docker Swarm提供了基本的集群能力,能夠使多個Docker Engine組合成一個group,提供多容器服務。Swarm使用標準的Docker API,啟動容器可以直接使用docker run命令。Swarm更核心的則是關注如何選擇一個主機并在其上啟動容器,最終運行服務。
Docker Swarm基本架構,如下圖所示(來自網絡,詳見后面參考鏈接):
如上圖所示,Swarm Node表示加入Swarm集群中的一個Docker Engine實例,基于該Docker Engine可以創建并管理多個Docker容器。其中,最開始創建Swarm集群的時候,Swarm Manager便是集群中的第一個Swarm Node。在所有的Node中,又根據其職能劃分為Manager Node和Worker Node,具體分別如下所示:
- Manager Node
Manager Node負責調度Task,一個Task表示要在Swarm集群中的某個Node上啟動Docker容器,一個或多個Docker容器運行在Swarm集群中的某個Worker Node上。同時,Manager Node還負責編排容器和集群管理功能(或者更準確地說,是具有Manager管理職能的Node),維護集群的狀態。需要注意的是,默認情況下,Manager Node也作為一個Worker Node來執行Task。Swarm支持配置Manager只作為一個專用的管理Node,后面我們會詳細說明。
- Worker Node
Worker Node接收由Manager Node調度并指派的Task,啟動一個Docker容器來運行指定的服務,并且Worker Node需要向Manager Node匯報被指派的Task的執行狀態。
構建Swarm集群
我們實踐Swarm集群,包括三個節Node,對應的主機名和IP地址分別如下所示:
manager 192.168.1.107 worker1 192.168.1.108 worker2 192.168.1.109
首先,需要保證各個Node上,docker daemon進程已經正常啟動,如果沒有則執行如下命令啟動:
systemctl start docker
接下來就可以創建Swarm集群,創建Swarm的命令,格式如下所示:
docker swarm init --advertise-addr <MANAGER-IP>
我們在準備好的manager Node上,登錄到該Node,創建一個Swarm,執行如下命令:
docker swarm init --advertise-addr 192.168.1.107
上面–advertise-addr選項指定Manager Node會publish它的地址為192.168.1.107,后續Worker Node加入到該Swarm集群,必須要能夠訪問到Manager的該IP地址。可以看到,上述命令執行結果,如下所示:
Swarm initialized: current node (5pe2p4dlxku6z2a6jnvxc4ve6) is now a manager. To add a worker to this swarm, run the following command: docker swarm join \ --token SWMTKN-1-4dm09nzp3xic15uebqja69o2552b75pcg7or0g9t2eld9ehqt3-1kb79trnv6fbydvl9vif3fsch \ 192.168.1.107:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
該結果中給出了后續操作引導信息,告訴我們如何將一個Worker Node加入到Swarm集群中。也可以通過如下命令,來獲取該提示信息:
docker swarm join-token worker
在任何時候,如果我們需要向已經創建的Swarm集群中增加Worker Node,只要新增一個主機(物理機、云主機等都可以),并在其上安裝好Docker Engine并啟動,然后執行上述docker swarm join命令,就可以加入到Swarm集群中。
這時,我們也可以查看當前Manager Node的基本信息,執行docker info命令,輸出信息中可以看到,包含如下關于Swarm的狀態信息:
Swarm: active NodeID: qc42f6myqfpoevfkrzmx08n0r Is Manager: true ClusterID: qi4i0vh7lgb60qxy3mdygb27f Managers: 1 Nodes: 1
可以看出,目前Swarm集群只有Manager一個Node,而且狀態是active。也可以在Manager Node上執行docker node ls命令查看Node狀態,如下所示:
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS qc42f6myqfpoevfkrzmx08n0r * manager Ready Active Leader
接下來,我們可以根據上面提示信息,我們分別在worker1、worker2兩個Worker Node 上,執行命令將Worker Node加入到Swarm集群中,命令如下所示:
docker swarm join \ --token SWMTKN-1-4dm09nzp3xic15uebqja69o2552b75pcg7or0g9t2eld9ehqt3-1kb79trnv6fbydvl9vif3fsch \ 192.168.1.107:2377
如果成功,可以看到成功加入Swarm集群的信息。這時,也可以在Manager Node上,查看Swarm集群的信息,示例如下所示:
Swarm: active NodeID: qc42f6myqfpoevfkrzmx08n0r Is Manager: true ClusterID: qi4i0vh7lgb60qxy3mdygb27f Managers: 1 Nodes: 3
想要查看Swarm集群中全部Node的詳細狀態信息,可以執行如下所示命令:
docker node ls
Swarm集群Node的狀態信息,如下所示:
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS oibbiiwrgwjkw0ni38ydrfsre worker1 Ready Active oocli2uzdt2hy6o50g5z6j7dq worker2 Ready Active qc42f6myqfpoevfkrzmx08n0r * manager Ready Active Leader
上面信息中,AVAILABILITY表示Swarm Scheduler是否可以向集群中的某個Node指派Task,對應有如下三種狀態:
- Active:集群中該Node可以被指派Task
- Pause:集群中該Node不可以被指派新的Task,但是其他已經存在的Task保持運行
- Drain:集群中該Node不可以被指派新的Task,Swarm Scheduler停掉已經存在的Task,并將它們調度到可用的Node上
查看某一個Node的狀態信息,可以在該Node上執行如下命令:
docker node inspect self
我們在Manager Node上執行上述命令,查看的狀態信息如下所示:
[ { "ID": "qc42f6myqfpoevfkrzmx08n0r", "Version": { "Index": 9 }, "CreatedAt": "2017-03-12T15:25:51.725341879Z", "UpdatedAt": "2017-03-12T15:25:51.84308356Z", "Spec": { "Role": "manager", "Availability": "active" }, "Description": { "Hostname": "manager", "Platform": { "Architecture": "x86_64", "OS": "linux" }, "Resources": { "NanoCPUs": 1000000000, "MemoryBytes": 1912082432 }, "Engine": { "EngineVersion": "17.03.0-ce", "Plugins": [ { "Type": "Network", "Name": "bridge" }, { "Type": "Network", "Name": "host" }, { "Type": "Network", "Name": "macvlan" }, { "Type": "Network", "Name": "null" }, { "Type": "Network", "Name": "overlay" }, { "Type": "Volume", "Name": "local" } ] } }, "Status": { "State": "ready", "Addr": "127.0.0.1" }, "ManagerStatus": { "Leader": true, "Reachability": "reachable", "Addr": "192.168.1.107:2377" } } ]
管理Swarm Node
Swarm支持設置一組Manager Node,通過支持多Manager Node實現HA。那么這些Manager Node之間的狀態的一致性就非常重要了,多Manager Node的Warm集群架構,如下圖所示(出自Docker官網):
通過上圖可以看到,Swarm使用了Raft協議來保證多個Manager之間狀態的一致性。基于Raft協議,Manager Node具有一定的容錯功能,假設Swarm集群中有個N個Manager Node,那么整個集群可以容忍最多有(N-1)/2個節點失效。如果是一個三Manager Node的Swarm集群,則最多只能容忍一個Manager Node掛掉。
下面,我們按照對Node的不同操作,通過命令的方式來詳細說明:
(1)Node狀態變更管理
前面我們已經提到過,Node的AVAILABILITY有三種狀態:Active、Pause、Drain,對某個Node進行變更,可以將其AVAILABILITY值通過Docker CLI修改為對應的狀態即可,下面是常見的變更操作:
- 設置Manager Node只具有管理功能
- 對服務進行停機維護,可以修改AVAILABILITY為Drain狀態
- 暫停一個Node,然后該Node就不再接收新的Task
- 恢復一個不可用或者暫停的Node
例如,將Manager Node的AVAILABILITY值修改為Drain狀態,使其只具備管理功能,執行如下命令:
docker node update --availability drain manager
這樣,Manager Node不能被指派Task,也就是不能部署實際的Docker容器來運行服務,而只是作為管理Node的角色。
(2)給Node添加標簽元數據
每個Node的主機配置情況可能不同,比如有的適合運行CPU密集型應用,有的適合運行IO密集型應用,Swarm支持給每個Node添加標簽元數據,這樣可以根據Node的標簽,來選擇性地調度某個服務部署到期望的一組Node上。
給SWarm集群中的某個Worker Node添加標簽,執行如下命令格式如下:
docker node update --label-add 鍵名稱=值
例如,worker1主機在名稱為bjidc這個數據中心,執行如下命令添加標簽:
docker node update --label-add datacenter=bjidc
(3)Node提權/降權
改變Node的角色,Worker Node可以變為Manager Node,這樣實際Worker Node有工作Node變成了管理Node,對應提權操作,例如將worker1和worker2都升級為Manager Node,執行如下命令:
docker node promote worker1 worker2
對上面已提權的worker1和worker2執行降權,需要執行如下命令:
docker node demote worker1 worker2
(4)退出Swarm集群
如果Manager想要退出Swarm集群, 在Manager Node上執行如下命令:
docker swarm node leave
就可以退出集群,如果集群中還存在其它的Worker Node,還希望Manager退出集群,則加上一個強制選項,命令行如下所示:
docker swarm node leave --force
同樣,如果Worker想要退出Swarm集群,在Worker Node上,執行如下命令:
docker swarm node leave
即使Manager已經退出SWarm集群,執行上述命令也可以使得Worker Node退出集群,然后又可以加入到其它新建的Swarm集群中。
管理服務
在Swarm集群上部署服務,必須在Manager Node上進行操作。先說明一下Service、Task、Container(容器)這個三個概念的關系,如下圖(出自Docker官網)非常清晰地描述了這個三個概念的含義:
在Swarm mode下使用Docker,可以實現部署運行服務、服務擴容縮容、刪除服務、滾動升級等功能,下面我們詳細說明。
(1)創建服務
創建Docker服務,可以使用docker service create命令實現,例如,我們要創建如下兩個服務,執行如下命令:
docker service create --replicas 1 --name myapp alpine ping shiyanjun.cn docker service create --replicas 2 --name myredis redis
第一個命令行,從Docker鏡像alpine創建了一個名稱為myapp的服務,其中指定服務副本數為1,也就是啟動一個Docker容器來運行該服務。第二個命令行, 創建一個Redis服務,服務副本數為2,那么會啟動兩個Docker容器來運行myredis服務。查看當前,已經部署啟動的全部應用服務,執行如下命令:
docker service ls
執行結果,如下所示:
ID NAME MODE REPLICAS IMAGE kilpacb9uy4q myapp replicated 1/1 alpine:latest vf1kcgtd5byc myredis replicated 2/2 redis
也可以查詢指定服務的詳細信息,執行如下命令:
docker service ps myredis
查看結果信息,如下所示:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 0p3r9zm2uxpl myredis.1 redis manager Running Running 48 seconds ago ty3undmoielo myredis.2 redis worker1 Running Running 44 seconds ago
上面信息中,在manager和worker1這兩個Node上部署了myredis這個應用服務,也包含了它們對應的當前狀態信息。此時,也可以通過執行docker ps命令,在Manager Node上查看當前啟動的Docker容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 07f93f82a407 redis:latest "docker-entrypoint..." 7 minutes ago Up 7 minutes 6379/tcp myredis.1.0p3r9zm2uxple5i1e2mqgnl3r
在Worker1上查看當前啟動的Docker容器,也就是我們的另一個myredis實例在該Node上:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 41c31e96cccb redis:latest "docker-entrypoint..." 8 minutes ago Up 8 minutes 6379/tcp myredis.2.ty3undmoielo18g7pnvh0nutz
創建服務時,我們可以對運行時服務容器進行配置,例如如下命令:
docker service create --name helloworld \ --env MYVAR=myvalue \ --workdir /tmp \ --user my_user \ alpine ping docker.com
上面,通過 --env 選項來設置環境變量,通過 --workdir 選項來設置工作目錄,通過 --user 選項來設置用戶信息。
(2)擴容縮容服務
Docker Swarm支持服務的擴容縮容,Swarm通過 --mode 選項設置服務類型,提供了兩種模式:一種是replicated,我們可以指定服務Task的個數(也就是需要創建幾個冗余副本),這也是Swarm默認使用的服務類型;另一種是global,這樣會在Swarm集群的每個Node上都創建一個服務。如下圖所示(出自Docker官網),是一個包含replicated和global模式的Swarm集群:
上圖中,黃色表示的replicated模式下的Service Replicas,灰色表示global模式下Service的分布。
服務擴容縮容,在Manager Node上執行命令的格式,如下所示:
docker service scale 服務ID=服務Task總數
例如,將前面我們部署的2個副本的myredis服務,擴容到3個副本,執行如下命令:
docker service scale myredis=3
通過命令docker service ls 查看,擴容操作結果如下所示:
ID NAME MODE REPLICAS IMAGE kilpacb9uy4q myapp replicated 1/1 alpine:latest vf1kcgtd5byc myredis replicated 3/3 redis
進一步通過docker service ps myredis查看一下myredis的各個副本的狀態信息,如下所示:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 0p3r9zm2uxpl myredis.1 redis manager Running Running 14 minutes ago ty3undmoielo myredis.2 redis worker1 Running Running 14 minutes ago zxsvynsgqmpk myredis.3 redis worker2 Running Running less than a second ago
可以看到,我們目前3個Node的Swarm集群,每個Node上都有一個myredis應用服務的副本,可見也實現了很好的負載均衡。
縮容服務,只需要將副本數小于當前應用服務擁有的副本數即可實現,大于指定縮容副本數的副本會被刪除。
(3)刪除服務
刪除服務,只需要在Manager Node上執行如下命令即可:
docker service rm 服務ID
例如,刪除myredis應用服務,執行docker service rm myredis,則應用服務myredis的全部副本都會被刪除。
(4)滾動更新
服務的滾動更新,這里我參考官網文檔的例子說明。在Manager Node上執行如下命令:
docker service create \ --replicas 3 \ --name redis \ --update-delay 10s \ redis:3.0.6
上面通過指定 --update-delay 選項,表示需要進行更新的服務,每次成功部署一個,延遲10秒鐘,然后再更新下一個服務。如果某個服務更新失敗,則Swarm的調度器就會暫停本次服務的部署更新。
另外,也可以更新已經部署的服務所在容器中使用的Image的版本,例如執行如下命令:
docker service update --image redis:3.0.7 redis
將Redis服務對應的Image版本有3.0.6更新為3.0.7,同樣,如果更新失敗,則暫停本次更新。
(5)添加Overlay網絡
在Swarm集群中可以使用Overlay網絡來連接到一個或多個服務。具體添加Overlay網絡,首先,我們需要創建在Manager Node上創建一個Overlay網絡,執行如下命令:
docker network create --driver overlay my-network
創建完Overlay網絡my-network以后,Swarm集群中所有的Manager Node都可以訪問該網絡。然后,我們在創建服務的時候,只需要指定使用的網絡為已存在的Overlay網絡即可,如下命令所示:
docker service create \ --replicas 3 \ --network my-network \ --name myweb \ nginx
這樣,如果Swarm集群中其他Node上的Docker容器也使用my-network這個網絡,那么處于該Overlay網絡中的所有容器之間,通過網絡可以連通。
參考鏈接
- https://docs.docker.com/engine/swarm/
- https://docs.docker.com/engine/swarm/swarm-mode/
- https://docs.docker.com/engine/swarm/key-concepts/
- https://docs.docker.com/engine/swarm/swarm-tutorial/
- https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm/
- https://docs.docker.com/engine/swarm/swarm-tutorial/add-nodes/
- https://docs.docker.com/engine/swarm/swarm-tutorial/deploy-service/
- https://docs.docker.com/engine/swarm/swarm-tutorial/inspect-service/
- https://docs.docker.com/engine/swarm/swarm-tutorial/scale-service/
- https://docs.docker.com/engine/swarm/swarm-tutorial/rolling-update/
- https://docs.docker.com/engine/swarm/manage-nodes/
- https://docs.docker.com/engine/swarm/swarm_manager_locking/
- https://docs.docker.com/engine/swarm/services/
- https://docs.docker.com/engine/swarm/networking/
- https://docs.docker.com/engine/swarm/admin_guide/
- https://docs.docker.com/engine/swarm/raft/
- https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/
- https://docs.docker.com/engine/swarm/ingress/
- https://platform9.com/blog/compare-kubernetes-vs-docker-swarm/
來自:http://shiyanjun.cn/archives/1625.html