一、docker swarm的介绍

第二十六节 docker swarm的部署_nginx

1.1  swarm介绍

Swarm是Docker公司推出的用来管理docker集群的平台,几乎全部用Go语言来完成的开发的,
代码开源在https://github.com/docker/swarm,它是将一群Docker宿主机变成一个单一的虚拟主机,
Swarm使用标准的Docker API接口作为其前端的访问入口,换言之,各种形式的Docker  Client(compose,docker-py等)
均可以直接与Swarm通信,甚至Docker本身都可以很容易的与Swarm集成,这大大方便了用户将原本基于单节点的系统移植到
Swarm上,同时Swarm内置了对Docker网络插件的支持,用户也很容易的部署跨主机的容器集群服务。

Docker Swarm和Docker Compose一样,都是Docker官方容器编排项目,但不同的是,Docker Compose是一个在单个服务器
或主机上创建多个容器的工具,一而Docker Swarm则可以在多个服务器或主机上创建容器集群服务,对于微服务的部署,显然
Docker Swarm会更加适合。

从Docker 1.12.0版本开始,Docker Swarm已经包含在Docker引擎中(docker swarm),并且已经内置了服务发现工具,
我们就不需要像之前一样,再配置 Etcd 或者 Consul来进行服务发现配置了。

Swarm deamon只是一个调度器(Scheduler)加路由器(router),Swarm自己不运行容器,它只是接受Docker客户端发来的请求,
调度适合的节点来运行容器,这就意味着,即使Swarm由于某些原因挂掉了,集群中的节点也会照常运行,当Swarm重新恢复运行之后,
他会收集重建集群信息。

1. docker swarm
2. messos+ marathon ===> dcos
3. kubernetres ===> kes /k3s
4. nomand

主要解决问题:

解决docker server的集群化管理和部署。

Swarm通过对Docker宿主机上添加的标签信息来将宿主机资源进行细粒度分区,通过分区来帮助用户将容器部署到目标宿主机上,同样通过分区方式还能提供更多的资源调度策略扩展。

第二十六节 docker swarm的部署_Docker_02

第二十六节 docker swarm的部署_nginx_03

swarm能做什么:

  1. 管理节点高可用,原生支持管理节点高可用,采用raft共识算法来支撑管理节点高可用。
  2. 应用程序高可用,支持服务伸缩,滚动更新和应用回滚等部署策略。

第二十六节 docker swarm的部署_Docker_04

1.2  swarm架构

第二十六节 docker swarm的部署_docker_05

在图中可以看出Docker Client使用Swarm对集群(Cluster)进行调度使用。

可以看出,Swarm是典型的master-slave结构,通过发现服务来选举manager。

manager是中心管理节点,各个node上运行agent接受manager的统一管理,

集群会自动通过Raft协议分布式选举出manager节点,无需额外的发现服务支持,

避免了单点的瓶颈问题,同时也内置了DNS的负载均衡和对外部负载均衡机制的集成支持

1.3  swarm概念

1.3.1  Swarm

Swarm集群的管理和编排是使用嵌入docker引擎的SwarmKit,可以在docker初始化时启动swarm模式或者加入已存在的swarm。

1.3.2  Node

Node一个节点是docker引擎集群的一个实例。您还可以将其视为Docker节点。
可以在单个物理计算机或云服务器上运行一个或多个节点,但生产群集部署通常包括分布在
多个物理和云计算机上的Docker节点。要将应用程序部署到swarm,请将服务定义提交给管理器节点。
管理器节点将称为任务的工作单元分派给工作节点。

1.3.3  Manager

Manager节点还执行维护所需群集状态所需的编排和集群管理功能。
Manager节点选择单个领导者来执行编排任务。
工作节点接收并执行从管理器节点分派的任务。
默认情况下,管理器节点还将服务作为工作节点运行,但您可以将它们配置为仅运行管理器任务
并且是仅管理器节点。代理程序在每个工作程序节点上运行,并报告分配给它的任务。
工作节点向管理器节点通知其分配的任务的当前状态,以便管理器可以维持每个工作者的期望状态。

1.3.4  Service

Service一个服务是任务的定义,管理机或工作节点上执行。它是群体系统的中心结构,是用户与群体交互的主要根源。
创建服务时,你需要指定要使用的容器镜像。

1.3.5  Task

Task任务是在docekr容器中执行的命令,Manager节点根据指定数量的任务副本分配任务给worker节点。

1.3.6   概念汇总

(1)docker的两种模式,单引擎模式和swarm集群模式。
单引擎模式-----docker server没有加入任何集群,且自身也没有加入初始化为swarm 节点,简单的说就是我们平时所操作的孤立的
docker server。
swarm模式----当docker server 加入到任意swarm集群,或者通过docker swarm init初始化swarm集群时,docker server会自动
切换到swarm 集群模式。

(2)swarm集群中节点分类,分为:manager(管理节点)、node(工作节点)
manager:是Swarm Daemon工作的节点,包含了调度器、路由、服务发现等功能,负责接收客户端的集群管理请求以及调度Node进行
具体工作。manager 本身也是一个node节点。
Node:接受manager调度,对容器进行创建、扩容和销毁等具体操作。

(3)raft共识算法,是实现分布式共识的一种算法,主要用来管理日志复制的一致性。
当Docker引擎在swarm模式下运行时,manager节点实现Raft一致性算法来管理全局集群状态。
Docker swarm模式之所以使用一致性算法,是为了确保集群中负责管理和调度任务的所有manager节点都存储相同的一致状态。
在整个集群中具有相同的一致状态意味着,如果出现故障,任何管理器节点都可以拾取任务并将服务恢复到稳定状态。
例如,如果负责在集群中调度任务的领导管理器意外死亡,则任何其他管理器都可以选择调度任务并重新平衡任务以匹配所需状态。
使用一致性算法在分布式系统中复制日志的系统需要特别小心。它们通过要求大多数节点在值上达成一致,确保集群状态在出现故障时
保持一致。
Raft最多可承受(N-1)/2次故障,需要(N/2)+1名成员的多数或法定人数才能就向集群提议的值达成一致。
这意味着,在运行Raft的5个管理器集群中,如果3个节点不可用,系统将无法处理更多的请求来安排其他任务。
现有任务保持运行,但如果管理器集不正常,调度程序无法重新平衡任务以应对故障。

(4)Swarm管理节点高可用。
Swarm管理节点内置有对HA的支持,即使有一个或多个节点发送故障,剩余管理节点也会继续保证Swarm运转。
Swarm实现了一种主从方式的多管理节点的HA,即使有多个管理节点也只有一个节点出于活动状态,处于活动状态的节点被称为主节点
(leader),而主节点也是唯一一个会对Swarm发送控制命令的节点,如果一个备用管理节点接收到了Swarm命令,则它会将其转发给主节点。
(5)集群管理,集群创建和组织。
(6)节点管理,集群下节点角色信息变更,节点任务情况监控。
(7)服务管理,集群服务部署,服务管理等。

二、docker swarm的部署

2.1  使用swarm的前提

使用Swarm 前提:
Docker版本1.12+
集群节点之间保证
TCP 2377(集群管理)
TCP/UDP 7946(容器网络发现)
UDP 4789(Overlay网络)
端口通信

节点规划:
操作系统:Centos7.9_x64

192.168.80.25 lyc-80-25(manager)
192.168.80.26 lyc-80-26(workere1)
192.168.80.27 lyc-80-27(worker02)

2.2  构建swarm集群

2.2.1  创建集群manager

192.168.80.25 lyc-80-25(manager)
-----------------------------------------------
docker swarm init --advertise-addr 192.168.80.25
------------------------------------------------
docker swarm join --token SWMTKN-1-29deix0jpwjj5g2ny7q2plmc94hlh3hg8q2q653sfg841n3prj-44cjudb2ik7ry6d4gibc789a3 192.168.80.25:2377

第二十六节 docker swarm的部署_docker_06

2.2.2  work加入manager

192.168.80.26 lyc-80-26(worker01)
-------------------------------------
docker swarm join --token SWMTKN-1-29deix0jpwjj5g2ny7q2plmc94hlh3hg8q2q653sfg841n3prj-44cjudb2ik7ry6d4gibc789a3 192.168.80.25:2377

第二十六节 docker swarm的部署_Docker_07

192.168.80.27 lyc-80-27(worker02)
---------------------------------------
docker swarm join --token SWMTKN-1-29deix0jpwjj5g2ny7q2plmc94hlh3hg8q2q653sfg841n3prj-44cjudb2ik7ry6d4gibc789a3 192.168.80.25:2377

第二十六节 docker swarm的部署_docker_08

查看swarm的集群
manager:
docker node ls

第二十六节 docker swarm的部署_Docker_09

2.3  node节点的管理

docker node --

第二十六节 docker swarm的部署_nginx_10

2.4  service节点的管理

docker service  --help
create     创建一个容器的服务
inspect    列出创建容器服务器的详细信息
logs       列出创建容器当中正在运行的服务器
ls         查看当前的容器的信息主要是副本数
ps         查看当前集群中跑的容器信息
rm         删除当中的容器服务
rollback   回滚到上一个配置
scale      调整容器服务的副本数
update     更新服务

第二十六节 docker swarm的部署_Docker_11

2.4.1创建一个服务器

Docker 1.12版本提供服务的Scaling、health check、滚动升级等功能,
并提供了内置的dns、vip机制,实现service的服务发现和负载均衡能力
1)创建网络在部署服务
#创建网络
docker network create  -d  overlay  nginx_net

docker network ls

第二十六节 docker swarm的部署_docker_12

1)使用nginx镜像,创建一个具有一个副本(--replicas 1)的nginx服务

docker service create  --replicas 1  --network nginx_net  --name my_nginx  -p 80:80 nginx
docker service ls

第二十六节 docker swarm的部署_nginx_13

2)查询Swarm中服务的信息

-pretty使命令输出格式化为可读的格式,不加 -pretty 可以输出更详细的信息
【对比】
docker service inspect my_nginx
docker service inspect  --pretty   my_nginx

第二十六节 docker swarm的部署_docker_14

3)查询哪个节点正在运行该服务

如下该容器被调度到worker01-node节点上启动了,然后访问
http://192.168.80.26即可访问这个容器应用
(如果调度到其他节点,访问也是如此)
docker  service  ps  my_nginx

第二十六节 docker swarm的部署_nginx_15

第二十六节 docker swarm的部署_Docker_16

docker ps -a

第二十六节 docker swarm的部署_nginx_17

4)在Swarm中动态扩展服务(scale)

当然,如果只是通过service启动容器,swarm也算不上什么新鲜东西了。
Service还提供了复制(类似kubernetes里的副本)功能。
可以通过docker service scale命令来设置服务中容器的副本数:
比如将上面的my_nginx容器动态扩展到4个

docker service scale my_nginx=4

第二十六节 docker swarm的部署_Docker_18

查看nginx 节点集群分布
docker service ps my_nginx

第二十六节 docker swarm的部署_docker_19

核对容器开辟情况

第二十六节 docker swarm的部署_Docker_20

第二十六节 docker swarm的部署_Docker_21

第二十六节 docker swarm的部署_Docker_22

5)模拟宕机node节点

特别需要清楚的一点:
如果一个节点宕机了(即该节点就会从swarm集群中被踢出),则Docker应该会将在该节点运行的容器,
调度到其他节点,以满足指定数量的副本保持运行状态。

将lyc-80-26(worker01)上的docker停掉
停掉lyc-80-26 上面dockers
service docker stop

manager节点查看
-------------
docker node ls
docker service ps my_nginx

第二十六节 docker swarm的部署_docker_23

第二十六节 docker swarm的部署_Docker_24

在manager节点查看是否多开了一个my_nginx容器
docker ps -

第二十六节 docker swarm的部署_Docker_25

如果经过问题修复后,lyc-80-26集群的docker又正常回复服务
我们可以重新启动docker
service docker start

然后停掉lyc-80-25上的一个新拉起来的nginx容器
docker  stop  docker  stop  my_nginx.1.xn4mejn95ej2038thdoo38vcl
这个nginx副本会重新漂移到lyc-80-26主机上面

第二十六节 docker swarm的部署_docker_26

第二十六节 docker swarm的部署_nginx_27

第二十六节 docker swarm的部署_nginx_28

【结论】

在swarm  cluster集群中启动的容器,

在worker node节点上删除或停用后,

该容器会自动转移到其他的worker node节点上。

【其他命令】

//节点变更
docker node promote lyc-80-26//将worker提升为manger
docker node demote lyc-80-25//将manger降级为worker
docker node rm lyc-80-26//将节点删除
docker swarm leave
离开集群【不是管理节点也可以离开】

//节点分配
docker node update--availability drain lyc-80-26    //lyc-80-26不分配节点
docker node update--availability active lyc-80-26   //恢复lyc-80-26分配

Service操作
查看service
docker service ls  //查看该服务下所有容器
docker service ps  容器名//查看指定容器运行状态详情
docker service logs  容器名//查看指定容器的运行日志
docker service rm  服务名//删除某个服务

Replicas操作
//控制容器副本数(重启stack后会失效)
docker service scale  容器名=副本数量
增加/缩减副本数量

第二十六节 docker swarm的部署_nginx_29

滚动更新服务
docker service create  \
--replicas 3
--name redis  \
--update-delay 10s  \
redis:3.0.6

docker service update  --image  redis:3.0.7  redis

第二十六节 docker swarm的部署_docker_30

第二十六节 docker swarm的部署_nginx_31

滚动更新服务
docker service create  \
--replicas 3  \
--name redis  \
--update-delay 10s  \
redis:3.0.6

docker service update  --image  redis:3.0.7  redis


创建服务时设定更新策略
docker service create \
--name my_web  \
--replicas 10  \
--update-delay 10s  \
--update-parallelism  2  \
--update-failure-action  continue \
nginx:1.12

创建服务时设定回滚策略
docker service create
--name my_web  \
--replicas 10  \
--rollback-parallelism 2  \
--rollback-monitor 20s  \
--rollback-max-failure-ratio  .2  \
nginx:1.12

服务更新
docker service update  --image nginx:1.13  my_web
手动回滚
docker service update  --rollback   my_web
docker service create  --replicas 1  --name hello busybox ping www.baidu.com

docker service  ls
docker service ps hello

修改swarm 的集群的副本数
docker service scale hello=3

第二十六节 docker swarm的部署_Docker_32

滚动更新服务
docker service create  \
--replicas 3  \
--name redis  \
update-delay 10s  \
redis:3.0.6

docker service  ls
docker service ps redis

第二十六节 docker swarm的部署_docker_33

更新底层的镜像
docker service update --image redis:3.0.7  redis

docker service  ls
docker service ps redis

第二十六节 docker swarm的部署_Docker_34

回滚底层镜像
docker service update  --rollback  redis

第二十六节 docker swarm的部署_nginx_35

【故障演示】

3个管理节点的失效配额为(3-1)/2-1,
若失效数量小于或等于配额,
则swarm自动执行选举与切换;
若超出配额范围,
则须手动强制重建群集(docker swarm init-f/--force)


出现故障时,需要查看群集、节点、服务、任务等信息,
结合命令行输出与日志对故障原因进行定位。


Centos 7下的docker日志文件默认为/var/log/message,
管理节点的/var/lib/docker/swarm/目录用于保存群集状态
与管理日志,可以按备份目录-导入目录-重建群集的步骤执行故障恢复。


docker官方建议备份与导入操作在docker主进程停用后执行。

将集群节点都变为主节点
docker node promote lyc-80-26
docker node promote lyc-80-27

【扩展】
将集群节点都变为从节点
docker node demote lyc-80-26
docker node demote lyc-80-27

第二十六节 docker swarm的部署_Docker_36

将lyc-80-25主机停止:
poweroff

lyc-80-26主机任然可以使用
docker node ls

第二十六节 docker swarm的部署_Docker_37

停掉lyc-80-26
poweroff

查看lyc-80-27
docker service ls
这个时候集群不可用

第二十六节 docker swarm的部署_Docker_38

【重建集群】

备份Swarm集群
如果Swarm开启了auto-lock,需要
unlock key从backup中恢复swarm停止管理节点
备份/var/lib/docker/swarm整个目录


重启管理节点
在lyc-80-25节点上手动强制重建群集
docker swarm init  --force-new-cluster   //记住token
docker node rm lyc-80-26
docker node rm lyc-80-27

移除失效的lyc-80-26与lyc-80-27节点
lyc-80-26
docker swarm leave
lyc-80-27
docker swarm leave

重新加入swarm集群
lyc-80-26
docker swarm join --token SWMTKN-1-29deix0jpwjj5g2ny7q2plmc94hlh3hg8q2q653sfg841n3prj-44cjudb2ik7ry6d4gibc789a3 192.168.80.25:2377
lyc-80-27
docker swarm join --token SWMTKN-1-29deix0jpwjj5g2ny7q2plmc94hlh3hg8q2q653sfg841n3prj-44cjudb2ik7ry6d4gibc789a3 192.168.80.25:2377

第二十六节 docker swarm的部署_nginx_39

第二十六节 docker swarm的部署_nginx_40

第二十六节 docker swarm的部署_nginx_41

第二十六节 docker swarm的部署_Docker_42

群集恢复后,所有任务全部运行在lyc-80-25节点:
docker service update  --force
命令用于强制对群集任务进行负载均衡,该操作包括任务的停止与重新分配。

docker service update  --force my_nginx

第二十六节 docker swarm的部署_Docker_43

2.5  部署swarm集群监控

拉取镜像
docker pull dockersamples/visualizer

启动容器
docker service create   \
--name=viz  \
--publish=8080:8080/tcp  \
--constraint=node.role==manager  \
--mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock  \
dockersamples/visualizer

第二十六节 docker swarm的部署_nginx_44

打开页面:
http://192.168.80.25:8080

第二十六节 docker swarm的部署_docker_45

第二十六节 docker swarm的部署_Docker_46


三、数据的管理

3.1  将宿主机挂载到容器

Volume

#创建数据卷
docker service create  \
--mount type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>  \
--name myservice  \
<IMAGE>
#查看数据卷详细信息
docker volume inspect  <VOLUME-NAME>
创建容器
docker service create  \
--mount type=volume,src=nginx-vol,dst=/usr/share/nginx/html/  \
--name my_nginx  \
nginx

第二十六节 docker swarm的部署_nginx_47

docker service scale my_nginx=3

docker service ps my_nginx
docker ps -a

docker  exec   -ti   e8cbb40f4343  bash
cd /usr/share/nginx/html/
ls

第二十六节 docker swarm的部署_Docker_48

第二十六节 docker swarm的部署_Docker_49

exit退出容器后,查看实际映射文件
####volume 卷的使用
docker volume ls
docker volume inspect nginx-vol

cd  /var/lib/docker/volumes/nginx-vol/_data
ls

第二十六节 docker swarm的部署_Docker_50

3.2  将目录挂载到容器

【格式】
Bind Mounts
#读写挂载
docker service create  \
--mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH>   \
--name myservice  \
<IMAGE>


#只读挂载
docker service create   \
--mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH>,ro   \
--name myservice  \
<IMAGE>
【bind mount 挂载】
----------------------
mkdir   /app/wwwroot/

docker service create  \
--mount  type=bind,src=/app/wwwroot,dst=/usr/share/nginx/html   \
--name bind-nginx  \
nginx

第二十六节 docker swarm的部署_docker_51

进容器看下 是不是已经映射到
docker ps -a

docker  exec   -ti   e8cbb40f4343  bash
cd /usr/share/nginx/html/
ls

第二十六节 docker swarm的部署_Docker_52

3.3  NFS数据持久存储

结构
docker service create \
--mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'
--name myservice \
<IMAGE>


3.4  docker stack批量部署

3.4.1  docker stack的介绍

单机模式下,我们可以使用Docker Compose来编排多个服务,而Docker Swarm只能实现对单个服务的简单部署。

本文的主角Docker Stack,通过Docker Stack我们只需对已有的docker-compose.yml配置文件稍加改造就可以完成

Docker集群环境下的多服务编排。

3.4.2  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。

3.4.3  docker stack的命令

命令                                描述
docker stack deploy    部署新的堆栈或更新现有堆栈
docker stack ls            列出现有堆栈
docker stack ps           列出堆栈中的任务
docker stack rm          删除一个或多个堆栈
docker stack services   列出堆栈中的服务

3.4.4  docker stack部署项目

部署一个投票APP,包含如下服务:
5个应用服务:vote、redis、worker,db,result
工具服务:portainer和visualizer

第二十六节 docker swarm的部署_docker_53

首先创建一个 docker-compose.yml 文件,使用Docker Compose v3 语法

vim docker-compose.yaml
--------------------------
version: "3"
services:
  redis:
    image: redis:alpine
    ports:
      - "6379"
    networks:
      - frontend
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
  db:
    image: postgres:9.4
    environment:
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "postgres"
      POSTGRES_HOST_AUTH_METHOD: "trust"
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
    deploy:
      placement:
        constraints: [node.role == manager]
  vote:
    image: dockersamples/examplevotingapp_vote:before
    ports:
      - 5000:80
    networks:
      - frontend
    depends_on:
      - redis
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
      restart_policy:
        condition: on-failure
  result:
    image: dockersamples/examplevotingapp_result:before
    ports:
      - 5001:80
    networks:
      - backend
    depends_on:
      - db
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
  worker:
    image: dockersamples/examplevotingapp_worker
    networks:
      - frontend
      - backend
    deploy:
      mode: replicated
      replicas: 1
      labels: [APP=VOTING]
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
        window: 120s
      placement:
        constraints: [node.role == manager]

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]
        
  portainer:
    image: portainer/portainer
    ports:
      - "9000:9000"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]

networks:
  frontend:
  backend:

volumes:
  db-data:
 
----------------------------------------
----------格式化-------
yum install dos2unix
dos2unix  docker-compose.yaml
docker stack deploy example  --compose-file=docker-compose.yaml 

第二十六节 docker swarm的部署_Docker_54

docker  stack  services  example

第二十六节 docker swarm的部署_Docker_55

访问web页面
vote          192.168.80.25:5000
visualizer    192.168.80.25:ip:8080
portainer     192.168.80.25:ip:9000
result        192.168.80.25:ip:5001

192.168.80.25:5000

第二十六节 docker swarm的部署_docker_56

192.168.80.25:ip:8080

第二十六节 docker swarm的部署_nginx_57

访问portaine
http://192.168.80.25:9000
首次访问,需要设置密码,密码必须符合12位以上复杂性要求

初始化
http://192.168.80.25:9000/#/init/admin

第二十六节 docker swarm的部署_nginx_58