docker入門基礎(八)

10、 Docker Swarm

一、幾個概念

一、Docker Swarm Mode

Docker v1.12 是一個很是重要的版本,Docker 從新實現了集羣的編排方式。在此以前,提供集羣功能的 Docker Swarm 是一個單獨的軟件,並且依賴外部數據庫(好比 Consul、etcd 或 Zookeeper)。前端

從 v1.12 開始,Docker Swarm 的功能已經徹底與 Docker Engine 集成,要管理集羣,只須要啓動 Swarm Mode。安裝好 Docker,Swarm 就已經在那裏了,服務發現也在那裏了(不須要安裝 Consul 等外部數據庫)。node

相比 Kubernetes,用 Docker Swarm 建立集羣很是簡單,不須要額外安裝任何軟件,也不須要作任何額外的配置。很適合做爲學習容器編排引擎的起點。mysql

二、swarm

swarm 運行 Docker Engine 的多個主機組成的集羣。nginx

從 v1.12 開始,集羣管理和編排功能已經集成進 Docker Engine。當 Docker Engine 初始化了一個 swarm 或者加入到一個存在的 swarm 時,它就啓動了 swarm mode。web

沒啓動 swarm mode 時,Docker 執行的是容器命令;運行 swarm mode 後,Docker 增長了編排 service 的能力。sql

Docker 容許在同一個 Docker 主機上既運行 swarm service,又運行單獨的容器。docker

三、node

swarm 中的每一個 Docker Engine 都是一個 node,有兩種類型的 node:managerworkershell

爲了向 swarm 中部署應用,咱們須要在 manager node 上執行部署命令,manager node 會將部署任務拆解並分配給一個或多個 worker node 完成部署。數據庫

manager node 負責執行編排和集羣管理工做,保持並維護 swarm 處於指望的狀態。swarm 中若是有多個 manager node,它們會自動協商並選舉出一個 leader 執行編排任務。

woker node 接受並執行由 manager node 派發的任務。默認配置下 manager node 同時也是一個 worker node,不過能夠將其配置成 manager-only node,讓其專職負責編排和集羣管理工做。

work node 會按期向 manager node 報告本身的狀態和它正在執行的任務的狀態,這樣 manager 就能夠維護整個集羣的狀態。

四、service

service 定義了 worker node 上要執行的任務。swarm 的主要編排任務就是保證 service 處於指望的狀態下。

舉一個 service 的例子:在 swarm 中啓動一個 http 服務,使用的鏡像是 httpd:latest,副本數爲 3。

manager node 負責建立這個 service,通過分析知道須要啓動 3 個 httpd 容器,根據當前各 worker node 的狀態將運行容器的任務分配下去,好比 worker1 上運行兩個容器,worker2 上運行一個容器。

運行了一段時間,worker2 忽然宕機了,manager 監控到這個故障,因而當即在 worker3 上啓動了一個新的 httpd 容器。

這樣就保證了 service 處於指望的三個副本狀態。

二、swarm 集羣

實驗環境

主機名 IP 系統 docker版本
swarm-manager 192.168.2.110 CentOS 7.5 docker-ce-18.09.0-3.el7.x86_64
swarm-worker1 192.168.2.120 CentOS 7.5 docker-ce-18.09.0-3.el7.x86_64
swarm-worker2 192.168.2.130 CentOS 7.5 docker-ce-18.09.0-3.el7.x86_64

在 swarm-manager 上執行以下命令建立 swarm。

[root@manager ~]# docker swarm init --advertise-addr 192.168.2.110
Swarm initialized: current node (rxoqe9tvd0rfd4zxc4uoniu05) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-04d6zlih0yqjgnlz8rlwhpz1qfc2ua9yp18y15wyefrctgrj0x-chtm4rrdysjp8e310um4rjx09 192.168.2.110:2377

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

--advertise-addr 指定與其餘 node 通訊的地址。

docker swarm init 輸出告訴咱們:

① swarm 建立成功,swarm-manager 成爲 manager node。

② 添加 worker node 須要執行的命令。

③ 添加 manager node 須要執行的命令。

執行 docker node ls 查看當前 swarm 的 node,目前只有一個 manager。

[root@manager ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
rxoqe9tvd0rfd4zxc4uoniu05 *   manager             Ready               Active              Leader              18.09.0

複製前面的 docker swarm join 命令,在 worker1 和worker2 上執行,將它們添加到 swarm 中。命令輸出以下:

[root@swarm-worker2 ~]# docker swarm join --token SWMTKN-1-04d6zlih0yqjgnlz8rlwhpz1qfc2ua9yp18y15wyefrctgrj0x-chtm4rrdysjp8e310um4rjx09 192.168.2.110:2377
This node joined a swarm as a worker.
[root@swarm-manager ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
rxoqe9tvd0rfd4zxc4uoniu05 *   swarm-manager       Ready               Active              Leader              18.09.0
jfpu6n3qt4gqnoqs8lv36o5fn     swarm-worker1       Ready               Active                                  18.09.0
byxpoc4rgi45jz1d8vi5hgrog     swarm-worker2       Ready               Active                                  18.09.0

若是當時沒有記錄下 docker swarm init 提示的添加 worker 的完整命令,能夠經過 docker swarm join-token worker 查看。

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

    docker swarm join --token SWMTKN-1-04d6zlih0yqjgnlz8rlwhpz1qfc2ua9yp18y15wyefrctgrj0x-chtm4rrdysjp8e310um4rjx09 192.168.2.110:2377

注意:此命令只能在 manager node 上執行。

至此,三節點的 swarm 集羣就已經搭建好了。

三、運行第一個 Service

部署一個運行 httpd 鏡像的 service,執行以下命令:

docker service create --name web_server httpd

部署 service 的命令形式與運行容器的 docker run 很類似,--name 爲 service 命名,nginx:1.14-alpine 爲鏡像的名字。

[root@swarm-manager ~]# docker service create --name my_web nginx:1.14-alpine
kqhkkqt0i6r6u40qknuyb76ja
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

經過 docker service ls 能夠查看當前 swarm 中的 service。

[root@swarm-manager ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
kqhkkqt0i6r6        my_web              replicated          1/1                 nginx:1.14-alpine

REPLICAS 顯示當前副本信息,1/1 的意思是 web_server 這個 service 指望的容器副本數量爲 1,目前已經啓動的副本數量爲1。也就是當前 service 尚未部署完成。命令 docker service ps 能夠查看 service 每一個副本的狀態。

[root@swarm-manager ~]# docker service ps my_web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
kokoy6h24gwp        my_web.1            nginx:1.14-alpine   swarm-worker1       Running             Running about a minute ago

能夠看到 service 惟一的副本被分派到 swarm-worker1,當前的狀態是 running

若是以爲不放心,還能夠到 swarm-worker1 去確認 nginx 容器已經運行。

[root@swarm-worker1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
a0f7ea32d403        nginx:1.14-alpine   "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes        80/tcp              my_web.1.kokoy6h24gwpbujbp0id9ay15

目前爲止 Service 與普通的容器尚未太大的不一樣

四、實現 Service 伸縮

對於 web 服務,咱們一般會運行多個實例。這樣能夠負載均衡,同時也能提供高可用。

swarm 要實現這個目標很是簡單,增長 service 的副本數就能夠了。在 swarm-manager 上執行以下命令:

docker service scale web_server=5
[root@swarm-manager ~]# docker service scale my_web=5
my_web scaled to 5
overall progress: 5 out of 5 tasks 
1/5: running   [==================================================>] 
2/5: running   [==================================================>] 
3/5: running   [==================================================>] 
4/5: running   [==================================================>] 
5/5: running   [==================================================>] 
verify: Service converged 

[root@swarm-manager ~]# docker service scale my_web=5
my_web scaled to 5
overall progress: 5 out of 5 tasks 
1/5: running   [==================================================>] 
2/5: running   [==================================================>] 
3/5: running   [==================================================>] 
4/5: running   [==================================================>] 
5/5: running   [==================================================>] 
verify: Service converged 
[root@swarm-manager ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
kqhkkqt0i6r6        my_web              replicated          5/5                 nginx:1.14-alpine   
[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
kokoy6h24gwp        my_web.1            nginx:1.14-alpine   swarm-worker1       Running             Running 3 minutes ago                        
e8yjfo9td44m        my_web.2            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                         
7fth256vi4dw        my_web.3            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                         
7dk4zf9li67p        my_web.4            nginx:1.14-alpine   swarm-worker1       Running             Running 50 seconds ago                       
kz9b8mv2q1sk        my_web.5            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago

5 個副本已經分佈在 swarm 的全部三個節點上。

默認配置下 manager node 也是 worker node,因此 swarm-manager 上也運行了副本。若是不但願在 manager 上運行 service,能夠執行以下命令:

docker node update --availability drain swarm-manager
[root@swarm-manager ~]# docker node update --availability drain swarm-manager 
swarm-manager
[root@swarm-manager ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
rxoqe9tvd0rfd4zxc4uoniu05 *   swarm-manager       Ready               Drain               Leader              18.09.0
jfpu6n3qt4gqnoqs8lv36o5fn     swarm-worker1       Ready               Active                                  18.09.0
byxpoc4rgi45jz1d8vi5hgrog     swarm-worker2       Ready               Active                                  18.09.0
[root@swarm-manager ~]# docker service ps web_server
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                    ERROR               PORTS
qdq9gcq6u01d        web_server.1        httpd:latest        swarm-worker2       Running             Running less than a second ago                       
h3b36zug0o3b        web_server.2        httpd:latest        swarm-worker2       Running             Running less than a second ago                       
pmxnu78a94lm        web_server.3        httpd:latest        swarm-worker1       Running             Running 2 minutes ago                                
j08u3j2ctwal        web_server.4        httpd:latest        swarm-worker1       Running             Running 20 seconds ago                               
2teq0ag7n4u4         \_ web_server.4    httpd:latest        swarm-manager       Shutdown            Shutdown 21 seconds ago                              
pmrzsdue7ryk        web_server.5        httpd:latest        swarm-worker1       Running             Running 20 seconds ago                               
wqtbw718bcx3         \_ web_server.5    httpd:latest        swarm-manager       Shutdown            Shutdown 21 seconds ago

swarm-manager 上的副本 web_server.4 \5 已經被 Shutdown 了,爲了達到 5 個副本數的目標,在 swarm-worker1 上添加了副本 web_server.4\5

前面咱們的場景是 scale up,咱們還能夠 scale down,減小副本數,運行下面的命令:

docker service scale web_server=3
[root@swarm-manager ~]# docker service scale my_web=3
my_web scaled to 3
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
[root@swarm-manager ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
kqhkkqt0i6r6        my_web              replicated          3/3                 nginx:1.14-alpine   
[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
kokoy6h24gwp        my_web.1            nginx:1.14-alpine   swarm-worker1       Running             Running 4 minutes ago                       
e8yjfo9td44m        my_web.2            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                        
7fth256vi4dw        my_web.3            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago

能夠看到,web_server.4web_server.5 這兩個副本已經被刪除了。

五、實現 Failover

故障是在所不免的,容器可能崩潰,Docker Host 可能宕機,不過幸運的是,Swarm 已經內置了 failover 策略。

建立 service 的時候,咱們沒有告訴 swarm 發生故障時該如何處理,只是說明了咱們指望的狀態(好比運行3個副本),swarm 會盡最大的努力達成這個指望狀態,不管發生什麼情況.

如今咱們測試 swarm 的 failover 特性,關閉 swarm-worker1。

Swarm 會檢測到 swarm-worker1 的故障,並標記爲 Down。

[root@swarm-manager ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
rxoqe9tvd0rfd4zxc4uoniu05 *   swarm-manager       Ready               Drain               Leader              18.09.0
jfpu6n3qt4gqnoqs8lv36o5fn     swarm-worker1       Down                Active                                  18.09.0
byxpoc4rgi45jz1d8vi5hgrog     swarm-worker2       Ready               Active                                  18.09.0

Swarm 會將 swarm-worker1 上的副本調度到其餘可用節點。咱們能夠經過 docker service ps 觀察這個 failover 過程。

[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
w285dbb0spcf        my_web.1            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                        
kokoy6h24gwp         \_ my_web.1        nginx:1.14-alpine   swarm-worker1       Shutdown            Running 5 minutes ago                       
e8yjfo9td44m        my_web.2            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                        
7fth256vi4dw        my_web.3            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago

能夠看到,web_server.1web_server.2 已經從 swarm-worker1 遷移到了 swarm-worker2,以前運行在故障節點 swarm-worker1 上的副本狀態被標記爲 Shutdown。即便worker1恢復也不行

六、內部訪問 Service

爲了便於分析,咱們從新部署 web_server。

[root@swarm-manager ~]# docker service rm my_web 
my_web
[root@swarm-manager ~]# docker service create --name web_server --replicas=2  nginx:1.14-alpine
qnr86tsd8ht8r7d1bfrbhxx5i
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged 
[root@swarm-manager ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
qnr86tsd8ht8        web_server          replicated          2/2                 nginx:1.14-alpine   
[root@swarm-manager ~]# docker service ps web_server 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
ak7uuvnjf8mx        web_server.1        nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                             
z9kck3rspzec        web_server.2        nginx:1.14-alpine   swarm-worker1       Running             Running about a minute ago

docker service rm 刪除 web_server,service 的全部副本(容器)都會被刪除。

② 從新建立 service,此次直接用 --replicas=2 建立兩個副本。

③ 每一個 worker node 上運行了一個副本。

要訪問 http 服務,最起碼網絡得通吧,服務的 IP 咱們得知道吧,但這些信息目前咱們都不清楚。不過至少咱們知道每一個副本都是一個運行的容器,要不先看看容器的網絡配置吧。

[root@swarm-worker1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
68502d64ca5f        nginx:1.14-alpine   "nginx -g 'daemon of…"   46 seconds ago      Up 46 seconds       80/tcp              web_server.2.z9kck3rspzecnyik9llg3pa1f
[root@swarm-worker1 ~]# docker exec web_server.2.z9kck3rspzecnyik9llg3pa1f ip r
default via 172.16.1.1 dev eth0 
172.16.1.0/24 dev eth0 scope link  src 172.16.1.2

在 swarm-worker1 上運行了一個容器,是 web_server 的一個副本,容器監聽了 80 端口,但並無映射到 Docker Host,因此只能經過容器的 IP 訪問。查看一下容器的 IP。

容器 IP 爲 172.16.1.2,實際上鍊接的是 Docker 默認 bridge 網絡。

咱們能夠直接在 swarm-worker1 上訪問容器的 http 服務。

[root@swarm-worker1 ~]# curl 172.16.1.2 -I
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Wed, 26 Dec 2018 02:16:48 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 21 Dec 2018 01:22:26 GMT
Connection: keep-alive
ETag: "5c1c4052-264"
Accept-Ranges: bytes

但這樣的訪問也僅僅是容器層面的訪問,服務並無暴露給外部網絡,只能在 Docker 主機上訪問。換句話說,當前配置下,咱們沒法訪問 service web_server。

七、外部訪問service

要將 service 暴露到外部,方法其實很簡單,執行下面的命令:

[root@swarm-manager ~]# docker service  update --publish-add 8080:80 web_server 
web_server
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged

若是是新建 service,能夠直接用使用 --publish 參數,好比:

docker service create --name web_server --publish 8080:80 --replicas=2 httpd

容器在 80 端口上監聽 http 請求,--publish-add 8080:80 將容器的 80 映射到主機的 8080 端口,這樣外部網絡就能訪問到 service 了。

[root@swarm-manager ~]# curl -I 192.168.2.110:8080
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Wed, 26 Dec 2018 02:18:17 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 21 Dec 2018 01:22:26 GMT
Connection: keep-alive
ETag: "5c1c4052-264"
Accept-Ranges: bytes

[root@swarm-manager ~]# curl -I 192.168.2.120:8080
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Wed, 26 Dec 2018 02:18:20 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 21 Dec 2018 01:22:26 GMT
Connection: keep-alive
ETag: "5c1c4052-264"
Accept-Ranges: bytes

[root@swarm-manager ~]# curl -I 192.168.2.130:8080
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Wed, 26 Dec 2018 02:18:22 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 21 Dec 2018 01:22:26 GMT
Connection: keep-alive
ETag: "5c1c4052-264"
Accept-Ranges: bytes

當咱們訪問任何節點的 8080 端口時,swarm 內部的 load balancer 會將請求轉發給 web_server 其中的一個副本,不管訪問哪一個節點,即便該節點上沒有運行 service 的副本,最終都能訪問到 service。

另外,咱們還能夠配置一個外部 load balancer,將請求路由到 swarm service。好比配置 HAProxy,將請求分發到各個節點的 8080 端口。

八、ingress 網絡

當咱們應用 --publish-add 8080:80 時,swarm 會從新配置 service。

[root@swarm-manager ~]# docker service ps web_server 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                 ERROR               PORTS
wyl8a3mhheyr        web_server.1        nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                              
ak7uuvnjf8mx         \_ web_server.1    nginx:1.14-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                             
v3sw5eq19z4x        web_server.2        nginx:1.14-alpine   swarm-worker1       Running             Running about a minute ago                        
z9kck3rspzec         \_ web_server.2    nginx:1.14-alpine   swarm-worker1       Shutdown            Shutdown about a minute ago

以前的全部副本都被 Shutdown,而後啓動了新的副本。咱們查看一下新副本的容器網絡配置。

[root@swarm-worker1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
9a28cef94e92        nginx:1.14-alpine   "nginx -g 'daemon of…"   About a minute ago   Up About a minute   80/tcp              web_server.2.v3sw5eq19z4x8ytauz3x2wl1w
[root@swarm-worker1 ~]# docker exec web_server.2.v3sw5eq19z4x8ytauz3x2wl1w ip r
default via 172.18.0.1 dev eth1 
10.255.0.0/16 dev eth0 scope link  src 10.255.0.10 
172.18.0.0/16 dev eth1 scope link  src 172.18.0.3

容器的網絡與 --publish-add 以前已經大不同了,如今有兩塊網卡,每塊網卡鏈接不一樣的 Docker 網絡

實際上:

  1. eth0 鏈接的是一個 overlay 類型的網絡,名字爲 ingress,其做用是讓運行在不一樣主機上的容器能夠相互通訊。
  2. eth1 鏈接的是一個 bridge 類型的網絡,名字爲 docker_gwbridge,其做用是讓容器可以訪問到外網。
[root@swarm-worker1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
d174ec92bfb9        bridge              bridge              local
f57703c9517c        docker_gwbridge     bridge              local
17bf4830406d        harbor_harbor       bridge              local
266cc54ae977        host                host                local
t4xnhmp6y5nr        ingress             overlay             swarm
a5d3ec376305        mybr0               bridge              local
436b29f1c660        none                null                local

ingress 網絡是 swarm 建立時 Docker 爲自動咱們建立的,swarm 中的每一個 node 都能使用 ingress

九、service之間通訊

微服務架構的應用由若干 service 組成。好比有運行 httpd 的 web 前端,有提供緩存的 memcached,有存放數據的 mysql,每一層都是 swarm 的一個 service,每一個 service 運行了若干容器。在這樣的架構中,service 之間是必然要通訊的。

1)服務發現

一種實現方法是將全部 service 都 publish 出去,而後經過 routing mesh 訪問。但明顯的缺點是把 memcached 和 mysql 也暴露到外網,增長了安全隱患。

若是不 publish,那麼 swarm 就要提供一種機制,可以:

  1. 讓 service 經過簡單的方法訪問到其餘 service。
  2. 當 service 副本的 IP 發生變化時,不會影響訪問該 service 的其餘 service。
  3. 當 service 的副本數發生變化時,不會影響訪問該 service 的其餘 service。

這其實就是服務發現(service discovery)。Docker Swarm 原生就提供了這項功能,經過服務發現,service 的使用者不須要知道 service 運行在哪裏,IP 是多少,有多少個副本,就能與 service 通訊。

2)建立 overlay 網絡

要使用服務發現,須要相互通訊的 service 必須屬於同一個 overlay 網絡,因此咱們先得建立一個新的 overlay 網絡。

[root@swarm-manager ~]#  docker network create --driver overlay myapp_net
rdozkrfo8mg9y9t3m8swiyv8q
[root@swarm-manager ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
4508165c1437        bridge              bridge              local
e56359b1d013        docker_gwbridge     bridge              local
266cc54ae977        host                host                local
t4xnhmp6y5nr        ingress             overlay             swarm
rdozkrfo8mg9        myapp_net           overlay             swarm
436b29f1c660        none                null                local

注意:目前 ingress 沒有提供服務發現,必須建立本身的 overlay 網絡。

3)部署 service 到 overlay

部署一個 web 服務,並將其掛載到新建立的 overlay 網絡。

[root@swarm-manager ~]# docker service create --name my_web --replicas=3 --network myapp_net nginx:1.14-alpine
hlv54albpldu99ox7a5bnpsch
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
v6luevjpk1of        my_web.1            nginx:1.14-alpine   swarm-worker1       Running             Running 22 seconds ago                       
o6orjanarrk8        my_web.2            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                         
zo6hgkztxzzx        my_web.3            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago

部署一個 util 服務用於測試,掛載到同一個 overlay 網絡。

[root@swarm-manager ~]# docker service create --name util --network myapp_net busybox sleep 10000000
pa8qw96mx4ru0bcb6krz21shi
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

sleep 10000000 的做用是保持 busybox 容器處於運行的狀態,咱們纔可以進入到容器中訪問 service my_web

4)驗證

經過 docker service ps util 確認 util 所在的節點爲 swarm-worker1。

[root@swarm-manager ~]# docker service ps util 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
uuf4yyfra9sd        util.1              busybox:latest      swarm-worker1       Running             Running about a minute ago

登陸到 swarm-worker1,在容器 util.1 中 ping 服務 my_web

[root@swarm-worker1 ~]# docker exec util.1.uuf4yyfra9sdqqbyd9mchcewt ping -c 3 my_web
PING my_web (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.141 ms
64 bytes from 10.0.0.2: seq=1 ttl=64 time=0.080 ms
64 bytes from 10.0.0.2: seq=2 ttl=64 time=0.080 ms

--- my_web ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.080/0.100/0.141 ms

能夠看到 my_web 的 IP 爲 10.0.0.2,這是哪一個副本的 IP 呢?

其實哪一個副本的 IP 都不是。10.0.0.2my_web service 的 VIP(Virtual IP),swarm 會將對 VIP 的訪問負載均衡到每個副本。

對於服務的使用者(這裏是 util.1),根本不須要知道 my_web副本的 IP,也不須要知道 my_web 的 VIP,只需直接用 service 的名字 my_web 就能訪問服務。

十、滾動更新 Service

滾動更新下降了應用更新的風險,若是某個副本更新失敗,整個更新將暫停,其餘副本則能夠繼續提供服務。同時,在更新的過程當中,老是有副本在運行的,所以也保證了業務的連續性。

下面咱們將部署三副本的服務,鏡像使用 nginx:1.14-alpine,而後將其更新到 nginx:1.15-alpine。

[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
v6luevjpk1of        my_web.1            nginx:1.14-alpine   swarm-worker1       Running             Running 12 minutes ago                       
o6orjanarrk8        my_web.2            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago                         
zo6hgkztxzzx        my_web.3            nginx:1.14-alpine   swarm-worker2       Running             Running 11 hours ago
docker service update --image httpd:2.2.32 my_web

--image 指定新的鏡像。

Swarm 將按照以下步驟執行滾動更新:

  1. 中止第一個副本。
  2. 調度任務,選擇 worker node。
  3. 在 worker 上用新的鏡像啓動副本。
  4. 若是副本(容器)運行成功,繼續更新下一個副本;若是失敗,暫停整個更新過程。

docker service ps 查看更新結果。

[root@swarm-manager ~]# docker service update --image nginx:1.15-alpine my_web
my_web
overall progress: 3 out of 3 tasks 
1/3: running   
2/3: running   
3/3: running   
verify: Service converged 
[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
ws8ym7uxht1g        my_web.1            nginx:1.15-alpine   swarm-worker1       Running             Running 15 seconds ago                        
v6luevjpk1of         \_ my_web.1        nginx:1.14-alpine   swarm-worker1       Shutdown            Shutdown 20 seconds ago                       
unbw58qiakb9        my_web.2            nginx:1.15-alpine   swarm-worker2       Running             Running 11 hours ago                          
o6orjanarrk8         \_ my_web.2        nginx:1.14-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                         
eqkbqmzyllxi        my_web.3            nginx:1.15-alpine   swarm-worker2       Running             Running 11 hours ago                          
zo6hgkztxzzx         \_ my_web.3        nginx:1.14-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago

默認配置下,Swarm 一次只更新一個副本,而且兩個副本之間沒有等待時間。咱們能夠經過 --update-parallelism 設置並行更新的副本數目,經過 --update-delay 指定滾動更新的間隔時間。

好比執行以下命令:

docker service update --replicas 6 --update-parallelism 2 --update-delay 1m30s my_web

service 增長到六個副本,每次更新兩個副本,間隔時間一分半鐘。

[root@swarm-manager ~]# docker service update --replicas 6 --update-parallelism 2 --update-delay 1m30s my_web 
my_web
overall progress: 6 out of 6 tasks 
1/6: running   
2/6: running   
3/6: running   
4/6: running   
5/6: running   
6/6: running   
verify: Service converged 
[root@swarm-manager ~]# docker service inspect --pretty my_web 

ID:     hlv54albpldu99ox7a5bnpsch
Name:       my_web
Service Mode:   Replicated
 Replicas:  6
Placement:
UpdateConfig:
 Parallelism:   2
 Delay:     1m30s
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Update order:      stop-first
RollbackConfig:
 Parallelism:   1
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Rollback order:    stop-first
ContainerSpec:
 Image:     nginx:1.15-alpine@sha256:2e497c294e3ba84aaeab7a0fbb1027819cd1f5f5892ed3c4a82b8b05010090da
 Init:      false
Resources:
Networks: myapp_net 
Endpoint Mode:  vip

docker service ps 確保6個副本處於正常狀態。

[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
ws8ym7uxht1g        my_web.1            nginx:1.15-alpine   swarm-worker1       Running             Running 3 minutes ago                        
v6luevjpk1of         \_ my_web.1        nginx:1.14-alpine   swarm-worker1       Shutdown            Shutdown 3 minutes ago                       
unbw58qiakb9        my_web.2            nginx:1.15-alpine   swarm-worker2       Running             Running 11 hours ago                         
o6orjanarrk8         \_ my_web.2        nginx:1.14-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                        
eqkbqmzyllxi        my_web.3            nginx:1.15-alpine   swarm-worker2       Running             Running 11 hours ago                         
zo6hgkztxzzx         \_ my_web.3        nginx:1.14-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                        
i0nuldq0b6fy        my_web.4            nginx:1.15-alpine   swarm-worker1       Running             Running 52 seconds ago                       
m8ofettpaj9n        my_web.5            nginx:1.15-alpine   swarm-worker2       Running             Running 11 hours ago                         
zphfbb64m5t8        my_web.6            nginx:1.15-alpine   swarm-worker1       Running             Running 52 seconds ago

將鏡像更新到nginx:1.15.7-alpine

[root@swarm-manager ~]# docker service update --image nginx:1.15.7-alpine my_web 
my_web
overall progress: 6 out of 6 tasks 
1/6: running   
2/6: running   
3/6: running   
4/6: running   
5/6: running   
6/6: running   
verify: Service converged 

[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE                 ERROR               PORTS
kc2y0tedle3u        my_web.1            nginx:1.15.7-alpine   swarm-worker1       Running             Running 21 seconds ago                            
ws8ym7uxht1g         \_ my_web.1        nginx:1.15-alpine     swarm-worker1       Shutdown            Shutdown 21 seconds ago                           
v6luevjpk1of         \_ my_web.1        nginx:1.14-alpine     swarm-worker1       Shutdown            Shutdown 8 minutes ago                            
mebv7vaxnn3v        my_web.2            nginx:1.15.7-alpine   swarm-worker2       Running             Running 11 hours ago                              
unbw58qiakb9         \_ my_web.2        nginx:1.15-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
o6orjanarrk8         \_ my_web.2        nginx:1.14-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
rhyrbqpna2jm        my_web.3            nginx:1.15.7-alpine   swarm-worker2       Running             Running 11 hours ago                              
eqkbqmzyllxi         \_ my_web.3        nginx:1.15-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
zo6hgkztxzzx         \_ my_web.3        nginx:1.14-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
vnwofhlgrn3s        my_web.4            nginx:1.15.7-alpine   swarm-worker1       Running             Running about a minute ago                        
i0nuldq0b6fy         \_ my_web.4        nginx:1.15-alpine     swarm-worker1       Shutdown            Shutdown about a minute ago                       
pvfa2dhzqhod        my_web.5            nginx:1.15.7-alpine   swarm-worker2       Running             Running 11 hours ago                              
m8ofettpaj9n         \_ my_web.5        nginx:1.15-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
yciilsu8di50        my_web.6            nginx:1.15.7-alpine   swarm-worker1       Running             Running 3 minutes ago                             
zphfbb64m5t8         \_ my_web.6        nginx:1.15-alpine     swarm-worker1       Shutdown            Shutdown 3 minutes ago

Swarm 還有個方便的功能是回滾,若是更新後效果不理想,能夠經過 --rollback 快速恢復到更新以前的狀態。

請注意,--rollback 只能回滾到上一次執行 docker service update 以前的狀態,並不能無限制地回滾。

[root@swarm-manager ~]# docker service update --rollback my_web
my_web
rollback: manually requested rollback 
overall progress: rolling back update: 6 out of 6 tasks 
1/6: running   [>                                                  ] 
2/6: running   [>                                                  ] 
3/6: running   [>                                                  ] 
4/6: running   [>                                                  ] 
5/6: running   [>                                                  ] 
6/6: running   [>                                                  ] 
verify: Service converged 

[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE                 ERROR               PORTS
h3ajwiltt5si        my_web.1            nginx:1.15-alpine     swarm-worker1       Running             Running about a minute ago                        
kc2y0tedle3u         \_ my_web.1        nginx:1.15.7-alpine   swarm-worker1       Shutdown            Shutdown about a minute ago                       
ws8ym7uxht1g         \_ my_web.1        nginx:1.15-alpine     swarm-worker1       Shutdown            Shutdown 3 minutes ago                            
v6luevjpk1of         \_ my_web.1        nginx:1.14-alpine     swarm-worker1       Shutdown            Shutdown 11 minutes ago                           
mz9biuxzrjpl        my_web.2            nginx:1.15-alpine     swarm-worker2       Running             Running 11 hours ago                              
mebv7vaxnn3v         \_ my_web.2        nginx:1.15.7-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                             
unbw58qiakb9         \_ my_web.2        nginx:1.15-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
o6orjanarrk8         \_ my_web.2        nginx:1.14-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
7kjpoptdhxgl        my_web.3            nginx:1.15-alpine     swarm-worker2       Running             Running 11 hours ago                              
rhyrbqpna2jm         \_ my_web.3        nginx:1.15.7-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                             
eqkbqmzyllxi         \_ my_web.3        nginx:1.15-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
zo6hgkztxzzx         \_ my_web.3        nginx:1.14-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
dehr4bbs3e58        my_web.4            nginx:1.15-alpine     swarm-worker1       Running             Running 50 seconds ago                            
vnwofhlgrn3s         \_ my_web.4        nginx:1.15.7-alpine   swarm-worker1       Shutdown            Shutdown 51 seconds ago                           
i0nuldq0b6fy         \_ my_web.4        nginx:1.15-alpine     swarm-worker1       Shutdown            Shutdown 5 minutes ago                            
wshw5cdo3v43        my_web.5            nginx:1.15-alpine     swarm-worker2       Running             Running 11 hours ago                              
pvfa2dhzqhod         \_ my_web.5        nginx:1.15.7-alpine   swarm-worker2       Shutdown            Shutdown 11 hours ago                             
m8ofettpaj9n         \_ my_web.5        nginx:1.15-alpine     swarm-worker2       Shutdown            Shutdown 11 hours ago                             
uqejx1yrc6yx        my_web.6            nginx:1.15-alpine     swarm-worker1       Running             Running 58 seconds ago                            
yciilsu8di50         \_ my_web.6        nginx:1.15.7-alpine   swarm-worker1       Shutdown            Shutdown 59 seconds ago                           
zphfbb64m5t8         \_ my_web.6        nginx:1.15-alpine     swarm-worker1       Shutdown            Shutdown 6 minutes ago

十一、replicated mode vs global mode

Swarm 能夠在 service 建立或運行過程當中靈活地經過 --replicas 調整容器副本的數量,內部調度器則會根據當前集羣的資源使用情況在不一樣 node 上啓停容器,這就是 service 默認的 replicated mode。在此模式下,node 上運行的副本數有多有少,通常狀況下,資源更豐富的 node 運行的副本數更多,反之亦然。

除了 replicated mode,service 還提供了一個 globalmode,其做用是強制在每一個 node 上都運行一個且最多一個副本。

此模式特別適合須要運行 daemon 的集羣環境。好比要收集全部容器的日誌,就能夠 global mode 建立 service,在全部 node 上都運行 gliderlabs/logspout 容器,即便以後有新的 node 加入,swarm 也會自動在新 node 上啓動一個 gliderlabs/logspout 副本。

[root@swarm-manager ~]# docker service create \
>        --mode global \
>        --name logspout \
>        --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
>        gliderlabs/logspout
su2vyv5n6vaazpj8ds3cvl14y
overall progress: 2 out of 2 tasks 
byxpoc4rgi45: running   
jfpu6n3qt4gq: running   
verify: Service converged 
[root@swarm-manager ~]# docker service ps logspout
ID                  NAME                                 IMAGE                        NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
z1633gpkqvh7        logspout.jfpu6n3qt4gqnoqs8lv36o5fn   gliderlabs/logspout:latest   swarm-worker1       Running             Running 21 seconds ago                       
8uibx9n8dt56        logspout.byxpoc4rgi45jz1d8vi5hgrog   gliderlabs/logspout:latest   swarm-worker2       Running             Running 11 hours ago

能夠經過 docker service inspect 查看 service 的 mode。

[root@swarm-manager ~]# docker service inspect logspout 
......
"Mode": {
                "Global": {}
            },
......

這裏是 Global,若是建立 service 時不指定,默認是 Replicated

不管採用 global mode 仍是 replicated mode,副本運行在哪些節點都是由 Swarm 決定的,做爲用戶咱們有沒有可能精細控制 service 的運行位置呢?

能,使用 label

十二、Label 控制 Service 的位置

邏輯分兩步:

  1. 爲每一個 node 定義 label。
  2. 設置 service 運行在指定 label 的 node 上。

label 能夠靈活描述 node 的屬性,其形式是 key=value,用戶能夠任意指定,例如將 swarm-worker1 做爲測試環境,爲其添加 label env=test

[root@swarm-manager ~]# docker node update --label-add env=test swarm-worker1 
swarm-worker1
[root@swarm-manager ~]# docker node inspect swarm-worker1 
[
    {
        "ID": "jfpu6n3qt4gqnoqs8lv36o5fn",
        "Version": {
            "Index": 388
        },
        "CreatedAt": "2018-12-25T02:43:42.19392138Z",
        "UpdatedAt": "2018-12-26T03:52:58.276285242Z",
        "Spec": {
            "Labels": {
                "env": "test"

對應的,將 swarm-worker2 做爲生產環境,添加 label env=prod

[root@swarm-manager ~]# docker node update --label-add env=prod swarm-worker2 
swarm-worker2
[root@swarm-manager ~]# docker node inspect swarm-worker2
[
    {
        "ID": "byxpoc4rgi45jz1d8vi5hgrog",
        "Version": {
            "Index": 389
        },
        "CreatedAt": "2018-12-25T02:43:44.590690728Z",
        "UpdatedAt": "2018-12-26T03:54:09.646331335Z",
        "Spec": {
            "Labels": {
                "env": "prod"
            },

如今部署 service 到測試環境:

[root@swarm-manager ~]# docker service create \
> --constraint node.labels.env==test \
> --replicas 3 \
> --name my_web \
> --publish 8080:80 \
> nginx:1.15.7-alpine
[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
9huefun6bfw8        my_web.1            nginx:1.15.7-alpine   swarm-worker1       Running             Running 24 seconds ago                       
tdyah0y5wflf        my_web.2            nginx:1.15.7-alpine   swarm-worker1       Running             Running 24 seconds ago                       
6dssuoosib4w        my_web.3            nginx:1.15.7-alpine   swarm-worker1       Running             Running 24 seconds ago

--constraint node.labels.env==test 限制將 service 部署到 label=test 的 node,即 swarm-worker1。從部署結果看,三個副本所有都運行在 swarm-worker1 上。

能夠經過 docker service inspect 查看 --constraint 的設置:

[root@swarm-manager ~]# docker service inspect my_web --pretty 

ID:     vgcmd2r8pznw6koqwf4l896hv
Name:       my_web
Service Mode:   Replicated
 Replicas:  3
Placement:
 Constraints:   [node.labels.env==test]

更新 service,將其遷移到生產環境:

[root@swarm-manager ~]# docker service update --constraint-add node.labels.env==prod my_web
my_web
overall progress: 3 out of 3 tasks 
1/3: running   
2/3: running   
3/3: running   
verify: Service converged 
[root@swarm-manager ~]# docker service ps my_web 
ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE          ERROR               PORTS
0z09qjd1iaj8        my_web.1            nginx:1.15.7-alpine   swarm-worker2       Running             Running 13 hours ago                       
9huefun6bfw8         \_ my_web.1        nginx:1.15.7-alpine   swarm-worker1       Shutdown            Shutdown 2 hours ago                       
wi9ab6ubjd6l        my_web.2            nginx:1.15.7-alpine   swarm-worker2       Running             Running 13 hours ago                       
tdyah0y5wflf         \_ my_web.2        nginx:1.15.7-alpine   swarm-worker1       Shutdown            Shutdown 2 hours ago                       
yk7a48parkrn        my_web.3            nginx:1.15.7-alpine   swarm-worker2       Running             Running 13 hours ago                       
6dssuoosib4w         \_ my_web.3        nginx:1.15.7-alpine   swarm-worker1       Shutdown

刪除並添加新的 constraint,設置 node.labels.env==prod,最終全部副本都遷移到了 swarm-worker2

label 還能夠跟 global 模式配合起來使用,好比只收集生產環境中容器的日誌。

[root@swarm-manager ~]# docker service create \
>        --mode global \
>        --constraint node.labels.env==prod \
>        --name logspout \
>        --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
>        gliderlabs/logspout
[root@swarm-manager ~]# docker service ps logspout 
ID                  NAME                                 IMAGE                        NODE                DESIRED STATE       CURRENT STATE          ERROR               PORTS
6kvcjfddostu        logspout.byxpoc4rgi45jz1d8vi5hgrog   gliderlabs/logspout:latest   swarm-worker2       Running             Running 12 hours ago

只有 swarm-worker2 節點上纔會運行 logspout。

1三、 Health Check

Docker 只能從容器啓動進程的返回代碼判斷其狀態,而對於容器內部應用的運行狀況基本沒有了解。

執行 docker run 命令時,一般會根據 Dockerfile 中的 CMD 或 ENTRYPOINT 啓動一個進程,這個進程的狀態就是 docker ps STATUS 列顯示容器的狀態。

[root@swarm-worker2 ~]# docker ps -a
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS                   PORTS               NAMES
7ee058b8f935        gliderlabs/logspout:latest   "/bin/logspout"          2 minutes ago       Up 2 minutes             80/tcp              logspout.byxpoc4rgi45jz1d8vi5hgrog.6kvcjfddostuz9bv6bltx9vl1
b92cb165214c        nginx:1.15.7-alpine          "nginx -g 'daemon of…"   2 hours ago         Up 2 hours               80/tcp              my_web.2.wi9ab6ubjd6lyzc4g05f863w6
866b27b17a11        nginx:1.15.7-alpine          "nginx -g 'daemon of…"   2 hours ago         Up 2 hours               80/tcp              my_web.1.0z09qjd1iaj8aq6ih039od4bi
ae8a52638e94        nginx:1.15.7-alpine          "nginx -g 'daemon of…"   2 hours ago         Up 2 hours               80/tcp              my_web.3.yk7a48parkrnct7r74bp5m24v
97fd40918b21        nginx:1.15-alpine            "nginx -g 'daemon of…"   3 hours ago         Exited (0) 2 hours ago                       my_web.3.7kjpoptdhxgldkpgo8gib29df
9b62f2347853        nginx:1.15-alpine            "nginx -g 'daemon of…"   3 hours ago         Exited (0) 2 hours ago                       my_web.5.wshw5cdo3v43zz4do2zi30hfi

命令顯示:

  1. 有的容器正在運行,狀態爲 UP
  2. 有的容器已經正常中止了,狀態是 Exited (0)
  3. 有的則因發生故障中止了,退出代碼爲非 0,例如 Exited (137)Exited (1) 等。

即便容器狀態是 UP,也不能保證應用沒有問題。web server 雖然沒有崩潰,但若是老是返回 HTTP 500 - Internal Server Error ,對應用來講這就是很嚴重的故障。

Docker 支持的 Health Check 能夠是任何一個單獨的命令,Docker 會在容器中執行該命令,若是返回 0,容器被認爲是 healthy,若是返回 1,則爲 unhealthy

對於提供 HTTP 服務接口的應用,經常使用的 Health Check 是經過 curl 檢查 HTTP 狀態碼,好比:

curl --fail http://localhost:8080/ || exit 1

若是 curl 命令檢測到任何一個錯誤的 HTTP 狀態碼,則返回 1,Health Check 失敗。

[root@swarm-manager ~]# docker service create --name my_db \
        --health-cmd "curl --fail http://localhost:8091/pools || exit 1" \
        couchbase
x12gbja26pb9unlx2l2of5q7x
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged 
[root@swarm-manager ~]# docker service ps my_db 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
80c0p3cs7y7k        my_db.1             couchbase:latest    swarm-worker1       Running             Running about a minute ago

--health-cmd Health Check 的命令,還有幾個相關的參數:

  1. --timeout 命令超時的時間,默認 30s。
  2. --interval 命令執行的間隔時間,默認 30s。
  3. --retries 命令失敗重試的次數,默認爲 3,若是 3 次都失敗了則會將容器標記爲 unhealthy。swarm 會銷燬並重建 unhealthy 的副本。

經過 docker ps 能夠查看到容器的狀態爲 healthy

[root@swarm-worker1 ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS                                                        NAMES
072cad1caa46        couchbase:latest    "/entrypoint.sh couc…"   2 minutes ago       Up 2 minutes (healthy)   8091-8096/tcp, 11207/tcp, 11210-11211/tcp, 18091-18096/tcp   my_db.1.80c0p3cs7y7kc9836ngxaetat

下面模擬一個 unhealthy 的場景,curl 指向一個不存在的 url。(未測試成功)

docker service create --name my_db \
       --health-cmd "curl --fail http://localhost:8091/non-exist || exit 1" \
       couchbase

容器被標記爲 unhealthy,其緣由是 curl 連續三次返回 404 錯誤。

Docker 默認只能經過容器進程的返回碼判斷容器的狀態,Health Check 則可以從業務角度判斷應用是否發生故障,是否須要重啓。

1四、使用 Secret

咱們常常要向容器傳遞敏感信息,最多見的莫過於密碼了。好比:

docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql

在啓動 MySQL 容器時咱們經過環境變量 MYSQL_ROOT_PASSWORD 設置了 MySQL 的管理員密碼。不過密碼是以明文的形式寫在 docker run 命令中,有潛在的安全隱患。

爲了解決這個問題,docker swarm 提供了 secret 機制,容許將敏感信息加密後保存到 secret 中,用戶能夠指定哪些容器可使用此 secret。

若是使用 secret 啓動 MySQL 容器,方法是:

一、在 swarm manager 中建立 secret my_secret_data,將密碼保存其中。

[root@swarm-manager ~]# echo "my-secret-pw" | docker secret create my_secret_data -
i0n061n5kpat7q8kmp4q8n2ud

二、啓動 MySQL service,並指定使用 secret my_secret_data

[root@swarm-manager ~]# docker service create \
>         --name mysql \
>         --secret source=my_secret_data,target=mysql_root_password \
>         -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
>         mysql:latest
t92t9rmooxab9netuxs20itzf
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

① source 指定容器使用 secret 後,secret 會被解密並存放到容器的文件系統中,默認位置爲 /run/secrets/ --secretsource=my_secret_data,target=mysql_root_password 的做用就是指定使用 secret my_secret_data,而後把器解密後的內容保存到容器 /run/secrets/mysql_root_password 文件中,文件名稱 mysql_root_passwordtarget 指定。

② 環境變量 MYSQL_ROOT_PASSWORD_FILE 指定從 /run/secrets/mysql_root_password 中讀取並設置 MySQL 的管理員密碼。

  1. 問:在第一步建立 secret 時,不也是使用了明文嗎?這跟在環境變量中直接指定密碼有什麼不一樣呢?

答:在咱們的例子中建立 secret 和使用 secret 是分開完成的,其好處是將密碼和容器解耦合。secret 能夠由專人(好比管理員)建立,而運行容器的用戶只需使用 secret 而不須要知道 secret 的內容。也就是說,例子中的這兩個步驟能夠由不一樣的人在不一樣的時間完成。

  1. 問:secret 是以文件的形式 mount 到容器中,容器怎麼知道去哪裏讀取 secret 呢?

答:這須要 image 的支持。若是 image 但願它部署出來的容器可以從 secret 中讀取數據,那麼此 image 就應該提供一種方式,讓用戶可以指定 secret 的位置。最經常使用的方法就是經過環境變量,Docker 的不少官方 image 都是採用這種方式。好比 MySQL 鏡像同時提供了 MYSQL_ROOT_PASSWORDMYSQL_ROOT_PASSWORD_FILE 兩個環境變量。用戶能夠用 MYSQL_ROOT_PASSWORD 顯示地設置管理員密碼,也能夠經過 MYSQL_ROOT_PASSWORD_FILE 指定 secret 路徑。

Secret 的使用場景

secret 可用於管理:

  1. 用戶名和密碼。
  2. TLS 證書。
  3. SSH 祕鑰。
  4. 其餘小於 500 KB 的數據。

secret 只能在 swarm service 中使用。普通容器想使用 secret,能夠將其包裝成副本數爲 1 的 service。

數據中心有三套 swarm 環境,分別用於開發、測試和生產。對於同一個應用,在不一樣的環境中使用不一樣的用戶名密碼。咱們能夠在三個環境中分別建立 secret,不過使用相同的名字,好比 usernamepassword。應用部署時只須要指定 secret 名字,這樣咱們就能夠用同一套腳本在不一樣的環境中部署應用了。

除了敏感數據,secret 固然也能夠用於非敏感數據,好比配置文件。不過目前新版本的 Docker 提供了 config 子命令來管理不須要加密的數據。config 與 secret 命令的使用方法徹底一致。

Secret 的安全性

當在 swarm 中建立 secret 時,Docker 經過 TLS 鏈接將加密後的 secret 發送給因此的 manager 節點。

secret 建立後,即便是 swarm manager 也沒法查看 secret 的明文數據,只能經過 docker secret inspect 查看 secret 的通常信息。

[root@swarm-manager ~]# docker secret inspect my_secret_data 
[
    {
        "ID": "i0n061n5kpat7q8kmp4q8n2ud",
        "Version": {
            "Index": 2820
        },
        "CreatedAt": "2019-01-02T02:11:42.350879188Z",
        "UpdatedAt": "2019-01-02T02:11:42.350879188Z",
        "Spec": {
            "Name": "my_secret_data",
            "Labels": {}
        }
    }
]

只有當 secret 被指定的 service 使用是,Docker 纔會將解密後的 secret 以文件的形式 mount 到容器中,默認的路徑爲/run/secrets/<secret_name>。例如在前面 MySQL 的例子中,咱們能夠在容器中查看 secret。

[root@swarm-manager ~]# docker service ps mysql 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
fd9ggspslrvv        mysql.1             mysql:latest        swarm-worker1       Running             Running 5 minutes ago    
[root@swarm-worker1 ~]# docker exec -it mysql.1.fd9ggspslrvv3et2s1lhj2drb cat /run/secrets/mysql_root_password
my-secret-pw

當容器中止運行,Docker 會 unmount secret,並從節點上清除。

舉例

建立一個 MySQL service,將密碼保存到 secret 中。咱們還會建立一個 WordPress service,它將使用 secret 鏈接 MySQL。這個例子將展現如何用 secret 避免在 image 中存放敏感信息,或者在命令行中直接傳遞敏感數據。

實驗步驟以下:

1\建立 secret

建立 secret 存放 MySQL 的管理員密碼。

[root@swarm-manager ~]# openssl rand -base64 20 | docker secret create mysql_root_password -
weiz3fof9qe56sjtztvixvjco

注意 ag7injh6juonwl09lq8st36o8 是新建立的 service 的 ID,而非 service 的內容。

上面這種方式是從標準輸入讀取 secret 的內容,也能夠指定從文件中讀取,例如:

openssl rand -base64 20 > password.txt
docker secret create my_password ./password.txt

通常狀況下,應用不會直接用 root 密碼訪問 MySQL。咱們會建立一個單獨的用戶 workpress,密碼存放到 secret mysql_password中。

[root@swarm-manager ~]# openssl rand -base64 20 | docker secret create mysql_password -
zv55ejrqimhflaajx5f6oez8e
[root@swarm-manager ~]# docker secret ls
ID                          NAME                  DRIVER              CREATED             UPDATED
zv55ejrqimhflaajx5f6oez8e   mysql_password                            45 seconds ago      45 seconds ago
weiz3fof9qe56sjtztvixvjco   mysql_root_password                       2 minutes ago       2 minutes ago
建立自定義的 overlay 網絡

MySQL 經過 overlay 網絡 mysql_private 與 WordPress 通訊,不須要將 MySQL service 暴露給外部網絡和其餘容器。

[root@swarm-manager ~]# docker network create -d overlay mysql_private
45j2p7ley2b6s8buyxohi9a27
建立 MySQL service

命令以下:

[root@swarm-manager ~]# docker service create \
>      --name mysql \
>      --network mysql_private \
>      --secret source=mysql_root_password,target=mysql_root_password \
>      --secret source=mysql_password,target=mysql_password \
>      -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
>      -e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \
>      -e MYSQL_USER="wordpress" \
>      -e MYSQL_DATABASE="wordpress" \
>      mysql:latest
8xg5dmt7rn6ecglv1frzcd9h0
overall progress: 1 out of 1 tasks 
1/1: running   
verify: Service converged 

[root@swarm-manager ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
8xg5dmt7rn6e        mysql               replicated          1/1                 mysql:latest        
[root@swarm-manager ~]# docker service ps mysql
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
evv84yno073p        mysql.1             mysql:latest        swarm-worker1       Running             Running about a minute ago

MYSQL_DATABASE 指明建立數據庫 wordpress

MYSQL_USERMYSQL_PASSWORD_FILE 指明建立數據庫用戶 workpress,密碼從 secret mysql_password 中讀取。

有關 mysql 鏡像環境變量更詳細的使用方法可參考 https://hub.docker.com/_/mysql/

建立 WordPress service

MySQL service 已就緒,如今建立 WordPress service。命令以下:

[root@swarm-manager ~]# docker service create \
>      --name wordpress \
>      --network mysql_private \
>      --publish 30000:80 \
>      --secret source=mysql_password,target=wp_db_password \
>      -e WORDPRESS_DB_HOST="mysql:3306" \
>      -e WORDPRESS_DB_NAME="wordpress" \
>      -e WORDPRESS_DB_USER="wordpress" \
>      -e WORDPRESS_DB_PASSWORD_FILE="/run/secrets/wp_db_password" \
>      wordpress:latest
dtunutpigfh8a07xsk33fibe6
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

WORDPRESS_DB_HOST 指明 MySQL service 地址 mysql:3306,這裏用到了 DNS。

WORDPRESS_DB_NAME 指明 WordPress 的數據庫爲 wordpress,與前面 MYSQL_DATABASE 一致。

WORDPRESS_DB_USER 指明鏈接 WordPress 數據庫的用戶爲 wordpress,與前面 MYSQL_USER 一致。

WORDPRESS_DB_PASSWORD_FILE 指明數據庫的用戶 wordpress 的密碼,從 secret mysql_password 中獲取。

有關 wordpress 鏡像環境變量更詳細的使用方法可參考 https://hub.docker.com/_/wordpress/

驗證 WordPress

訪問 http://[swarm_master_ip]:30000/

能正常顯示初始化界面,代表 WordPress 已經鏈接到 MySQL,部署成功。

1五、stack

回憶一下前面部署 WordPress 應用的過程:

  1. 首先建立 secret。
  2. 而後建立 MySQL service,這是 WordPress 依賴的服務。
  3. 最後建立 WordPress service。

也就是說,這個應用包含了兩個 service:MySQL 和 WordPress,它們之間有明確的依賴關係,必須先啓動 MySQL。

爲了保證這個依賴關係,咱們控制了 docker secretdocker service 命令的執行順序,只不過這個過程是手工完成的。

1546397442360

稍微複雜一點的是第三步,經過 if 判斷 MySQL service 是否運行,若是是,則運行 WordPress service,不然經過 while 繼續等待,直到 MySQL 運行。

這個腳本大致上可以工做,實現了自動化,但有兩個缺點:

  1. 目前只有兩個 service,還比較簡單。如今的應用一般都包含多個 service,特別是採用 microservices 架構的應用,幾十個 service 是很正常的。用 shell 腳本啓動和管理如此多的 service 將是一件很是有挑戰的任務。
  2. whileif 維護 service 之間的依賴關係也是頗有挑戰的,容易出錯。並且如何判斷 service 正常運行也不是件容易的事,腳本中只簡單檢查了 service 是否存在,並無考慮 service 的實際運行狀態。

咱們但願有一種更高效和可靠的方法來部署基於 service 的應用,這就是 stack。

stack 包含一系列 service,這些 service 組成了應用。stack 經過一個 YAML 文件定義每一個 service,並描述 service 使用的資源和各類依賴。

WordPress 的 stack 版本

若是將前面 WordPress 用 stack 來定義,YAML 文件能夠是這樣:

[root@swarm-manager ~]#  vim wordpress.yml


version: '3.1'

services:
  db:
    image: mysql:latest
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD_FILE: /run/secrets/ad_password
    secrets:
      - db_root_password
      - db_password
    
    wordpress:
      depends_on:
        - db
      image: wordpress:latest
      port:
        - "8000:80"
      environment:
        WORDPRESS_DB_HOST: db:3306
        WORDPRESS_DB_USER: wordpress
        WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
      secrets:
        - db_password


secrets:
  db_password:
    file: db_password.txt
  db_root_password:
    file: db_root_password.txt

volumes:
    db_data:

YAML 是一種閱讀性很強的文本格式,上面這個 stack 中定義了三種資源:service、secret 和 volume。

services 定義了兩個 service:dbwordpress

secrets 定義了兩個 secret:db_passworddb_root_password,在 service dbwordpress 的定義中引用了這兩個 secret。

volumes 定義了一個 volume:db_data,service db 使用了此 volume。

wordpress 經過 depends_on 指定本身依賴 db 這個 service。Docker 會保證當 db 正常運行後再啓動 wordpress

能夠在 YAML 中定義的元素遠遠不止這裏看到的這幾個,完整列表和使用方法可參考文檔 https://docs.docker.com/compose/compose-file/

定義好了 stack YAML 文件,就能夠經過 docker stack deploy 命令部署應用。

[root@swarm-manager ~]# vim db_password.txt
[root@swarm-manager ~]# vim db_root_password.txt
[root@swarm-manager ~]# docker stack deploy -c wordpress.yml wpstack
Creating secret wpstack_db_root_password
Creating secret wpstack_db_password
Creating service wpstack_db
Creating service wpstack_wordpress
[root@swarm-manager ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
b2tblr32vu8g        wpstack_db          replicated          1/1                 mysql:latest        
wiwtgohw332z        wpstack_wordpress   replicated          1/1                 wordpress:latest    *:8000->80/tcp

部署完成後能夠經過相關命令查看各類資源的狀態。

[root@swarm-manager ~]# docker stack services wpstack 
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
b2tblr32vu8g        wpstack_db          replicated          1/1                 mysql:latest        
wiwtgohw332z        wpstack_wordpress   replicated          1/1                 wordpress:latest    *:8000->80/tcp
[root@swarm-manager ~]# docker stack ps wpstack 
ID                  NAME                      IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR                       PORTS
e98ttrud0oga        wpstack_wordpress.1       wordpress:latest    swarm-worker2       Running             Running 7 days ago                                       
3ko8t5y3ja9e         \_ wpstack_wordpress.1   wordpress:latest    swarm-worker2       Shutdown            Failed 7 days ago            "task: non-zero exit (1)"   
qmsyq2ahutxp        wpstack_db.1              mysql:latest        swarm-worker1       Running             Running about a minute ago                               
l0fs3ijwqm5b         \_ wpstack_db.1          mysql:latest        swarm-worker1       Shutdown            Failed about a minute ago    "task: non-zero exit (1)"   
[root@swarm-manager ~]# docker secret ls
ID                          NAME                       DRIVER              CREATED             UPDATED
zv55ejrqimhflaajx5f6oez8e   mysql_password                                 About an hour ago   About an hour ago
weiz3fof9qe56sjtztvixvjco   mysql_root_password                            About an hour ago   About an hour ago
ssqn3cndzy9vh5mw8285td2gq   wpstack_db_password                            9 minutes ago       9 minutes ago
fdudslxllj30van0yyyde4e7o   wpstack_db_root_password                       9 minutes ago       9 minutes ago

若是想更新 stack 的某些屬性,直接修改 YAML 文件,而後從新部署。好比將 WordPress 的端口由 8000 改成 8888

ports:
      - "8888:80"

再次執行 docker stack deploy 命令。

[root@swarm-manager ~]# docker stack deploy -c wordpress.yml wpstack
Updating service wpstack_db (id: b2tblr32vu8gzqxu33iv0xct1)
Updating service wpstack_wordpress (id: wiwtgohw332z20o2snocxukh4)
[root@swarm-manager ~]# docker stack ps wpstack
ID                  NAME                      IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR                       PORTS
jp91ygjocy26        wpstack_wordpress.1       wordpress:latest    swarm-worker2       Running             Running 7 days ago                                    
q4zoh1z2ebv5         \_ wpstack_wordpress.1   wordpress:latest    swarm-worker1       Shutdown            Shutdown 22 seconds ago                               
ixmhlli95l2e         \_ wpstack_wordpress.1   wordpress:latest    swarm-worker2       Shutdown            Failed 7 days ago         "task: non-zero exit (1)"   
yn0n3fhnawvr         \_ wpstack_wordpress.1   wordpress:latest    swarm-worker2       Shutdown            Failed 7 days ago         "task: non-zero exit (1)"   
2w9dhvz3ifzx         \_ wpstack_wordpress.1   wordpress:latest    swarm-worker2       Shutdown            Failed 7 days ago         "task: non-zero exit (1)"   
qmsyq2ahutxp        wpstack_db.1              mysql:latest        swarm-worker1       Running             Running 3 minutes ago                                 
l0fs3ijwqm5b         \_ wpstack_db.1          mysql:latest        swarm-worker1       Shutdown            Failed 3 minutes ago      "task: non-zero exit (1)"

爲了更新端口,swarm 啓動了一個新的 wpstack_wordpress容器,以前的容器已經被 shutdown。

要刪除 stack 也很簡單:

[root@swarm-manager ~]# docker stack rm wpstack
Removing service wpstack_db
Removing service wpstack_wordpress
Removing secret wpstack_db_root_password
Removing secret wpstack_db_password
Removing network wpstack_default

docker stack rm 會將 stack 相關的因此資源清除乾淨。

stack 將應用所包含的 service,依賴的 secret、voluem 等資源,以及它們之間的關係定義在一個 YAML 文件中。相比較手工執行命令或是腳本,stack 有明顯的優點。

  1. YAML 描述的是 What,是 stack 最終要達到的狀態。
    好比 service 有幾個副本?使用哪一個 image?映射的端口是什麼?而腳本則是描述如何執行命令來達到這個狀態,也就是 How。顯而易見,What 更直觀,也更容易理解。至於如何將 What 翻譯成 How,這就是 Docker swarm 的任務了,用戶只須要告訴 Docker 想達到什麼效果。
  2. 重複部署應用變得很是容易。
    部署應用所須要的一切信息都已經寫在 YAML 中,要部署應用只需一條命令 docker stack deploy。stack 的這種自包含特性使得在不一樣的 Docker 環境中部署應用變得極其簡單。在開發、測試和生成環境中部署能夠徹底採用同一份 YAML,並且每次部署的結果都是一致的。
  3. 能夠像管理代碼同樣管理部署。 YAML 本質上將應用的部署代碼化了,任何對應用部署環境的修改均可以經過修改 YAML 來實現。能夠將 YAML 歸入到版本控制系統中進行管理,任何對 YAML 的修改都會被記錄和跟蹤,甚至能夠像評審代碼同樣對 YAML 執行 code review。應用部署再也不是一個黑盒子,也再也不是經驗豐富的工程師專有的技能,因此的細節都在 YAML 中,清晰可見。
相關文章
相關標籤/搜索