Docker系列(十四):Docker Swarm集羣

一、Swarm簡介

Swarm是Docker官方提供的一款集羣管理工具,其主要作用是把若干臺Docker主機抽象爲一個整體,並且通過一個入口統一管理這些Docker主機上的各種Docker資源。Swarm和Kubernetes比較類似,但是更加輕便,具有的功能也較kubernetes更少一些。

Swarm 在 Docker 1.12 版本之前屬於一個獨立的項目,在 Docker 1.12 版本發佈之後,該項目合併到了 Docker 中,成爲 Docker 的一個子命令。目前,Swarm 是 Docker 社區提供的唯一一個原生支持 Docker 集羣管理的工具。它可以把多個 Docker 主機組成的系統轉換爲單一的虛擬 Docker 主機,使得容器可以組成跨主機的子網網絡。

在多臺機器上部署Docker,組成一個Docker集羣,並把整個集羣的資源抽象成資源池,使用者部署Docker應用的時候,只需要將應用交給Swarm,Swarm會根據整個集羣資源的使用情況來分配資源給部署的Docker應用,可以將這個集羣的資源利用率達到最大。

二、Docker Swarm的相關概念

Swarm

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

從 v1.12 開始,集羣管理和編排功能已經集成進 Docker Engine。當 Docker Engine 初始化了一個Swarm或者加入到一個存在的Swarm時,它就啓動了 Swarm Mode。沒啓動Swarm Mode時,Docker執行的是容器命令;運行Swarm Mode後,Docker增加了編排service的能力。

Docker允許在同一個Docker主機上既運行Swarm Service,又運行單獨的容器。

Node

Swarm中的每個Docker Engine都是一個node,有兩種類型的 node:manager 和worker。

Manager:接收客戶端服務定義,將任務發送到worker節點;維護集羣期望狀態和集羣管理功能及Leader選舉。默認情況下manager節點也會運行任務,也可以配置只做管理任務。

Worker:接收並執行從管理節點分配的任務,並報告任務當前狀態,以便管理節點維護每個服務期望狀態。

爲了向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中啓動一個nginx服務,使用的鏡像是 nginx:latest,副本數爲3。

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

運行了一段時間,worker2突然宕機了,manager監控到這個故障,於是立即在 worker3上啓動了一個新的nginx容器。

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

三、Docker Swarm 架構

Docker系列(十四):Docker Swarm集羣

上圖演示了一個標準的Docker Swarm集羣結構,它可能對應了一到多臺的實際服務器。每臺服務器上都裝有Docker並且開啓了基於HTTP的DockerAPI。

這個集羣中有一個SwarmManager的管理者,用來管理集羣中的容器資源。管理者的管理對象不是服務器層面而是集羣層面的,也就是說通過Manager,我們只能籠統地向集羣發出指令而不能具體到某臺具體的服務器上要幹什麼(這也是Swarm的根本所在)。

至於具體的管理實現方式,Manager向外暴露了一個HTTP接口,外部用戶通過這個HTTP接口來實現對集羣的管理。對於稍微大一點的集羣,最好是拿出一臺實際的服務器作爲專門的管理者,作爲學習而言,也可以把管理者和被管理者放在一臺服務器上。

四、Docker Swarm的特性

Swarm特點:

  1. Docker Engine集成集羣管理
    使用Docker Engine CLI 創建一個Docker Engine的Swarm模式,在集羣中部署應用程序服務。
  2. 去中心化設計
    Swarm角色分爲Manager和Worker節點,Manager節點故障不影響應用使用。
  3. 擴容縮容
    可以聲明每個服務運行的容器數量,通過添加或刪除容器數自動調整期望的狀態。
  4. 期望狀態協調
    Swarm Manager節點不斷監視集羣狀態,並調整當前狀態與期望狀態之間的差異。例如,設置一個服務運行10個副本容器,如果兩個副本的服務器節點崩潰,Manager將創建兩個新的副本替代崩潰的副本。並將新的副本分配到可用的worker節點。
  5. 多主機網絡
    可以爲服務指定overlay網絡。當初始化或更新應用程序時,Swarm manager會自動爲overlay網絡上的容器分配IP地址。
  6. 服務發現
    Swarm manager節點爲集羣中的每個服務分配唯一的DNS記錄和負載均衡VIP。可以通過Swarm內置的DNS服務器查詢集羣中每個運行的容器。
  7. 負載均衡
    實現服務副本負載均衡,提供入口訪問。也可以將服務入口暴露給外部負載均衡器再次負載均衡。
  8. 安全傳輸
    Swarm中的每個節點使用TLS相互驗證和加密,確保安全的其他節點通信。
  9. 滾動更新
    升級時,逐步將應用服務更新到節點,如果出現問題,可以將任務回滾到先前版本。

五、Docker Swarm的部署

環境準備:

節點 IP地址 主機名 操作系統 Docker版本號
manager 192.168.49.41 docker01.contoso.com CentOS 7.4 1.13.1
worker 192.168.49.42 docker02.contoso.com CentOS 7.4 1.13.1
worker 192.168.49.43 docker03.contoso.com CentOS 7.4 1.13.1

1、在manager節點上

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

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

```
docker swarm join \
--token SWMTKN-1-53n4uazhxx4yf9f2wmebbm0q7nfecrchr485ar8uj1jyw9l7om-3tjc6s01ha8dh49gnkvru81uq \
192.168.49.41:2377
```

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

2、在work節點上

[root@docker02 ~]# docker swarm join \
>     --token SWMTKN-1-53n4uazhxx4yf9f2wmebbm0q7nfecrchr485ar8uj1jyw9l7om-3tjc6s01ha8dh49gnkvru81uq \
>     192.168.49.41:2377
This node joined a swarm as a worker.
[root@docker03 ~]# docker swarm join \
>     --token SWMTKN-1-53n4uazhxx4yf9f2wmebbm0q7nfecrchr485ar8uj1jyw9l7om-3tjc6s01ha8dh49gnkvru81uq \
>     192.168.49.41:2377
This node joined a swarm as a worker.

3、在manager上查看swarm集羣狀態

[root@docker01 ~]# docker node ls
ID                           HOSTNAME              STATUS  AVAILABILITY  MANAGER STATUS
fkqck55wao2dr23t9jq42e6e7    docker03.contoso.com  Ready   Active        
q1gkyqchf5nz20qgzy2dfeyk9    docker02.contoso.com  Ready   Active        
rycnd1olbld9kizgb1h5rf8rs *  docker01.contoso.com  Ready   Active        Leader

可以看到,docker01的MANAGER STATUS顯示爲Leader,也就是管理節點,而其他兩個節點都作爲worker節點,這樣三臺服務器組成的一個swarm集羣就完成了。

六、Docker Swarm管理相關命令

docker swarm常用的管理命令分如下幾類:

  • docker swarm: 用於配置機器集羣,包括管理manager和worker兩類機器節點的增刪
  • docker node: 提供對機器節點的管理
  • docker service: 提供了service創建,更新,回滾,task擴展收縮等功能

集羣管理:docker swarm

用法:

​ Usage: docker swarm COMMAND

命令:

​ init:初始化一個swarm集羣

​ join:作爲一個worker/管理節點加入一個集羣

​ join-token:管理加入swarm集羣的token

​ leave:離開swarm集羣

​ unlock:解除鎖定swarm。使用一個用戶提供的unlock密鑰來解除一個鎖定的manager節點,該命令僅用於重新激活一個設置了autolock且docker服務發生了重啓的manager節點。當autolock啓用後unlock密鑰會打印在顯示信息中,當然也可以使用docker swarm unlock-key命令獲取。

​ unlock-key:解除鎖定swarm的key

​ update:更新swarm集羣

常用選項:

​ docker swarm init --advertise-addr <manager_ip> # 指定初始化ip地址節點

​ docker swarm init --force-new-cluster # 去除本地之外的所有管理節點

​ docker swarm init --task-history-limit int # 保留任務歷史記錄的最大值(整數)

​ docker swarm join --advertise-addr string # 指定管理節點地址

​ docker swarm join --token string # 指定加入swarm集羣的token

​ docker swarm leave -f # 強制執行將當前節點移除swarm集羣,忽略警告

節點管理:docker node

用法:

​ Usage: docker node COMMAND

命令:

​ demote:將一個或多個manager節點降級爲worker節點

​ inspect:顯示一個或多個節點的詳細信息

​ ls:列出swarm集羣中的節點

​ promote:將一個或多個節點從worker節點提升爲manager節點

​ ps:列出在一個或多個節點上運行的任務,默認顯示當前節點上運行的任務

​ rm:從swarm集羣中移除一個或多個節點

​ update:更新一個節點

常用選項:

​ docker node inspect 主機名 # 查看節點的詳細信息,默認json格式

​ docker node inspect --pretty 主機名 # 查看節點的相信信息,平鋪格式

​ docker node update --availability

​ # 設置節點的狀態("active"-正常,pause-暫停,drain-排除自身worker任務)

服務管理:docker service

用法:

​ Usage: docker service COMMAND

命令:

​ create:創建一個新的service

​ inspect:顯示一個或多個service的詳細信息

​ ls:列出所有services

​ ps:列出指定service的所有任務

​ rm:移除一個或多個service

​ scale:擴展一個或多個service的replica數目

​ update:更新指定的service

常用選項:

​ docker service create --replicas 副本數 # 創建服務時指定副本個數

​ docker service create --name 名字 # 創建服務時指定容器名字

​ docker service create --update-delay 秒數 # 創建服務時指定每次與容器之間的更新間隔(單位是秒)

​ docker service create --update-parallelism 個數 # 創建服務時指定更新時同時進行更新的數量,默認爲1

​ docker service create --update-failure-action 類型 # 創建服務時指定任務容器更新失敗時的模式,其中pause爲停止,continue爲繼續,默認爲pause

​ docker service create --rollback-mointor 秒數 # 創建服務時指定每次容器與容器之間的回滾時間間隔

​ docker service create --rollback-max-failure-ratio .數值 # 創建服務時指定回滾故障率如果小於指定百分比允許繼續運行 (注意數值前面的“.”,比如.2爲20%)

​ docker service create --network 網絡名稱 # 創建服務時指定docker網絡

​ docker service create --mount type=volume,src=[volume_name],dst=[容器目錄] # 創建服務時給容器添加volume類型數據卷

​ docker service create --mount type=bind,src=[宿主目錄],dst=[容器目錄] # 創建服務時給容器添加bind讀寫目錄掛載

​ docker service create --mount type=bind,src=[宿主目錄],dst=[容器目錄],readonly # 創建服務時給容器添加bind只讀目錄掛載

​ docker service create --endpoint-mode dnsrr [service_name] # 創建服務時使用dnsrr負載均衡模式

​ docker service create --config source=docker配置文件,target=配置文件路徑 # 創建服務同時將docker配置文件指定到容器本地目錄

​ docker service create --publish 暴露端口:容器端口 [service_name] # 創建服務時將容器的指定端口暴露出來

​ docker service inspect --pretty [service_name] # 以平鋪格式查看服務詳細信息

​ docker service logs # 查看服務內日誌輸出

​ docker service ls # 列出服務

​ docker service ps [service_name] # 查看服務啓動的所有任務

​ docker service ps -f "desired-state=running" [service_name] # 查看服務正在運行的任務

​ docker service scale [service_name]=數量 # 擴展服務容器副本數量

​ docker service update --args "指令" [service_name] # 給容器添加指令

​ docker service update --image 更新版本 [service_name] # 更新服務容器鏡像版本

​ docker service update --rollback [service_name] # 回滾服務容器版本

​ docker service update --network-add 網絡名稱 [service_name] # 更新服務容器添加網絡

​ docker service update --network-rm 網絡名稱 [service_name] # 更新服務容器刪除網絡

​ docker service update --publish-add 暴露端口:容器端口 [service_name] # 更新服務添加容器暴露端口

​ docker service update --publish-rm 暴露端口:容器端口 [service_name] # 更新服務移除容器暴露端口

​ docker service update --endpoint-mode dnsrr [service_name] # 更新服務修改負載均衡模式爲dnsrr

​ docker service update --config-add 配置文件名稱,target=容器配置文件位置 [service_name] # 更新服務添加新的配置文件到容器中

​ docker service update --config-rm 配置文件名稱 [service_name] # 更新服務刪除配置文件

​ docker service update --force [service_name] # 強制更新服務

七、Docker Swarm服務管理示例

創建服務

創建一個nginx服務:

[root@docker01 ~]# docker service create --name nginx nginx:latest
0q1wk3uw6pmaq1m61eooo5ybv

查看nginx服務運行的任務:

[root@docker01 ~]# docker service ps nginx
ID            NAME     IMAGE         NODE                  DESIRED STATE  CURRENT STATE          ERROR  PORTS
y418x232aqd4  nginx.1  nginx:latest  docker01.contoso.com  Running        Running 9 minutes ago         

查看nginx服務的詳細信息:

[root@docker01 ~]# docker service inspect nginx
[
    {
        "ID": "0q1wk3uw6pmaq1m61eooo5ybv",
        "Version": {
            "Index": 21
        },
        "CreatedAt": "2019-05-02T12:39:33.903002904Z",
        "UpdatedAt": "2019-05-02T12:39:33.903002904Z",
        "Spec": {
            "Name": "nginx",
            "TaskTemplate": {
                "ContainerSpec": {
                    "Image": "nginx:latest@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c",
                    "DNSConfig": {}
                },
                "Resources": {
                    "Limits": {},
                    "Reservations": {}
                },
                "RestartPolicy": {
                    "Condition": "any",
                    "MaxAttempts": 0
                },
                "Placement": {},
                "ForceUpdate": 0
            },
            "Mode": {
                "Replicated": {
                    "Replicas": 1
                }
            },
            "UpdateConfig": {
                "Parallelism": 1,
                "FailureAction": "pause",
                "MaxFailureRatio": 0
            },
            "EndpointSpec": {
                "Mode": "vip"
            }
        },
        "Endpoint": {
            "Spec": {}
        },
        "UpdateStatus": {
            "StartedAt": "0001-01-01T00:00:00Z",
            "CompletedAt": "0001-01-01T00:00:00Z"
        }
    }
]

擴展nginx服務的數量:

[root@docker01 ~]# docker service scale nginx=3 
nginx scaled to 3

查看當前運行的所有服務:

[root@docker01 ~]# docker service ls 
ID            NAME   MODE        REPLICAS  IMAGE
0q1wk3uw6pma  nginx  replicated  3/3       nginx:latest

更新nginx服務指令:

[root@docker01 ~]# docker service update --args "ping www.baidu.com" nginx
nginx

更新nginx服務添加80端口映射:

[root@docker01 ~]# docker service update --publish-add 8080:80 nginx
nginx

更新nginx服務移除80端口映射:

[root@docker01 ~]# docker service update --publish-rm 8080:80 nginx
nginx

刪除nginx服務:

[root@docker01 ~]# docker service rm nginx
nginx

滾動更新

創建服務時設定更新策略:

[root@docker01 ~]# docker service create --name myredis --replicas 6 --update-delay 10s --update-parallelism 2 --update-failure-action continue redis:3.0.6
swlovrpgox3gl6cs3b94me4ws

查看服務運行情況:

[root@docker01 ~]# docker service ls 
ID            NAME     MODE        REPLICAS  IMAGE
swlovrpgox3g  myredis  replicated  6/6       redis:3.0.6
[root@docker01 ~]# docker service ps myredis
ID            NAME       IMAGE        NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
w42c1giiahwe  myredis.1  redis:3.0.6  docker01.contoso.com  Running        Running 49 seconds ago         
j6i3igrlv16o  myredis.2  redis:3.0.6  docker03.contoso.com  Running        Running 51 seconds ago         
cp0w3o0t4ei0  myredis.3  redis:3.0.6  docker02.contoso.com  Running        Running 50 seconds ago         
go26b31pw6lp  myredis.4  redis:3.0.6  docker01.contoso.com  Running        Running 50 seconds ago         
4nwbkvr1pl4c  myredis.5  redis:3.0.6  docker03.contoso.com  Running        Running 50 seconds ago         
t5h4lclip9uz  myredis.6  redis:3.0.6  docker02.contoso.com  Running        Running 51 seconds ago   

手動更新服務:

[root@docker01 ~]# docker service update --image redis:3.0.7 myredis
myredis

查看更新過程:

[root@docker01 ~]# docker service ps myredis
ID            NAME           IMAGE        NODE                  DESIRED STATE  CURRENT STATE                    ERROR  PORTS
w42c1giiahwe  myredis.1      redis:3.0.6  docker01.contoso.com  Running        Running 2 minutes ago                   
3930onk71uav  myredis.2      redis:3.0.7  docker03.contoso.com  Ready          Ready less than a second ago            
j6i3igrlv16o   \_ myredis.2  redis:3.0.6  docker03.contoso.com  Shutdown       Running less than a second ago          
nol40hkh9g56  myredis.3      redis:3.0.7  docker02.contoso.com  Running        Running 11 seconds ago                  
cp0w3o0t4ei0   \_ myredis.3  redis:3.0.6  docker02.contoso.com  Shutdown       Shutdown 11 seconds ago                 
q0oqrwrbju3n  myredis.4      redis:3.0.7  docker03.contoso.com  Running        Ready less than a second ago            
go26b31pw6lp   \_ myredis.4  redis:3.0.6  docker01.contoso.com  Shutdown       Shutdown less than a second ago         
jdqbfpf64xsu  myredis.5      redis:3.0.7  docker02.contoso.com  Running        Running 11 seconds ago                  
4nwbkvr1pl4c   \_ myredis.5  redis:3.0.6  docker03.contoso.com  Shutdown       Shutdown 11 seconds ago                 
t5h4lclip9uz  myredis.6      redis:3.0.6  docker02.contoso.com  Running        Running 2 minutes ago      
[root@docker01 ~]# docker service ps -f "desired-state=running" myredis
ID            NAME       IMAGE        NODE                  DESIRED STATE  CURRENT STATE          ERROR  PORTS
vnjs46emceib  myredis.1  redis:3.0.7  docker01.contoso.com  Running        Running 2 minutes ago         
3930onk71uav  myredis.2  redis:3.0.7  docker03.contoso.com  Running        Running 2 minutes ago         
nol40hkh9g56  myredis.3  redis:3.0.7  docker02.contoso.com  Running        Running 2 minutes ago         
q0oqrwrbju3n  myredis.4  redis:3.0.7  docker03.contoso.com  Running        Running 2 minutes ago         
jdqbfpf64xsu  myredis.5  redis:3.0.7  docker02.contoso.com  Running        Running 2 minutes ago         
urp2fndjogvb  myredis.6  redis:3.0.7  docker01.contoso.com  Running        Running 2 minutes ago  

動態回滾

創建服務時設定回滾策略:

[root@docker01 ~]# docker service create --name myredis --replicas 6 --update-parallelism 2 --update-monitor 20s --update-max-failure-ratio .2 redis:3.0.6
fmfrmn6bjvf3rhe14yleioruq

注意:原有的--rollback-parallelism和--rollback-monitor都已合併到--update-*中了,所以如果使用--rollback相關參數會提示unknow flag。

查看服務狀態:

[root@docker01 ~]# docker service ps myredis
ID            NAME       IMAGE        NODE                  DESIRED STATE  CURRENT STATE          ERROR  PORTS
mj4r36nbqyfv  myredis.1  redis:3.0.6  docker01.contoso.com  Running        Running 2 minutes ago         
y6ddrzvqdq6r  myredis.2  redis:3.0.6  docker03.contoso.com  Running        Running 2 minutes ago         
3j831bwd885p  myredis.3  redis:3.0.6  docker02.contoso.com  Running        Running 2 minutes ago         
o1hia18mi13u  myredis.4  redis:3.0.6  docker01.contoso.com  Running        Running 2 minutes ago         
jatwoh12s9jr  myredis.5  redis:3.0.6  docker03.contoso.com  Running        Running 2 minutes ago         
ldo6bxes2itv  myredis.6  redis:3.0.6  docker02.contoso.com  Running        Running 2 minutes ago         

手動執行更新命令:

[root@docker01 ~]# docker service update --image redis:3.0.7 myredis
myredis

查看更新結果:

[root@docker01 ~]# docker service ps -f "desired-state=running" myredis
ID            NAME       IMAGE        NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
qb9ydmhvx2g0  myredis.1  redis:3.0.7  docker02.contoso.com  Running        Running 50 seconds ago         
wun44p8i2i0j  myredis.2  redis:3.0.7  docker03.contoso.com  Running        Running 53 seconds ago         
q94nwkicn56j  myredis.3  redis:3.0.7  docker01.contoso.com  Running        Running 52 seconds ago         
m5mnwanemoi5  myredis.4  redis:3.0.7  docker03.contoso.com  Running        Running 53 seconds ago         
b8jy2aeclv64  myredis.5  redis:3.0.7  docker02.contoso.com  Running        Running 51 seconds ago         
9bb1k1nfaol4  myredis.6  redis:3.0.7  docker02.contoso.com  Running        Running 50 seconds ago        

手動執行回滾命令:

[root@docker01 ~]# docker service update --rollback --update-delay 0s myredis
myredis

查看回滾結果:

[root@docker01 ~]# docker service ps -f "desired-state=running" myredis
ID            NAME       IMAGE        NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
xiwpjue0fm1k  myredis.1  redis:3.0.6  docker03.contoso.com  Running        Running 50 seconds ago         
ggkntqx0l7bq  myredis.2  redis:3.0.6  docker03.contoso.com  Running        Running 49 seconds ago         
l7kv1hat1z8s  myredis.3  redis:3.0.6  docker01.contoso.com  Running        Running 51 seconds ago         
ql31hdzcp11h  myredis.4  redis:3.0.6  docker01.contoso.com  Running        Running 52 seconds ago         
ts7kyklkrc9h  myredis.5  redis:3.0.6  docker02.contoso.com  Running        Running 53 seconds ago         
feffo49ulnlk  myredis.6  redis:3.0.6  docker02.contoso.com  Running        Running 53 seconds ago        

使用Overlay網絡

創建overlay網絡:

[root@docker01 ~]# docker network create --driver overlay myovl
mqptdqjx30cpra84wfu2akbxg

創建新服務並使用overlay網絡:

[root@docker01 ~]# docker service create --replicas 3 --network myovl --name bbox01 busybox:latest ping www.baidu.com
s7tttmyqw1c5zvvkzc339as1w

查看服務狀態:

[root@docker01 ~]# docker service ps bbox01
ID            NAME      IMAGE           NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
r9k6izleq2g5  bbox01.1  busybox:latest  docker01.contoso.com  Running        Running 10 seconds ago         
yaevcojb17k1  bbox01.2  busybox:latest  docker03.contoso.com  Running        Running 10 seconds ago         
xeiju66hoei0  bbox01.3  busybox:latest  docker02.contoso.com  Running        Running 10 seconds ago         

在節點上查看服務中容器的IP地址:

# 節點1(manager節點)
[root@docker01 ~]# docker container ps -a
CONTAINER ID        IMAGE                                                                             COMMAND                  CREATED             STATUS              PORTS               NAMES
4c1244d20495        busybox@sha256:954e1f01e80ce09d0887ff6ea10b13a812cb01932a0781d6b0cc23f743a874fd   "ping www.baidu.com"     2 minutes ago       Up 2 minutes                            bbox01.1.r9k6izleq2g55hbdgnj67ddwg
[root@docker01 ~]# docker exec -it 4c1244d20495 /bin/sh
/ # ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:00:04  
          inet addr:10.0.0.4  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::42:aff:fe00:4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:13 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1038 (1.0 KiB)  TX bytes:648 (648.0 B)

# 節點2(worker節點)
[root@docker02 ~]# docker ps -a
CONTAINER ID        IMAGE                                                                             COMMAND                  CREATED              STATUS              PORTS               NAMES
5a28a0d5aec1        busybox@sha256:954e1f01e80ce09d0887ff6ea10b13a812cb01932a0781d6b0cc23f743a874fd   "ping www.baidu.com"     About a minute ago   Up About a minute                       bbox01.3.xeiju66hoei0ooc8i46pmekcv
[root@docker02 ~]# docker exec -it 5a28a0d5aec1 /bin/sh 
/ # ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:00:03  
          inet addr:10.0.0.3  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::42:aff:fe00:3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:14 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1128 (1.1 KiB)  TX bytes:648 (648.0 B)

# 節點3(worker節點)
[root@docker03 ~]# docker ps -a
CONTAINER ID        IMAGE                                                                             COMMAND                  CREATED             STATUS              PORTS               NAMES
b3fe85292655        busybox@sha256:954e1f01e80ce09d0887ff6ea10b13a812cb01932a0781d6b0cc23f743a874fd   "ping www.baidu.com"     2 minutes ago       Up 2 minutes                            bbox01.2.yaevcojb17k1bzphmj20mr9rh
[root@docker03 ~]# docker exec -it b3fe85292655 /bin/sh
/ # ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:00:05  
          inet addr:10.0.0.5  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::42:aff:fe00:5/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:14 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1128 (1.1 KiB)  TX bytes:648 (648.0 B)

測試網絡是否可以通信:

[root@docker01 ~]# docker exec -it 4c1244d20495 /bin/sh
/ # ping 10.0.0.3 -c2 -w2
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=5.653 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=0.354 ms

--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.354/3.003/5.653 ms
/ # ping 10.0.0.5 -c2 -w2
PING 10.0.0.5 (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: seq=0 ttl=64 time=21.347 ms
64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.320 ms

--- 10.0.0.5 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.320/10.833/21.347 ms

集羣數據持久化

1)使用數據卷(默認方式)

創建服務時掛載數據卷:

[root@docker01 ~]# docker service create --mount type=volume,src=test,dst=/data --name bbox02 busybox ping www.baidu.com
9qr8s9vha2ywa7becvqfukwxv

查看服務運行在哪個節點上:

[root@docker01 ~]# docker service ps bbox02
ID            NAME      IMAGE           NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
azwf00koeug2  bbox02.1  busybox:latest  docker03.contoso.com  Running        Running 26 seconds ago         

到對應的節點上:

[root@docker03 ~]# docker volume ls 
DRIVER              VOLUME NAME
local               test

在docker數據卷中創建一個文件:

[root@docker03 ~]# echo "hello" >> /var/lib/docker/volumes/test/_data/hello.txt
[root@docker03 ~]# cat /var/lib/docker/volumes/test/_data/hello.txt 
hello

進入容器內部查看:

[root@docker03 ~]# docker exec -it $(docker ps -a |grep bbox02|awk '{print $1}') /bin/sh
/ # ls /data
hello.txt
/ # cat /data/hello.txt 
hello

2)使用nfs共享存儲

在節點1創建nfs存儲:

[root@docker01 ~]# yum -y install nfs-utils 
[root@docker01 ~]# vi /etc/exports
[root@docker01 ~]# cat /etc/exports
/data/test 192.168.49.0/24(rw)
[root@docker01 ~]# mkdir -p /data/test
[root@docker01 ~]# echo "Own by nfs." >> /data/test/readme.txt
[root@docker01 ~]# chmod -R 777 /data/test
[root@docker01 ~]# systemctl start rpcbind
[root@docker01 ~]# systemctl start nfs

在其他兩個節點上安裝nfs客戶端:

[root@docker02 ~]# yum -y install nfs-utils
[root@docker02 ~]# systemctl start rpcbind
[root@docker02 ~]# systemctl nfs
[root@docker03 ~]# yum -y install nfs-utils
[root@docker03 ~]# systemctl start rpcbind
[root@docker03 ~]# systemctl nfs

創建服務時掛載nfs共享存儲:

[root@docker01 ~]# docker service create --mount 'type=volume,src=nfs-dir,dst=/data,volume-driver=local,volume-opt=type=nfs,volume-opt=device=192.168.49.41:/data/test,"volume-opt=o=addr=192.168.49.41,vers=4,soft,timeo=180,bg,tcp,rw"' --name bbox03 busybox ping www.baidu.com
cq3yg35fnpy5nrwuc2chmh366

查看服務運行在哪個節點上:

[root@docker01 ~]# docker service ps bbox03
ID            NAME      IMAGE           NODE                  DESIRED STATE  CURRENT STATE          ERROR  PORTS
msbvkm3s3388  bbox03.1  busybox:latest  docker02.contoso.com  Running        Running 2 minutes ago         

到對應的節點上查看:

[root@docker02 ~]# docker volume ls 
DRIVER              VOLUME NAME
local               nfs-dir
[root@docker02 ~]# docker volume inspect nfs-dir
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/nfs-dir/_data",
        # 服務運行節點上數據的存放位置
        "Name": "nfs-dir",
        "Options": {
            "device": "192.168.49.41:/data/test",
            "o": 
            # nfs共享存儲位置,與其他節點共享 "addr=192.168.49.41,vers=4,soft,timeo=180,bg,tcp,rw",
            "type": "nfs"
        },
        "Scope": "local"
    }
]

測試數據是否同步:

[root@docker02 ~]# docker exec -it $(docker ps -a |grep bbox03|awk '{print $1}') /bin/sh
/ # ls /data
readme.txt
/ # cat /data/readme.txt 
Own by nfs.

嘗試在容器中創建新的文件:

[root@docker02 ~]# docker exec -it $(docker ps -a |grep bbox03|awk '{print $1}') /bin/sh
/ # echo "Added by container bbox03." >> /data/newfile

到nfs節點主機上查看:

[root@docker01 ~]# cat /data/test/newfile 
Added by container bbox03.

說明nfs共享存儲上的文件已經同步。

八、Docker Swarm高級功能

負載均衡

Swarm模式內置DNS組件,可以自動爲集羣中的每個服務分配DNS記錄。 Swarm manager使用內部負載均衡,根據服務的DNS名稱在集羣內的服務之間分發請求。

Swarm manager使用 ingress load blancing暴露你想從外部訪問集羣提供的服務。 Swarm manager自動爲服務分配一個範圍30000-32767端口的Published Port, 也可以爲該服務指定一個Published Port。

ingress network是一個特殊的overlay網絡,便於服務的節點直接負載均衡。當任何swarm節點在已發佈的端口上接收到請求時,它將該請求轉發給調用的IPVS模塊, IPVS跟蹤參與該服務的所有容器IP地址,選擇其中一個,並通過ingress network將請求路由給它。

Docker Swarm負載均衡分爲兩種模式:

  • VIP:分配獨立的虛擬IP,DNS記錄解析到服務名中作爲代理IP。
  • dnsrr:DNS記錄不解析VIP,而去解析每個容器內的IP。dnsrr模式不支持端口對外暴露。

1)VIP模式

創建一個nginx服務:

[root@docker01 ~]# docker service create --replicas 3 --name mynginx --network myovl --publish 8080:80 mynginx:v1
ialfxo51tn9d64h3lbmu6k8i0
[root@docker01 ~]# docker service ps mynginx
ID            NAME       IMAGE       NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
ig9a0v0c9k94  mynginx.1  mynginx:v1  docker03.contoso.com  Running        Running 37 seconds ago         
pffqk518icd8  mynginx.2  mynginx:v1  docker02.contoso.com  Running        Running 28 seconds ago         
oqv4q2775ju0  mynginx.3  mynginx:v1  docker01.contoso.com  Running        Running 34 seconds ago 

再創建一個centos服務:

[root@docker01 ~]# docker service create --name mycentos --network myovl 192.168.49.40:5000/centos_ssh:v1.0
uacfpcgt016q6jco0jhyn1ehq
[root@docker01 ~]# docker service ps mycentos
ID            NAME        IMAGE                               NODE                  DESIRED STATE  CURRENT STATE           ERROR  PORTS
wuxxyoccd8fv  mycentos.1  192.168.49.40:5000/centos_ssh:v1.0  docker03.contoso.com  Running        Running 46 seconds ago         

進入mycentos服務的容器中查看:

[root@docker03 ~]# docker exec -it $(docker ps -a |grep mycentos.1|awk '{print $1}') /bin/bash
[root@29870e8a8752 /]# nslookup mynginx
Server:     127.0.0.11
Address:    127.0.0.11#53

Non-authoritative answer:
Name:   mynginx
Address: 10.0.0.2
[root@29870e8a8752 /]# dig A +noall +answer mynginx
mynginx.        600 IN  A   10.0.0.2

在manage節點上也可以查看mynginx的dns解析地址:

[root@docker01 ~]# docker service inspect mynginx -f '{{.Endpoint.VirtualIPs}}'
[{w7nsefcod7xfjio63acqqb2bw 10.255.0.2/16} {mqptdqjx30cpra84wfu2akbxg 10.0.0.2/24}]

這裏看到mynginx服務解析的IP地址是10.0.0.2, 我們再到swarm各個節點上查看服務包含的容器的IP地址:

# 在manager節點上
[root@docker01 ~]# docker exec -it $(docker ps -a |awk '$2~/mynginx:v1/{print $1}') /sbin/ifconfig|grep -A1 eth2
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.0.0.5  netmask 255.255.255.0  broadcast 0.0.0.0

# 在節點2(worker節點)上       
[root@docker02 ~]# docker exec -it $(docker ps -a |awk '$2~/mynginx:v1/{print $1}') /sbin/ifconfig|grep -A1 eth2
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.0.0.4  netmask 255.255.255.0  broadcast 0.0.0.0

# 在節點3(worker節點)上        
[root@docker03 ~]# docker exec -it $(docker ps -a |awk '$2~/mynginx:v1/{print $1}') /sbin/ifconfig|grep -A1 eth2
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.0.0.3  netmask 255.255.255.0  broadcast 0.0.0.0

可見,mynginx服務中的容器並沒有一個IP地址是10.0.0.2,而服務的dns解析地址卻是10.0.0.2,我們嘗試用在同一個網絡中的mycentos服務中的容器進行訪問:

[root@docker03 ~]# docker exec -it $(docker ps -a |grep mycentos.1|awk '{print $1}') /bin/bash
[root@29870e8a8752 /]# curl mynginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@29870e8a8752 /]# curl 10.0.0.4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

可見使用服務名稱去訪問和使用容器IP獲取的內容一樣,說明負載均衡是生效的。

嘗試修改容器內部的index.html內容,不同的容器改做不同的內容:

[root@docker01 ~]# echo "manager01" >> index.html
[root@docker01 ~]# docker cp index.html $(docker ps -a |awk '$2~/mynginx:v1/{print $1}'):/usr/local/nginx/html

[root@docker02 ~]# echo "worker01" >> index.html
[root@docker02 ~]# docker cp index.html $(docker ps -a |awk '$2~/mynginx:v1/{print $1}'):/usr/local/nginx/html

[root@docker03 ~]# echo "worker02" >> index.html
[root@docker03 ~]# docker cp index.html $(docker ps -a |awk '$2~/mynginx:v1/{print $1}'):/usr/local/nginx/html

因爲我在創建服務的時候已經暴露了8080端口,這次直接在宿主機(swarm節點)上進行測試:

# manager節點上
[root@docker01 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
manager01
worker01
worker02
manager01
worker01
worker02
manager01
worker01
worker02
manager01

# 節點2上
[root@docker02 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
worker02
manager01
worker01
worker02
manager01
worker01
worker02
manager01
worker01
worker02

# 節點3上
[root@docker03 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
worker01
worker02
manager01
worker01
worker02
manager01
worker01
worker02
manager01
worker01

到此,docker swarm服務基於VIP的負載均衡功能穩定,而且實現正常。

2)dnsrr模式

創建一個myweb服務:

[root@docker01 ~]# docker service create --replicas 3 --name myweb --network myovl --endpoint-mode dnsrr mynginx:v1
fovdvwdvtbfscatve68e1ank6

查看myweb服務運行在哪些節點上;

[root@docker01 ~]# docker service ps myweb
ID            NAME     IMAGE       NODE                  DESIRED STATE  CURRENT STATE          ERROR  PORTS
zqityfx1ma6m  myweb.1  mynginx:v1  docker02.contoso.com  Running        Running 4 minutes ago         
uuj9y2clos52  myweb.2  mynginx:v1  docker01.contoso.com  Running        Running 4 minutes ago         
1ex0vt8poa8s  myweb.3  mynginx:v1  docker03.contoso.com  Running        Running 4 minutes ago      

同樣的,我們到處於同一網絡的mycentos容器上去查看myweb的dns記錄解析:

[root@docker03 ~]# docker exec -it $(docker ps -a |grep mycentos.1|awk '{print $1}') /bin/bash
[root@29870e8a8752 /]# nslookup myweb
Server:     127.0.0.11
Address:    127.0.0.11#53

Non-authoritative answer:
Name:   myweb
Address: 10.0.0.9
Name:   myweb
Address: 10.0.0.8
Name:   myweb
Address: 10.0.0.10

同樣,我們嘗試去ping服務myweb:

[root@29870e8a8752 /]# ping myweb -c2 -w2
PING myweb (10.0.0.8) 56(84) bytes of data.
64 bytes from myweb.1.zqityfx1ma6mzxi6cexyzmoej.myovl (10.0.0.8): icmp_seq=1 ttl=64 time=0.352 ms
64 bytes from myweb.1.zqityfx1ma6mzxi6cexyzmoej.myovl (10.0.0.8): icmp_seq=2 ttl=64 time=0.675 ms

--- myweb ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.352/0.513/0.675/0.163 ms
[root@29870e8a8752 /]# ping myweb -c2 -w2
PING myweb (10.0.0.9) 56(84) bytes of data.
64 bytes from myweb.2.uuj9y2clos52lrubobdvr10fg.myovl (10.0.0.9): icmp_seq=1 ttl=64 time=0.398 ms
64 bytes from myweb.2.uuj9y2clos52lrubobdvr10fg.myovl (10.0.0.9): icmp_seq=2 ttl=64 time=0.274 ms

--- myweb ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
[root@29870e8a8752 /]# ping myweb -c2 -w2
PING myweb (10.0.0.10) 56(84) bytes of data.
64 bytes from myweb.3.1ex0vt8poa8s424tp5z9ibpur.myovl (10.0.0.10): icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from myweb.3.1ex0vt8poa8s424tp5z9ibpur.myovl (10.0.0.10): icmp_seq=2 ttl=64 time=0.081 ms

--- myweb ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.059/0.070/0.081/0.011 ms

這裏也可以看到,基於DNS的負載均衡也沒有問題,對服務的訪問會採用dns輪詢的方式,每次隨機分配到其中的容器節點上。

比較Docker Swarm的兩種負載均衡模式,兩種模式都有各自的應用場景,但是VIP模式更適合生產環境,在集羣中創建服務並擴容多個容器的情況下,容器會有一個統一的VIP,且該VIP不會隨意發生變化,這樣我們僅需將服務啓動時暴露端口到宿主機的指定端口,同時在宿主機網絡中另外啓動其他的負載均衡服務(如nginx, haproxy等),再將該節點的端口proxy到swarm集羣中節點的指定端口上,這樣就實現了多層負載均衡,且容器個數可以隨意伸縮,靈活性大大提高。

而使用基於DNS的負載均衡,因爲採用輪詢的方式,會將請求隨機分配到容器中,對於某些會緩存DNS到IP地址映射的應用程序,可能會導致在服務中容器發生變動(如銷燬、重啓等)導致超時,所以DNS解析一旦發生變化就需考慮是否會影響到其他服務,如需使用要考慮到這一點。

節點高可用性

查看當前的節點狀態:

[root@docker01 ~]# docker node ls
ID                           HOSTNAME              STATUS  AVAILABILITY  MANAGER STATUS
fkqck55wao2dr23t9jq42e6e7    docker03.contoso.com  Ready   Active        
q1gkyqchf5nz20qgzy2dfeyk9    docker02.contoso.com  Ready   Active        
rycnd1olbld9kizgb1h5rf8rs *  docker01.contoso.com  Ready   Active        Leader
[root@docker01 ~]# docker service ls
ID            NAME      MODE        REPLICAS  IMAGE
fovdvwdvtbfs  myweb     replicated  3/3       mynginx:v1
ialfxo51tn9d  mynginx   replicated  3/3       mynginx:v1
uacfpcgt016q  mycentos  replicated  1/1       192.168.49.40:5000/centos_ssh:v1.0

關閉manager節點,檢查容器是否仍然運行:

# 節點2
[root@docker02 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
c74b73a90f97        mynginx:v1          "./sbin/nginx -g '..."   48 minutes ago      Up 48 minutes       80/tcp              myweb.1.zqityfx1ma6mzxi6cexyzmoej
0f4ceba39262        mynginx:v1          "./sbin/nginx -g '..."   4 hours ago         Up 4 hours          80/tcp              mynginx.2.pffqk518icd8coz9ed5u0u1y5

# 節點3
[root@docker03 ~]# docker ps -a
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS               NAMES
e27dc98de27b        mynginx:v1                           "./sbin/nginx -g '..."   48 minutes ago      Up 48 minutes       80/tcp              myweb.3.1ex0vt8poa8s424tp5z9ibpur
29870e8a8752        192.168.49.40:5000/centos_ssh:v1.0   "/usr/sbin/sshd -D"      3 hours ago         Up 3 hours          22/tcp              mycentos.1.wuxxyoccd8fv5d932phnab9c4
60071b149e3f        mynginx:v1                           "./sbin/nginx -g '..."   4 hours ago         Up 4 hours          80/tcp              mynginx.1.ig9a0v0c9k94a9qvtp6kk7cgy

再檢查服務是否可用:

[root@docker02 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
worker01
worker02
worker01
worker02
worker01
worker02
worker01
worker02
worker01
worker02
[root@docker03 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
worker02
worker01
worker02
worker01
worker02
worker01
worker02
worker01
worker02
worker01

重新啓動manager節點後:

[root@docker02 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
worker02
manager01
worker01
worker02
manager01
worker01
worker02
manager01
worker01
worker02

嘗試將節點2和節點3提升爲manager節點:

[root@docker01 ~]# docker node promote q1gkyqchf5nz20qgzy2dfeyk9
Node q1gkyqchf5nz20qgzy2dfeyk9 promoted to a manager in the swarm.
[root@docker01 ~]# docker node promote fkqck55wao2dr23t9jq42e6e7
Node fkqck55wao2dr23t9jq42e6e7 promoted to a manager in the swarm.[root@docker01 ~]# docker node ls 
ID                           HOSTNAME              STATUS  AVAILABILITY  MANAGER STATUS
fkqck55wao2dr23t9jq42e6e7    docker03.contoso.com  Ready   Active        Reachable
q1gkyqchf5nz20qgzy2dfeyk9    docker02.contoso.com  Ready   Active        Reachable
rycnd1olbld9kizgb1h5rf8rs *  docker01.contoso.com  Ready   Active        Leader

再次嘗試關閉manager節點:

[root@docker02 ~]# docker node ls
ID                           HOSTNAME              STATUS  AVAILABILITY  MANAGER STATUS
fkqck55wao2dr23t9jq42e6e7    docker03.contoso.com  Ready   Active        Reachable
q1gkyqchf5nz20qgzy2dfeyk9 *  docker02.contoso.com  Ready   Active        Leader
rycnd1olbld9kizgb1h5rf8rs    docker01.contoso.com  Ready   Active        Unreachable

此時服務的狀態:

[root@docker02 ~]# docker service ls
ID            NAME      MODE        REPLICAS  IMAGE
fovdvwdvtbfs  myweb     replicated  3/3       mynginx:v1
ialfxo51tn9d  mynginx   replicated  3/3       mynginx:v1
uacfpcgt016q  mycentos  replicated  1/1       192.168.49.40:5000/centos_ssh:v1.0

恢復manager節點:

[root@docker01 ~]# docker node ls
ID                           HOSTNAME              STATUS  AVAILABILITY  MANAGER STATUS
fkqck55wao2dr23t9jq42e6e7    docker03.contoso.com  Ready   Active        Leader
q1gkyqchf5nz20qgzy2dfeyk9    docker02.contoso.com  Ready   Active        Reachable
rycnd1olbld9kizgb1h5rf8rs *  docker01.contoso.com  Ready   Active        Reachable

服務狀態:

[root@docker01 ~]# docker service ls
ID            NAME      MODE        REPLICAS  IMAGE
fovdvwdvtbfs  myweb     replicated  3/3       mynginx:v1
ialfxo51tn9d  mynginx   replicated  3/3       mynginx:v1
uacfpcgt016q  mycentos  replicated  1/1       192.168.49.40:5000/centos_ssh:v1.0

測試服務是否可用:

[root@docker01 ~]# for i in `seq 10`;do curl http://127.0.0.1:8080;done
worker02
worker01
worker01
worker02
worker01
worker01
worker02
worker01
worker01
worker02

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

管理節點的個數以及允許宕機的個數如下:

mannager node 允許宕機個數 服務運行狀態
3 1 正常
5 2 正常
7 3 正常
... ... ...
n (n+1)/2 正常

故障恢復:
如果swarm失去法定人數,swarm不能自動恢復,工作節點上的任務繼續運行,不受影響,但無法執行管理任務,包括擴展或更新服務,加入或刪除節點。恢復的最佳方式是將丟失的leader節點重新聯機。如果不可能,唯一方法是使用—force-new- cluster管理節點的操作,這將去除本機之外的所有管理器身份。

Docker Swarm節點高可用結構圖如下:

Docker系列(十四):Docker Swarm集羣

Docker Stack編排

stack是一組相互關聯的服務,它們共享依賴關係,並且可以一起orchestrated(編排)和縮放。單個stack能夠定義和協調整個應用程序的功能(儘管非常複雜的應用程序可能希望使用多個堆棧),stack 是構成特定環境中的 service 集合, 它是自動部署多個相互關聯的服務的簡便方法,而無需單獨定義每個服務。
stack file 是一種 yaml 格式的文件,類似於 docker-compose.yml 文件,它定義了一個或多個服務,並定義了服務的環境變量、部署標籤、容器數量以及相關的環境特定配置等。

Docker Stack的yaml和docker compose很相似,絕大部分選項都可以共用,以下選項除外:

  • build
  • cgroup_parent
  • container_name
  • devices
  • dns
  • dns_search
  • tmpfs
  • external_links
  • links
  • network_mode
  • security_opt
  • stop_signal
  • sysctls
  • userns_mode

Docker Stack常用命令:

  • docker stack deploy:部署新的堆棧或更新現有堆棧
  • docker stack ls:列出現有堆棧
  • docker stack ps:列出堆棧中的任務
  • docker stack rm:刪除一個或多個堆棧
  • docker stack services:列出堆棧中的服務

Docker Stack和Docker Compose的區別:

  • Docker stack會忽略了“構建”指令,無法使用stack命令構建新鏡像,它是需要鏡像是預先已經構建好的。 所以docker-compose更適合於開發場景;
  • Docker Compose是一個Python項目,在內部,它使用Docker API規範來操作容器。所以需要安裝Docker -compose,以便與Docker一起在您的計算機上使用;
  • Docker Stack功能包含在Docker引擎中。你不需要安裝額外的包來使用它,docker stacks 只是swarm mode的一部分。
    Docker stack不支持基於第2版寫的docker-compose.yml ,也就是version版本至少爲3。然而Docker Compose對版本爲2和3的 文件仍然可以處理;
  • docker stack把docker compose的所有工作都做完了,因此docker stack將占主導地位。同時,對於大多數用戶來說,切換到使用docker stack既不困難,也不需要太多的開銷。如果您是Docker新手,或正在選擇用於新項目的技術,請使用docker stack。

Docker Stack服務編排示例:

下面使用Docker Stack編排一個wordpress服務,當然wordpress主要依賴於LNMP環境,另外就是因爲之前的文章中已經使用Docker Compose實現過wordpress搭建,所以這裏改用Docker Stack編排,更能對比二者的差別,方便大家在後續工作中根據情況選用不同的方式。

首先,先看一下目錄結構:

[root@docker01 services]# tree -L 2 .
.
├── dbdata
├── webcontent
│   ├── nginxconf
│   └── wordpress
└── wordpress.yml

4 directories, 1 files

這裏的目錄內容是從前面docker compose那裏修改生成了,文件內容基本不變,只是新增了一個wordpress.yml。這裏看一下wordpress.yml的內容:

[root@docker01 services]# cat wordpress.yml 
version: '3'
services: 
  mysqldb:
    image: mysql:5.6
    ports:
      - 3306:3306
    networks:
      - wps_net
    volumes: 
      - ./dbdata:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  php:
    image: wordpress_php-cgi:latest
    networks:
      - wps_net
    volumes:
      - ./webcontent/wordpress:/usr/local/nginx/html
    deploy:
      mode: replicated
      replicas: 12

  nginx:
    image: wordpress_nginx:latest
    ports: 
      - 8080:80
    networks:
      - wps_net
    volumes:
      - ./webcontent/nginxconf:/usr/local/nginx/conf.d
      - ./webcontent/wordpress:/usr/local/nginx/html
    deploy:
      mode: replicated
      replicas: 3
    depends_on:
      - mysqldb
      - php

networks:
  wps_net:
    driver: overlay

再看一下nginx中關於wordpress的配置:

[root@docker01 services]# cat webcontent/nginxconf/wordpress.conf 
server {
    listen 80;
    server_name localhost;
    server_tokens off;

    access_log logs/access.log main;
    error_log  logs/error.log;

    root /usr/local/nginx/html;
    index index.php;

    location ~ .*\.(php|php5)?$ {
        fastcgi_pass php:9000;
        include /usr/local/nginx/conf/fastcgi.conf;
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param PHP_VALUE "include_path=/usr/local/php:/usr/local/nginx/html:.";
    }

}

其他的,webcontent/wordpress裏爲wordpress源碼包加壓後的文件,不過爲了減少後續的配置步驟,這裏需要修改webcontent/wordpress/wp-config.php文件,修改如下選項爲下面列舉的值:

define('DB_NAME', 'wordpress');

define('DB_USER', 'wordpress');

define('DB_PASSWORD', 'wordpress');

define('DB_HOST', 'mysqldb');

define('DB_CHARSET', 'utf8');

define('DB_COLLATE', '');

使用Docker Stack進行編排:

[root@docker01 services]# docker stack deploy -c wordpress.yml wordpress
Creating network wordpress_wps_net
Creating service wordpress_mysqldb
Creating service wordpress_php
Creating service wordpress_nginx

查看stack堆棧中的服務:

[root@docker01 services]# docker stack ls 
NAME       SERVICES
wordpress  3
[root@docker01 services]# docker stack services wordpress
ID            NAME               MODE        REPLICAS  IMAGE
4t1tzw3p41sv  wordpress_nginx    replicated  3/3       wordpress_nginx:latest
ethi5exgee4b  wordpress_php      replicated  12/12     wordpress_php-cgi:latest
kuqf94pda9xk  wordpress_mysqldb  replicated  1/1       mysql:5.6

查看stack服務的運行的任務:

[root@docker01 services]# docker stack ps wordpress -f 'desired-state=running'
ID            NAME                 IMAGE                     NODE                  DESIRED STATE  CURRENT STATE        ERROR  PORTS
3oqomt8pc7dp  wordpress_mysqldb.1  mysql:5.6                 docker01.contoso.com  Running        Running 2 hours ago         
oea1ff7vvygo  wordpress_nginx.1    wordpress_nginx:latest    docker01.contoso.com  Running        Running 2 hours ago         
66t9uy42fjdy  wordpress_php.1      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
xesem7154mv9  wordpress_php.2      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
tvp4v2y3fg0c  wordpress_nginx.2    wordpress_nginx:latest    docker01.contoso.com  Running        Running 2 hours ago         
e5b6i6y623lb  wordpress_nginx.3    wordpress_nginx:latest    docker01.contoso.com  Running        Running 2 hours ago         
lek976adweuj  wordpress_php.3      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
wivi95v5hh9r  wordpress_php.4      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
ymhschv3sdzf  wordpress_php.5      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
eqerngorx9lo  wordpress_php.6      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
zkz42pgv2h4u  wordpress_php.7      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
yszw5wa3xgn1  wordpress_php.8      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
xxrwlatxf6wj  wordpress_php.9      wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
ugt3kd21usaj  wordpress_php.10     wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
wo92batyp19a  wordpress_php.11     wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         
ifxhg0kpjxpz  wordpress_php.12     wordpress_php-cgi:latest  docker01.contoso.com  Running        Running 2 hours ago         

在Docker Swarm宿主機所在的局域網中找另外一臺主機,部署nginx並配置反向代理:

[root@salt-minion01 ~]# ifconfig |grep -A1 eth0
eth0      Link encap:Ethernet  HWaddr 00:0C:29:F3:43:86  
          inet addr:192.168.49.101  Bcast:192.168.49.255  Mask:255.255.255.0
[root@salt-minion01 ~]# cat /usr/local/nginx/conf.d/wordpress.conf 
upstream wordpress {
    server 192.168.49.41:8080 weight=10;
    server 192.168.49.42:8080 weight=10;
    server 192.168.49.43:8080 weight=10;
}

server {
    listen 80 default;
    server_name localhost;

    location / {
        proxy_pass http://wordpress;
        proxy_set_header Host $host;
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

}

準備完畢後,嘗試訪問nginx反向代理的IP地址:

Docker系列(十四):Docker Swarm集羣

已經成功看到wordpress的安裝界面,只需簡單配置一下就可以完成wordpress的安裝,就不再演示,至此通過Docker Stack部署wordpress完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章