Docker Swarm


Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源。Swarm和Kubernetes比较类似,具有的功能也较kubernetes更少一些。

基本特性

Docker Swarm具有如下基本特性:

Docker集群管理

使用内置的集群管理功能,可以直接通过Docker CLI命令来创建Swarm集群,然后去部署应用服务,而不再需要其它外部的软件来创建和管理一个Swarm集群。

去中心化设计

Swarm集群中包含Manager和Worker两类Node,可以直接基于Docker Engine来部署任何类型的Node。而且,在Swarm集群运行期间,既可以对其作出任何改变,实现对集群的扩容和缩容等,如添加Manager Node,如删除Worker Node,而做这些操作不需要暂停或重启当前的Swarm集群服务。

服务扩容缩容

对于部署的每一个应用服务,可以通过命令行的方式,设置启动多少个Docker容器去运行它。已经部署完成的应用,如果有扩容或缩容的需求,只需要通过命令行指定需要几个Docker容器即可,Swarm集群运行时便能自动地、灵活地进行调整。

协调预期状态与实际状态的一致性

Swarm集群Manager Node会不断地监控集群的状态,协调集群状态使得预期状态和实际状态保持一致。例如启动了一个应用服务,指定服务副本为10,则会启动10个Docker容器去运行,如果某个Worker Node上面运行的2个Docker容器挂掉了,则Swarm Manager会选择集群中其它可用的Worker Node,并创建2个服务副本,使实际运行的Docker容器数仍然保持与预期的10个一致。

多主机网络

可以为待部署应用服务指定一个Overlay网络,当应用服务初始化或者进行更新时,Swarm Manager在给定的Overlay网络中为Docker容器自动地分配IP地址,实际是一个虚拟IP地址(VIP)。

服务发现

Swarm Manager会给集群中每一个服务分配一个唯一的DNS名称,对运行中的Docker容器进行负载均衡。可以通过Swarm内置的DNS Server,查询Swarm集群中运行的Docker容器状态。

负载均衡

在Swarm内部,可以指定如何在各个Node之间分发服务容器(Service Container),实现负载均衡。如果想要使用Swarm集群外部的负载均衡器,可以将服务容器的端口暴露到外部。

swarm mode中的节点

下面是swarm node的架构图:

docker_swarm_mode_arch.png

manager node

  • manager node作为整个集群的调度者,负责分配资源、编排容器、分发task、管理各种service

  • 为了保持高可用和容错率,需要保持多个manager node,manager node使用一种Raft一致性算法来保持集群的高可用状态

  • manager Node也作为一个Worker Node来执行Task,Swarm支持配置Manager只作为一个专用的管理Node

Worker Node

  • Worker Node不负责维护集群的整体信息

  • Worker Node接收由Manager Node调度并指派的Task,启动一个Docker容器来运行指定的服务,并且Worker Node需要向Manager Node汇报被指派的Task的执行状态

Swarm集群搭建

搭建如下Swarm集群,包括三个Node,对应的主机名和IP地址分别如下所示,这三台机器是可以互相通信的:

Node IP
manager 192.168.33.10
worker1 192.168.33.11
worker2 192.168.33.12

启动manager节点

使用下面的命令可以创建一个manager节点:

ubuntu@swarm-manager:~$ docker swarm init --advertise-addr 192.168.33.10
Swarm initialized: current node (x579h1dmbuiftl3t0v1bqm5pr) is now a manager.

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

    docker swarm join \
    --token SWMTKN-1-05k8n988php7b5yl90lz6r8kusfgli5wvm6buc8m3n2tkvsiko-bxnunif1vx4og9s9dmvy8lf0y \
    192.168.33.10:2377

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

创建manager节点的时候会返回一个token,那个token是worker节点加入集群的token。

启动worker节点

使用如下的命令,可以把worker1、worker2节点加入集群中:

ubuntu@swarm-worker1:~$ docker swarm join --token SWMTKN-1-05k8n988php7b5yl90lz6r8kusfgli5wvm6buc8m3n2tkvsiko-bxnunif1vx4og9s9dmvy8lf0y 192.168.33.10:2377
This node joined a swarm as a worker.

ubuntu@swarm-worker2:~$ docker swarm join --token SWMTKN-1-05k8n988php7b5yl90lz6r8kusfgli5wvm6buc8m3n2tkvsiko-bxnunif1vx4og9s9dmvy8lf0y 192.168.33.10:2377
This node joined a swarm as a worker.

查看节点的状态

ubuntu@swarm-manager:~$ docker node ls
ID                           HOSTNAME       STATUS  AVAILABILITY  MANAGER STATUS
f9qlbs5mpalab9kj5c9iabc9z    swarm-worker1  Ready   Active
py76ujrlk3ib7yg475ix98lxv    swarm-worker2  Ready   Active
x579h1dmbuiftl3t0v1bqm5pr *  swarm-manager  Ready   Active        Leader

上面信息中,AVAILABILITY表示Swarm Scheduler是否可以向集群中的某个Node指派Task,对应有如下三种状态:

  • Active:集群中该Node可以被指派Task

  • Pause:集群中该Node不可以被指派新的Task,但是其他已经存在的Task保持运行

  • Drain:集群中该Node不可以被指派新的Task,Swarm Scheduler停掉已经存在的Task,并将它们调度到可用的Node上

Node状态变更管理

Node的AVAILABILITY有三种状态:Active、Pause、Drain,对某个Node进行变更,可以将其AVAILABILITY值通过Docker CLI修改为对应的状态即可,下面是常见的变更操作:

  • 设置Manager Node只具有管理功能

  • 对服务进行停机维护,可以修改AVAILABILITY为Drain状态

  • 暂停一个Node,然后该Node就不再接收新的Task

  • 恢复一个不可用或者暂停的Node

如下,将Manager Node的AVAILABILITY值修改为Drain状态,使其只具备管理功能,执行如下命令:

ubuntu@swarm-manager:~$ docker node update --availability drain x579h1dmbuiftl3t0v1bqm5pr
x579h1dmbuiftl3t0v1bqm5pr
ubuntu@swarm-manager:~$ docker node ls
ID                           HOSTNAME       STATUS  AVAILABILITY  MANAGER STATUS
f9qlbs5mpalab9kj5c9iabc9z    swarm-worker1  Ready   Active
py76ujrlk3ib7yg475ix98lxv    swarm-worker2  Ready   Active
x579h1dmbuiftl3t0v1bqm5pr *  swarm-manager  Ready   Drain         Leader

Node提权/降权

改变Node的角色,Worker Node可以变为Manager Node,这样实际Worker Node有工作Node变成了管理Node。如下,将worker1和worker2都升级为Manager Node,执行如下命令:

ubuntu@swarm-manager:~$ docker node promote swarm-worker1 swarm-worker2
Node swarm-worker1 promoted to a manager in the swarm.
Node swarm-worker2 promoted to a manager in the swarm.
ubuntu@swarm-manager:~$
ubuntu@swarm-manager:~$ docker node ls
ID                           HOSTNAME       STATUS  AVAILABILITY  MANAGER STATUS
f9qlbs5mpalab9kj5c9iabc9z    swarm-worker1  Ready   Active        Reachable
py76ujrlk3ib7yg475ix98lxv    swarm-worker2  Ready   Active        Reachable
x579h1dmbuiftl3t0v1bqm5pr *  swarm-manager  Ready   Drain         Leader

对上面已提权的worker1和worker2执行降权,需要执行如下命令:

ubuntu@swarm-manager:~$ docker node demote swarm-worker1 swarm-worker2
Manager swarm-worker1 demoted in the swarm.
Manager swarm-worker2 demoted in the swarm.
ubuntu@swarm-manager:~$
ubuntu@swarm-manager:~$ docker node ls
ID                           HOSTNAME       STATUS  AVAILABILITY  MANAGER STATUS
f9qlbs5mpalab9kj5c9iabc9z    swarm-worker1  Ready   Active
py76ujrlk3ib7yg475ix98lxv    swarm-worker2  Ready   Active
x579h1dmbuiftl3t0v1bqm5pr *  swarm-manager  Ready   Drain         Leader

退出Swarm集群

如果Worker想要退出Swarm集群,在Worker Node上,执行如下命令:

ubuntu@swarm-worker1:~$ docker swarm leave
Node left the swarm.

在manager节点上查看节点的信息,可以看到worker1状态为DOWN

ubuntu@swarm-manager:~$ docker node ls
ID                           HOSTNAME       STATUS  AVAILABILITY  MANAGER STATUS
f9qlbs5mpalab9kj5c9iabc9z    swarm-worker1  Down    Active
py76ujrlk3ib7yg475ix98lxv    swarm-worker2  Ready   Active
x579h1dmbuiftl3t0v1bqm5pr *  swarm-manager  Ready   Drain         Leader

如果Manager想要退出Swarm集群,但是集群中还存在其它的Worker Node,则需要加上一个强制选项:

ubuntu@swarm-manager:~$ docker swarm leave --force
Node left the swarm.

管理服务

在Swarm mode下使用Docker,可以实现部署运行服务、服务扩容缩容、删除服务、滚动更新等功能,在Swarm集群上部署服务,必须在Manager Node上进行操作。下图是Service、Task、Container(容器)这个三个概念的关系:

service_task_container.png

创建服务

首先要启动Swarm集群,使用了如下的命令创建了一个demo的service:

ubuntu@swarm-manager:~$ docker service create --name demo busybox sh -c "while true; do sleep 3600; done"
qbju4in1fqq5oa7j67ff2pp48

查看服务

可以看到当前的demo service运行在swarm-manager节点上:

ubuntu@swarm-manager:~$ docker service ls
ID            NAME  MODE        REPLICAS  IMAGE
qbju4in1fqq5  demo  replicated  1/1       busybox:latest

ubuntu@swarm-manager:~$ docker service ps demo
ID            NAME    IMAGE           NODE           DESIRED STATE  CURRENT STATE          ERROR  PORTS
968zbp0nz05k  demo.1  busybox:latest  swarm-manager  Running        Running 2 minutes ago

ubuntu@swarm-manager:~$ docker ps
CONTAINER ID        IMAGE                                                                             COMMAND                  CREATED             STATUS              PORTS               NAMES
1f2d80f554ca        busybox@sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812   "sh -c 'while true..."   4 minutes ago       Up 4 minutes                            demo.1.968zbp0nz05kahew3pp2y4j4c

扩容缩容服务

Docker Swarm支持服务的扩容缩容,可以replicated模式指定服务Task的个数,如下命令将demo service水平扩容成5个:

ubuntu@swarm-manager:~$ docker service scale demo=5
demo scaled to 5
ubuntu@swarm-manager:~$
ubuntu@swarm-manager:~$ docker service ls
ID            NAME  MODE        REPLICAS  IMAGE
qbju4in1fqq5  demo  replicated  5/5       busybox:latest

ubuntu@swarm-manager:~$ docker service ps demo
ID            NAME    IMAGE           NODE           DESIRED STATE  CURRENT STATE           ERROR  PORTS
968zbp0nz05k  demo.1  busybox:latest  swarm-manager  Running        Running 9 minutes ago
1mgc8aj4mkoy  demo.2  busybox:latest  swarm-worker1  Running        Running 57 seconds ago
7l49mkwuhoym  demo.3  busybox:latest  swarm-worker2  Running        Running 43 seconds ago
ho68t3g7sf2p  demo.4  busybox:latest  swarm-worker2  Running        Running 43 seconds ago
so3ajlroy35j  demo.5  busybox:latest  swarm-manager  Running        Running 57 seconds ago

可以看到在swarm-manager和swarm-worker2有两个service、swarm-worker1有一个service。如在swarm-worker2节点上使用docker ps,可以看到有个demo容器在运行:

ubuntu@swarm-worker2:~$ docker ps
CONTAINER ID        IMAGE                                                                             COMMAND                  CREATED             STATUS              PORTS               NAMES
ee77dc7037b3        busybox@sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812   "sh -c 'while true..."   2 minutes ago       Up 2 minutes                            demo.3.7l49mkwuhoymkxq5yr72qaa8z
c036b5ad902d        busybox@sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812   "sh -c 'while true..."   2 minutes ago       Up 2 minutes                            demo.4.ho68t3g7sf2pk7cp4bt8l9q5y

如果把swarm-worker2节点上其中一个容器删除,Swarm Manager会选择集群中其它可用的节点,再创建一个服务,使实际运行的Docker容器数仍然保持与预期的5个一致:

ubuntu@swarm-manager:~$ docker service ls
ID            NAME  MODE        REPLICAS  IMAGE
qbju4in1fqq5  demo  replicated  4/5       busybox:latest
ubuntu@swarm-manager:~$
ubuntu@swarm-manager:~$ docker service ls
ID            NAME  MODE        REPLICAS  IMAGE
qbju4in1fqq5  demo  replicated  5/5       busybox:latest


ubuntu@swarm-manager:~$ docker service ps demo
ID            NAME        IMAGE           NODE           DESIRED STATE  CURRENT STATE           ERROR                        PORTS
968zbp0nz05k  demo.1      busybox:latest  swarm-manager  Running        Running 19 minutes ago
1mgc8aj4mkoy  demo.2      busybox:latest  swarm-worker1  Running        Running 11 minutes ago
7l49mkwuhoym  demo.3      busybox:latest  swarm-worker2  Running        Running 10 minutes ago
p1a2gc7in33s  demo.4      busybox:latest  swarm-worker2  Running        Running 52 seconds ago
ho68t3g7sf2p   \_ demo.4  busybox:latest  swarm-worker2  Shutdown       Failed 57 seconds ago   "task: non-zero exit (137)"
so3ajlroy35j  demo.5      busybox:latest  swarm-manager  Running        Running 11 minutes ago

在swarm-worker2节点上删除了一个服务后,最终在swarm-worker2节点上启动了一个新的服务。

缩容服务: 只需要将副本数小于当前应用服务拥有的副本数即可实现,大于指定缩容副本数的副本会被删除。

删除服务

使用下面的命令即可把服务删除:

ubuntu@swarm-manager:~$ docker service rm demo
demo

Swarm部署服务的例子

如下使用Swarm部署Dcoker Compose那节中使用的flask app的例子:

首先使用Dockerfile build一个flaskredis_web的image,要把image push到自己的hub里面,因为在部署的时候Swarm如果发现本机没有相应的image,就会从hub里面拉取,如果hub里面不存在,则会报错:

ubuntu@swarm-manager:~/flask-redis$ docker build -t heqingliang/flaskredis_web .

创建一个overlay网络:

ubuntu@swarm-manager:~/flask-redis$ docker network create -d overlay demo
bp3clp7ooa8bl2rpmqvd1b1um
ubuntu@swarm-manager:~/flask-redis$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
de6c660c5569        bridge              bridge              local
bp3clp7ooa8b        demo                overlay             swarm
ff50d198b5dc        docker_gwbridge     bridge              local
65e22acf0beb        host                host                local
wdh4p4u1ch9w        ingress             overlay             swarm
ba700ffa1e8f        my-bridge           bridge              local
c305a4b53c3b        none                null                local

创建redis服务:

ubuntu@swarm-manager:~/flask-redis$ docker service create --name redis --network demo redis
td7e95h7jhwmraziiy7f8p8u3
ubuntu@swarm-manager:~/flask-redis$ docker service ps redis
ID            NAME     IMAGE         NODE           DESIRED STATE  CURRENT STATE           ERROR  PORTS
6ol4i4fv1u92  redis.1  redis:latest  swarm-manager  Running        Running 16 seconds ago

创建web服务,可以看到falsk-web服务运行在worker1节点:

ubuntu@swarm-manager:~/flask-redis$ docker service create --name flask-web -p 5000:5000 --network demo --env REDIS_HOST=redis heqingliang/flaskredis_web

ubuntu@swarm-manager:~/flask-redis$ docker service ps flask-web
ID            NAME         IMAGE                       NODE           DESIRED STATE  CURRENT STATE               ERROR  PORTS
cjh5ku96wtj9  flask-web.1  heqingliang/flaskredis_web  swarm-worker1  Running        Running about a minute ago

访问flask-web服务, 虽然falsk-web服务运行在swarm-worker1节点,但是访问集群中的任意一个节点的5000端口都可以,其他节点会转发到swarm-worker1节点:

ubuntu@swarm-manager:~/flask-redis$ curl 192.168.33.11:5000
<h1>Hello Container, I have been seen b'1' times.</h1>
ubuntu@swarm-manager:~/flask-redis$ curl 192.168.33.11:5000
<h1>Hello Container, I have been seen b'2' times.
ubuntu@swarm-manager:~/flask-redis$
ubuntu@swarm-manager:~/flask-redis$ curl 192.168.33.10:5000
<h1>Hello Container, I have been seen b'3' times.</h1>
ubuntu@swarm-manager:~/flask-redis$ curl 192.168.33.10:5000
<h1>Hello Container, I have been seen b'4' times.</h1>
ubuntu@swarm-manager:~/flask-redis$
ubuntu@swarm-manager:~/flask-redis$ curl 192.168.33.12:5000
<h1>Hello Container, I have been seen b'5' times.</h1>