前言
之前介绍了使用 Docker Compose 在一台 Docker 主机下对多服务多容器进行编排,本文来介绍使用 Docker Swarm 实现对多台 Docker 主机的集群管理。
什么是 Docker Swarm
Swarm 是 Docker 公司在2014年12月初发布的一套用来管理 Docker 集群的较为简单的工具,由于 Swarm 使用标准的Docker API接口作为其前端访问入口,所以各种形式的Docker Client(dockerclient in go, docker_py, docker等)都可以直接与Swarm通信。老的 Docker Swarm 使用独立的外部KV存储(比如Consul、etcd、zookeeper),搭建独立运行的Docker主机集群,用户像操作单台Docker 机器一样操作整个集群,Docker Swarm 把多台 Docker 主机当做一台 Docker 主机来管理。新的 Swarm mode 是在docker 1.12版本中集成到 Docker 引擎中的,引入服务的概念,提供了众多的新特性,比如:具有容错能力的去中心化设计、内置服务发现、负载均衡、路由网格、动态伸缩、滚动更新、安全传输等。使得 Docker 原生的 Swarm mode 集群具备与 Mesos、Kubernetes 叫板的实力。
- Docker Swarm 和 Swarm mode 的区别
- 注:本文所说的 swarm 指的是 Swarm mode
配置说明
有两台主机 ( 主机1 、主机2 ) ,都已安装 Docker 。当然也可以使用 Docker 三剑客 之一的 Docker Machine 来创建一批运行 Docker 的虚拟机。
创建集群
在 主机1 上创建 swarm 集群,主机1 作为 manager 节点
$ docker swarm init --listen-addr 0.0.0.0:2377 --advertise-addr 主机1的IP
Swarm initialized: current node (kwsp26dy6gwjhocl6nxbez0bu) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-1023ixl3yabjyws8tjdyvob7r40mljsu8atzsmlju18wz2itlj-br6gzae8focpm0ccpjaquz251 主机1的IP:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
注意:主机1 的防火墙要开放对应的端口
加入集群
主机2 作为 worker 节点加入上面创建的集群
# 也就是上面创建完集群提示的命令
$ docker swarm join --token SWMTKN-1-1023ixl3yabjyws8tjdyvob7r40mljsu8atzsmlju18wz2itlj-br6gzae8focpm0ccpjaquz251 主机1的IP:2377
This node joined a swarm as a worker.
当然,主机2 也可作为 manager 节点加入上面创建的集群,当 主机1 挂掉的时候它可以晋升为 Leader 。
在 主机1 上查询对应的 token
$ docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-1-1023ixl3yabjyws8tjdyvob7r40mljsu8atzsmlju18wz2itlj-07q9wti67nkvqixykkcbi1ge8 主机1的IP:2377
注意:这个 token 和前面的 token 是不一样的
在 主机2 上执行对应的命令作为 manager 节点加入集群。可以添加 --listen-addr
和 --advertise-addr
选项 ( 同创建集群步骤 )
$ docker swarm join --token SWMTKN-1-1023ixl3yabjyws8tjdyvob7r40mljsu8atzsmlju18wz2itlj-07q9wti67nkvqixykkcbi1ge8 主机1的IP:2377
This node joined a swarm as a manager.
查看集群节点
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kwsp26dy6gwjhocl6nxbez0bu * 主机1 Ready Active Leader 18.03.0-ce
19nnijmzs16o1f92e6wdmltsf 主机2 Ready Active Reachable 18.03.0-ce
创建一个 overlay 网络
$ docker network create --driver overlay network_swarm
在 swarm 集群上创建服务,并指定使用的网络
$ docker service create --replicas 2 --name service_test --network=network_swarm nginx:alpine
查看服务状态
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
yp2tn16eqaqc service_test replicated 2/2 nginx:alpine
$ docker service ps service_test
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8mgu08gmy0bf service_test.1 nginx:alpine 主机1 Running Running 4 minutes ago
310uv4k6aro8 service_test.2 nginx:alpine 主机2 Running Running 4 minutes ago
在 主机1 上查看容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9df9c365d68 nginx:alpine "nginx -g 'daemon of…" About a minute ago Up About a minute 80/tcp service_test.1.8mgu08gmy0bf9pxovz5zxn8ih
在 主机2 上查看容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
43704a7989ed nginx:alpine "nginx -g 'daemon of…" About a minute ago Up 2 minutes 80/tcp service_test.2.310uv4k6aro8n47c37zj0w1c8
可以看到有一个名为 service_test 的服务,有两个实例 ( service_test.1.xxx 和 service_test.2.xxx ) 分别部署在两台机器上。
调整服务的实例个数
$ docker service scale service_test=3
service_test scaled to 3
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
# 主机1
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed226d0dc795 nginx:alpine "nginx -g 'daemon of…" 24 seconds ago Up 23 seconds 80/tcp service_test.3.u6iop1v2gup15asywx6g927vo
d9df9c365d68 nginx:alpine "nginx -g 'daemon of…" 2 minutes ago Up 8 minutes 80/tcp service_test.1.8mgu08gmy0bf9pxovz5zxn8ih
服务发现与自动恢复
手动删除或关闭运行中的容器,过几秒之后就能发现 Swarm 会自动创建新的容器来顶替失效的容器。
负载均衡
在单机模式下进行测试,情况会简单点
- 删除已有的服务
$ docker service rm service_test
- worker 节点退出集群,使 manager 节点成为单节点的集群
$ docker swarm leave
- 创建带端口映射的服务 service_test
$ docker service create --replicas 2 --name service_test -p 80:80 --network=network_swarm nginx:alpine
- 创建作为干扰的服务 service_test2 ( 和 service_test 共用一个网络)
$ docker service create --replicas 1 --name service_test2 -p 81:80 --network=network_swarm nginx:alpine
- 查看所有容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2d9a5111659 nginx:alpine "nginx -g 'daemon of…" 19 seconds ago Up 17 seconds 80/tcp service_test2.1.rdseqv5oq2cli3vojsk5unibm
cf09fa224a32 nginx:alpine "nginx -g 'daemon of…" About a minute ago Up About a minute 80/tcp service_test.2.xdl050dxdrzfpdzjwjf40mook
1b998a72b03b nginx:alpine "nginx -g 'daemon of…" About a minute ago Up About a minute 80/tcp service_test.1.fgu08x9mi33xd002ctdjoeyv0
- 修改每个 nginx 的页面内容
# 注意:容器id修改为实际的容器id
# service_test.1
$ docker exec -i 1b sed -i 's$<title>Welcome to nginx!</title>$<title>Welcome to nginx-1!</title>$' /usr/share/nginx/html/index.html
# service_test.2
$ docker exec -i cf sed -i 's$<title>Welcome to nginx!</title>$<title>Welcome to nginx-2!</title>$' /usr/share/nginx/html/index.html
# service_test2.1
$ docker exec -i c2 sed -i 's$<title>Welcome to nginx!</title>$<title>Welcome to nginx-3!</title>$' /usr/share/nginx/html/index.html
- 查看效果
$ curl 127.0.0.1:81 | grep title
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 614 100 614 0 0 181k 0 --:--:-- --:--:-- --:--:-- 199k
<title>Welcome to nginx-3!</title>
$ curl 127.0.0.1 | grep title
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 614 100 614 0 0 196k 0 --:--:-- --:--:-- --:--:-- 299k
<title>Welcome to nginx-2!</title>
$ curl 127.0.0.1 | grep title
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 614 100 614 0 0 164k 0 --:--:-- --:--:-- --:--:-- 299k
<title>Welcome to nginx-1!</title>
$ curl 127.0.0.1 | grep title
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 614 100 614 0 0 174k 0 --:--:-- --:--:-- --:--:-- 299k
<title>Welcome to nginx-2!</title>
...
可以看出访问 80 端口会交替访问 service_test.1 和 service_test.2 且不会转发到 service_test2.1 上,说明 负载均衡 成功。
- 查看宿主机的 NAT 映射情况
$ sudo iptables -t nat -nL
...
Chain DOCKER-INGRESS (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:81 to:172.18.0.2:81
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.18.0.2:80
RETURN all -- 0.0.0.0/0 0.0.0.0/0
可以看出宿主机的 80 端口被映射到了 172.18.0.2 的 80 端口上,而这个 172.18.0.2 的 IP 就属于 overlay 网络 network_swarm
。对于 overlay 网络的原理不是很清楚,也没深入研究下去,有了解的小伙伴欢迎指教。不过,我猜测应该是 overlay 网络内部实现了某种一对多的端口映射从而实现 负载均衡 的效果。
使用 Docker Stack 部署多服务集群( 参考 )
Docker Swarm 只能实现对单个服务的简单部署,如果要实现和 Docker Compose 一样的多服务编排就需要用到 Docker Stack 命令,具体用法请看另一篇 文章 。
Swarm 的不足
- 功能简单有限
- 当集群中某台机器的资源 ( CPU、内存等 ) 不足时,Swarm 在部署服务的时候还是会傻傻地平均分配容器到这台机器上。
常用命令
- docker swarm 命令用于管理 Swarm 群集
命令 | 描述 |
---|---|
docker swarm init | 初始化一个 swarm 群集 |
docker swarm join | 加入群集作为节点或管理器 |
docker swarm join-token | 管理用于加入群集的令牌 |
docker swarm leave | 离开 swarm 群集 |
docker swarm unlock | 解锁 swarm 群集 |
docker swarm unlock-key | 管理解锁钥匙 |
docker swarm update | 更新 swarm 群集 |
- docker node 命令用于管理 Swarm 群集中的机器节点
命令 | 描述 |
---|---|
docker node demote | 从 swarm 群集管理器中降级一个或多个节点 |
docker node inspect | 显示一个或多个节点的详细信息 |
docker node ls | 列出 swarm 群集中的节点 |
docker node promote | 将一个或多个节点推入到群集管理器中 |
docker node ps | 列出在一个或多个节点上运行的任务,默认为当前节点 |
docker node rm | 从 swarm 群集删除一个或多个节点 |
docker node update | 更新一个节点 |
- docker service 命令用于管理 Swarm 群集中的服务
命令 | 描述 |
---|---|
docker service create | 创建服务 |
docker service inspect | 显示一个或多个服务的详细信息 |
docker service logs | 获取服务的日志 |
docker service ls | 列出服务 |
docker service rm | 删除一个或多个服务 |
docker service scale | 设置服务的实例数量 |
docker service update | 更新服务 |
docker service rollback | 恢复服务至 update 之前的配置 |