Docker Swarm架構、特性與基本實踐(轉載)

NO IMAGE

轉自(時延軍):http://shiyanjun.cn/archives/1625.html

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基本架構,如下圖所示(來自網路,詳見後面參考連結):
docker-swarm-architecture
如上圖所示,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地址分別如下所示:

1
2
3
manager  192.168.1.107
worker1  192.168.1.108
worker2  192.168.1.109

首先,需要保證各個Node上,docker daemon程序已經正常啟動,如果沒有則執行如下命令啟動:

1
systemctl start docker

接下來就可以建立Swarm叢集,建立Swarm的命令,格式如下所示:

1
docker swarm init --advertise-addr <MANAGER-IP>

我們在準備好的manager Node上,登入到該Node,建立一個Swarm,執行如下命令:

1
docker swarm init --advertise-addr 192.168.1.107

上面--advertise-addr選項指定Manager Node會publish它的地址為192.168.1.107,後續Worker Node加入到該Swarm叢集,必須要能夠訪問到Manager的該IP地址。可以看到,上述命令執行結果,如下所示:

1
2
3
4
5
6
7
8
9
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叢集中。也可以通過如下命令,來獲取該提示資訊:

1
docker swarm join-token worker

在任何時候,如果我們需要向已經建立的Swarm叢集中增加Worker Node,只要新增一個主機(物理機、雲主機等都可以),並在其上安裝好Docker Engine並啟動,然後執行上述docker swarm join命令,就可以加入到Swarm叢集中。
這時,我們也可以檢視當前Manager Node的基本資訊,執行docker info命令,輸出資訊中可以看到,包含如下關於Swarm的狀態資訊:

1
2
3
4
5
6
Swarm: active
 NodeID: qc42f6myqfpoevfkrzmx08n0r
 Is Manager: true
 ClusterID: qi4i0vh7lgb60qxy3mdygb27f
 Managers: 1
 Nodes: 1

可以看出,目前Swarm叢集只有Manager一個Node,而且狀態是active。也可以在Manager Node上執行docker node ls命令檢視Node狀態,如下所示:

1
2
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
qc42f6myqfpoevfkrzmx08n0r *  manager   Ready   Active        Leader

接下來,我們可以根據上面提示資訊,我們分別在worker1、worker2兩個Worker Node 上,執行命令將Worker Node加入到Swarm叢集中,命令如下所示:

1
2
3
docker swarm join \
    --token SWMTKN-1-4dm09nzp3xic15uebqja69o2552b75pcg7or0g9t2eld9ehqt3-1kb79trnv6fbydvl9vif3fsch \
    192.168.1.107:2377

如果成功,可以看到成功加入Swarm叢集的資訊。這時,也可以在Manager Node上,檢視Swarm叢集的資訊,示例如下所示:

1
2
3
4
5
6
Swarm: active
 NodeID: qc42f6myqfpoevfkrzmx08n0r
 Is Manager: true
 ClusterID: qi4i0vh7lgb60qxy3mdygb27f
 Managers: 1
 Nodes: 3

想要檢視Swarm叢集中全部Node的詳細狀態資訊,可以執行如下所示命令:

1
docker node ls

Swarm叢集Node的狀態資訊,如下所示:

1
2
3
4
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上執行如下命令:

1
docker node inspect self

我們在Manager Node上執行上述命令,檢視的狀態資訊如下所示:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
[
    {
        "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-multiple-manager-architecture
通過上圖可以看到,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狀態,使其只具備管理功能,執行如下命令:

1
docker node update --availability drain manager

這樣,Manager Node不能被指派Task,也就是不能部署實際的Docker容器來執行服務,而只是作為管理Node的角色。

(2)給Node新增標籤後設資料

每個Node的主機配置情況可能不同,比如有的適合執行CPU密集型應用,有的適合執行IO密集型應用,Swarm支援給每個Node新增標籤後設資料,這樣可以根據Node的標籤,來選擇性地排程某個服務部署到期望的一組Node上。
給SWarm叢集中的某個Worker Node新增標籤,執行如下命令格式如下:

1
docker node update --label-add 鍵名稱=值

例如,worker1主機在名稱為bjidc這個資料中心,執行如下命令新增標籤:

1
docker node update --label-add datacenter=bjidc

(3)Node提權/降權

改變Node的角色,Worker Node可以變為Manager Node,這樣實際Worker Node有工作Node變成了管理Node,對應提權操作,例如將worker1和worker2都升級為Manager Node,執行如下命令:

1
docker node promote worker1 worker2

對上面已提權的worker1和worker2執行降權,需要執行如下命令:

1
docker node demote worker1 worker2

(4)退出Swarm叢集

如果Manager想要退出Swarm叢集, 在Manager Node上執行如下命令:

1
docker swarm node leave

就可以退出叢集,如果叢集中還存在其它的Worker Node,還希望Manager退出叢集,則加上一個強制選項,命令列如下所示:

1
docker swarm node leave --force

同樣,如果Worker想要退出Swarm叢集,在Worker Node上,執行如下命令:

1
docker swarm node leave

即使Manager已經退出SWarm叢集,執行上述命令也可以使得Worker Node退出叢集,然後又可以加入到其它新建的Swarm叢集中。

管理服務

在Swarm叢集上部署服務,必須在Manager Node上進行操作。先說明一下Service、Task、Container(容器)這個三個概念的關係,如下圖(出自Docker官網)非常清晰地描述了這個三個概念的含義:
services-diagram
在Swarm mode下使用Docker,可以實現部署執行服務、服務擴容縮容、刪除服務、滾動更新等功能,下面我們詳細說明。

(1)建立服務

建立Docker服務,可以使用docker service create命令實現,例如,我們要建立如下兩個服務,執行如下命令:

1
2
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服務。檢視當前,已經部署啟動的全部應用服務,執行如下命令:

1
docker service ls

執行結果,如下所示:

1
2
3
ID            NAME     MODE        REPLICAS  IMAGE
kilpacb9uy4q  myapp    replicated  1/1       alpine:latest
vf1kcgtd5byc  myredis  replicated  2/2       redis

也可以查詢指定服務的詳細資訊,執行如下命令:

1
docker service ps myredis

檢視結果資訊,如下所示:

1
2
3
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容器:

1
2
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上:

1
2
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
41c31e96cccb        redis:latest        "docker-entrypoint..."   8 minutes ago       Up 8 minutes        6379/tcp            myredis.2.ty3undmoielo18g7pnvh0nutz

建立服務時,我們可以對執行時服務容器進行配置,例如如下命令:

1
2
3
4
5
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叢集:
docker-swarm-replicated-vs-global
上圖中,黃色表示的replicated模式下的Service Replicas,灰色表示global模式下Service的分佈。
服務擴容縮容,在Manager Node上執行命令的格式,如下所示:

1
docker service scale 服務ID=服務Task總數

例如,將前面我們部署的2個副本的myredis服務,擴容到3個副本,執行如下命令:

1
docker service scale myredis=3

通過命令docker service ls 檢視,擴容操作結果如下所示:

1
2
3
ID            NAME     MODE        REPLICAS  IMAGE
kilpacb9uy4q  myapp    replicated  1/1       alpine:latest
vf1kcgtd5byc  myredis  replicated  3/3       redis

進一步通過docker service ps myredis檢視一下myredis的各個副本的狀態資訊,如下所示:

1
2
3
4
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上執行如下命令即可:

1
docker service rm 服務ID

例如,刪除myredis應用服務,執行docker service rm myredis,則應用服務myredis的全部副本都會被刪除。

(4)滾動更新

服務的滾動更新,這裡我參考官網文件的例子說明。在Manager Node上執行如下命令:

1
2
3
4
5
docker service create \
  --replicas 3 \
  --name redis \
  --update-delay 10s \
  redis:3.0.6

上面通過指定--update-delay選項,表示需要進行更新的服務,每次成功部署一個,延遲10秒鐘,然後再更新下一個服務。如果某個服務更新失敗,則Swarm的排程器就會暫停本次服務的部署更新。
另外,也可以更新已經部署的服務所在容器中使用的Image的版本,例如執行如下命令:

1
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網路,執行如下命令:

1
docker network create --driver overlay my-network

建立完Overlay網路my-network以後,Swarm叢集中所有的Manager Node都可以訪問該網路。然後,我們在建立服務的時候,只需要指定使用的網路為已存在的Overlay網路即可,如下命令所示:

1
2
3
4
5
docker service create \
  --replicas 3 \
  --network my-network \
  --name myweb \
  nginx

這樣,如果Swarm叢集中其他Node上的Docker容器也使用my-network這個網路,那麼處於該Overlay網路中的所有容器之間,通過網路可以連通。

參考連結