概述
目录
- 十、 Docker Swarm
- 1、几个概念
- 2、swarm 集群
- 3、运行第一个 Service
- 4、实现 Service 伸缩
- 5、实现 Failover
- 6、内部访问 Service
- 7、外部访问service
- 8、ingress 网络
- 9、service之间通信
- 10、滚动更新 Service
- 11、replicated mode vs global mode
- 12、Label 控制 Service 的位置
- 13、 Health Check
- 14、使用 Secret
- 15、stack
十、 Docker Swarm
1、几个概念
1、Docker Swarm Mode
Docker v1.12 是一个非常重要的版本,Docker 重新实现了集群的编排方式。在此之前,提供集群功能的 Docker Swarm 是一个单独的软件,而且依赖外部数据库(比如 Consul、etcd 或 Zookeeper)。
从 v1.12 开始,Docker Swarm 的功能已经完全与 Docker Engine 集成,要管理集群,只需要启动 Swarm Mode。安装好 Docker,Swarm 就已经在那里了,服务发现也在那里了(不需要安装 Consul 等外部数据库)。
相比 Kubernetes,用 Docker Swarm 创建集群非常简单,不需要额外安装任何软件,也不需要做任何额外的配置。很适合作为学习容器编排引擎的起点。
2、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,又运行单独的容器。
3、node
swarm 中的每个 Docker Engine 都是一个 node,有两种类型的 node: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 就可以维护整个集群的状态。
4、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 处于期望的三个副本状态。
2、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 集群就已经搭建好了。
3、运行第一个 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 与普通的容器还没有太大的不同
4、实现 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.45
。
前面我们的场景是 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.4
和 web_server.5
这两个副本已经被删除了。
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.1
和 web_server.2
已经从 swarm-worker1 迁移到了 swarm-worker2,之前运行在故障节点 swarm-worker1 上的副本状态被标记为 Shutdown
。即使worker1恢复也不行
6、内部访问 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。
7、外部访问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 端口。
8、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 网络
实际上:
- eth0 连接的是一个 overlay 类型的网络,名字为
ingress
,其作用是让运行在不同主机上的容器可以相互通信。 - 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
。
9、service之间通信
微服务架构的应用由若干 service 组成。比如有运行 httpd 的 web 前端,有提供缓存的 memcached,有存放数据的 mysql,每一层都是 swarm 的一个 service,每个 service 运行了若干容器。在这样的架构中,service 之间是必然要通信的。
1)服务发现
一种实现方法是将所有 service 都 publish 出去,然后通过 routing mesh 访问。但明显的缺点是把 memcached 和 mysql 也暴露到外网,增加了安全隐患。
如果不 publish,那么 swarm 就要提供一种机制,能够:
- 让 service 通过简单的方法访问到其他 service。
- 当 service 副本的 IP 发生变化时,不会影响访问该 service 的其他 service。
- 当 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.2
是 my_web
service 的 VIP(Virtual IP),swarm 会将对 VIP 的访问负载均衡到每一个副本。
对于服务的使用者(这里是 util.1),根本不需要知道 my_web
副本的 IP,也不需要知道 my_web
的 VIP,只需直接用 service 的名字 my_web
就能访问服务。
10、滚动更新 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 将按照如下步骤执行滚动更新:
- 停止第一个副本。
- 调度任务,选择 worker node。
- 在 worker 上用新的镜像启动副本。
- 如果副本(容器)运行成功,继续更新下一个副本;如果失败,暂停整个更新过程。
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
11、replicated mode vs global mode
Swarm 可以在 service 创建或运行过程中灵活地通过 --replicas
调整容器副本的数量,内部调度器则会根据当前集群的资源使用状况在不同 node 上启停容器,这就是 service 默认的 replicated
mode。在此模式下,node 上运行的副本数有多有少,一般情况下,资源更丰富的 node 运行的副本数更多,反之亦然。
除了 replicated
mode,service 还提供了一个 global
mode,其作用是强制在每个 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
12、Label 控制 Service 的位置
逻辑分两步:
- 为每个 node 定义 label。
- 设置 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。
13、 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
命令显示:
- 有的容器正在运行,状态为
UP
。 - 有的容器已经正常停止了,状态是
Exited (0)
。 - 有的则因发生故障停止了,退出代码为非 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 的命令,还有几个相关的参数:
--timeout
命令超时的时间,默认 30s。--interval
命令执行的间隔时间,默认 30s。--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 则能够从业务角度判断应用是否发生故障,是否需要重启。
14、使用 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 容器,方法是:
1、在 swarm manager 中创建 secret my_secret_data
,将密码保存其中。
[root@swarm-manager ~]# echo "my-secret-pw" | docker secret create my_secret_data -
i0n061n5kpat7q8kmp4q8n2ud
2、启动 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_password
由 target
指定。
② 环境变量 MYSQL_ROOT_PASSWORD_FILE
指定从 /run/secrets/mysql_root_password
中读取并设置 MySQL 的管理员密码。
- 问:在第一步创建 secret 时,不也是使用了明文吗?这跟在环境变量中直接指定密码有什么不同呢?
答:在我们的例子中创建 secret 和使用 secret 是分开完成的,其好处是将密码和容器解耦合。secret 可以由专人(比如管理员)创建,而运行容器的用户只需使用 secret 而不需要知道 secret 的内容。也就是说,例子中的这两个步骤可以由不同的人在不同的时间完成。
- 问:secret 是以文件的形式 mount 到容器中,容器怎么知道去哪里读取 secret 呢?
答:这需要 image 的支持。如果 image 希望它部署出来的容器能够从 secret 中读取数据,那么此 image 就应该提供一种方式,让用户能够指定 secret 的位置。最常用的方法就是通过环境变量,Docker 的很多官方 image 都是采用这种方式。比如 MySQL 镜像同时提供了 MYSQL_ROOT_PASSWORD
和 MYSQL_ROOT_PASSWORD_FILE
两个环境变量。用户可以用 MYSQL_ROOT_PASSWORD
显示地设置管理员密码,也可以通过 MYSQL_ROOT_PASSWORD_FILE
指定 secret 路径。
Secret 的使用场景
secret 可用于管理:
- 用户名和密码。
- TLS 证书。
- SSH 秘钥。
- 其他小于 500 KB 的数据。
secret 只能在 swarm service 中使用。普通容器想使用 secret,可以将其包装成副本数为 1 的 service。
数据中心有三套 swarm 环境,分别用于开发、测试和生产。对于同一个应用,在不同的环境中使用不同的用户名密码。我们可以在三个环境中分别创建 secret,不过使用相同的名字,比如 username
和 password
。应用部署时只需要指定 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_USER
和 MYSQL_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,部署成功。
15、stack
回忆一下前面部署 WordPress 应用的过程:
- 首先创建 secret。
- 然后创建 MySQL service,这是 WordPress 依赖的服务。
- 最后创建 WordPress service。
也就是说,这个应用包含了两个 service:MySQL 和 WordPress,它们之间有明确的依赖关系,必须先启动 MySQL。
为了保证这个依赖关系,我们控制了 docker secret
和 docker service
命令的执行顺序,只不过这个过程是手工完成的。
稍微复杂一点的是第三步,通过 if
判断 MySQL service 是否运行,如果是,则运行 WordPress service,否则通过 while
继续等待,直到 MySQL 运行。
这个脚本大体上能够工作,实现了自动化,但有两个缺点:
- 目前只有两个 service,还比较简单。现在的应用通常都包含多个 service,特别是采用 microservices 架构的应用,几十个 service 是很正常的。用 shell 脚本启动和管理如此多的 service 将是一件非常有挑战的任务。
- 用
while
和if
维护 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:db
和 wordpress
。
② secrets
定义了两个 secret:db_password
和 db_root_password
,在 service db
和 wordpress
的定义中引用了这两个 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 有明显的优势。
- YAML 描述的是
What
,是 stack 最终要达到的状态。
比如 service 有几个副本?使用哪个 image?映射的端口是什么?而脚本则是描述如何执行命令来达到这个状态,也就是How
。显而易见,What
更直观,也更容易理解。至于如何将What
翻译成How
,这就是 Docker swarm 的任务了,用户只需要告诉 Docker 想达到什么效果。 - 重复部署应用变得非常容易。
部署应用所需要的一切信息都已经写在 YAML 中,要部署应用只需一条命令docker stack deploy
。stack 的这种自包含特性使得在不同的 Docker 环境中部署应用变得极其简单。在开发、测试和生成环境中部署可以完全采用同一份 YAML,而且每次部署的结果都是一致的。 - 可以像管理代码一样管理部署。
YAML 本质上将应用的部署代码化了,任何对应用部署环境的修改都可以通过修改 YAML 来实现。可以将 YAML 纳入到版本控制系统中进行管理,任何对 YAML 的修改都会被记录和跟踪,甚至可以像评审代码一样对 YAML 执行 code review。应用部署不再是一个黑盒子,也不再是经验丰富的工程师专有的技能,所以的细节都在 YAML 中,清晰可见。
转载于:https://www.cnblogs.com/wlbl/p/10207749.html
最后
以上就是甜甜身影为你收集整理的docker入门基础(八)的全部内容,希望文章能够帮你解决docker入门基础(八)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复