从主机的层面来看,DockerSwarm管理的是DockerHost集群。所以先来讨论一个重要的概念-集群化(Clustering)。
服务器集群由一组网络上相互连接的服务器组成,它们一起协同工作。一个集群和一堆服务器最显著的区别在于:集群能够像单个系统那样工作,同时提供高可用、负载均衡和并行处理。
如果我们部署应用和服务时选择的是多个独立的服务器而非集群,资源的整体利用率则很难达到最优,因为我们无法提前知道如何分布这些应用才能达到资源利用的最大化。而且,应用使用资源的趋势是波动的,早上某些服务可能需要大量的内存,而下午使用量就降下来了。提前指定应用应该运行在哪个服务器上会丧失业务的弹性,当某个服务器宕机了,我们不得不手工将受影响的应用迁移到其他服务器上。
实现集群化后我们的思维方式就必须改变了:不再考虑一个一个的服务器,而是将集群看做是一个整体。
部署应用时,我们只考虑需要多少内存和CPU而不是考虑会使用那台服务器的内存和CPU。我们不应该关心应用会被部署在哪里,我们关心的是运行这个应用需要哪些资源,然后将它部署到集群,集群管理程序(比如DockerSwarm)会搞定这些细节。
集群整体容量的调整是通过往集群中添加和删除主机节点实现的。但不管做怎样的操作,集群始终还是一个整体。
我们会创建Docker Swarm集群、部署应用、伸缩扩展应用,以及对应用执行滚动升级。
Docker Swarm Mode
Dockerv1.12是一个非常重要的版本,Docker重新实现了集群的编排方式。在此之前,提供集群功能的DockerSwarm是一个单独的软件,而且依赖外部数据库(比如Consul、etcd或Zookeeper)。从v112开始,DockerSwarm的功能已经完全与DockerEngine集成,要管理集群,只需要启动 Swarm Mode。安装好DockerSwarm就已经在那里了,服务发现也在那里了(不需要安装Consul等外部数据库)。
相比Kubernetes,用DockerSwarm创建集群非常简单,不需要额外安装任何软件,也不需要做任何额外的配置。很适合作为学习容器编排引擎的起点。
重要概念
在创建集群之前,先明确几个概念。
swarm运行DockerEngine的多个主机组成的集群。
从V112开始,集群管理和编排功能已经集成进DockerEngine。当DockerEngine初始化了一个 swarm或者加入到一个存在的swarm时,它就启动了swarm mode。
没启动swarmmode时,Docker执行的是容器命令;运行swarmmode后,Docker增加了编排 service 的能力。
Docker允许在同一个Docker主机上既运行swarm service又运行单独的容器。
swarm中的每个DockerEngine都是一个node,有两种类型的node:manager 和 worker.
为了向swarm中部署应用,我们需要在manager node 上执行部署命令,manager node会将部署任务拆解并分配给一个或多个worker node 完成部署。
managernode负责执行编排和集群管理工作,保持并维护swarm处于期望的状态。swarm中如果有多个 manager node,它们会自动协商并选举出一个 leader 执行编排任务。
woker node接受并执行由manager node派发的任务。默认配置下manager node 同时也是一个
worknode会定期向managernode报告自己的状态和它正在执行的任务的状态,这样manager就可以维护整个集群的状态。
service定义了worker node上要执行的任务。swarm的主要编排任务就是保证service处于期望的状态下。
举一个service的例子:在swarm中启动一个http服务,使用的镜像是 httpdlatest,副本数为 3。 manager node 负责创建这个 service,经过分析知道需要启动3个 httpd 容器,根据当前各 worker node的状态将运行容器的任务分配下去,比如worker1上运行两个容器,worker2 上运行一个容器。运行了一段时间,worker2突然宕机了,manager监控到这个故障,于是立即在worker3上启动了一个新的httod 容器,这样就保证了service处于期望的三个副本状态。
实践 Docker Swarm。
创建三节点的swarm集群。
角色 p
swarm-manager 192.168.56.10
swarm-worker1 192.168.56.20
swarm-worker2 192.168.56.30
#cat /etc/hosts
192.168.56.10 swarm-manager
192.168.56.20 swarm-worker1
192.168.56.30 swarm-worker2
关闭SElinux,firewalld添加端口
# firewal1-cmd --add-port=2377/tcp--permanent
# firewal1-cmd --add-port=7946/tcp--permanent
# firewall-cmd --add-port=7946/udp --permanent
# firewall-cmd--add-port=4789/udp--permanent
# firewal1-cmd --add-port=4789/tcp--permanent
firewal1-cmd--reload
swarm-manager是managernode,swarm-worker1和swarm-worker2是worker node。所有节点的Docker版本均不低于v1.12。
在swarm-manager上执行如下命令创建swarm。
docker swarm init --advertise-addr192.168.56.101
# docker swarm init --advertise-addr 192.168.56.10
Swarm initialized: current node (j6p2rf6fpcw416tkn2pa0jji0)is now a manager.To add a worker to this swarm, run the following command:
docker swarm join--token SWMTKN-1-3g9tuoub2w34i8ysnmcnz3xa97bvgz8qrhd6wgenyrg650jii3-0hs8wh8h13i31ryowb73ndnx41921685610:2377
To add a manager to this swarm, run 'docker swarm join-token manager'and follow the instructions.
-advertise-addr 指定与其他 node 通信的地址。 docker swarm init 输出告诉我们:
1.swarm创建成功,swarm-manager成为manager node。2.添加 worker node 需要执行的命令。
3.添加manager node需要执行的命令。
执行 docker nodels 查看当前swarm的 node,目前只有一个manager。
# docker node ls
ID HOSTNAME STATUS AVAILABILITY ENGINE VERSION
j6p2rf6fpcw416tkn2ps0jji0* Ready Active swarm-manager Leader 19.03.2
复制前面的docker swarmjoin命令,在swarm-worker1和swarm-worker2 上执行,将它们添加到 swarm 中。命令输出如下:
#docker swarm join--token SWMTKN-1-
3g9tuoub2w34i8ysnmcnz3xa97bvgz8qrhd6wgenyrg650jii3-0hs8wh8h13i31ryowb73ndnx4192.168.56.10:2377
docker nodels可以看到两个worker node已经添加进来了。
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION j6p2rf6fpcw4l6tkn2ps0jji0 * swarm-manager Ready Active Leader 19.03.2 g6z8cbpxend7tu55asz2q9f3z swarm-worker1 Ready Active 19.03.2 raa8bbtyukqqdk3pa0vfvhtwp swarm-worker2 Ready Active 19.03.2
如果当时没有记录下docker swarm init 提示的添加worker的完整命令,可以通过 docker swarm join-token worker 查看。
注意:此命令只能在manager node 上执行。
在swarm中部署第一个service。
创建好了Swarm集群现在部署一个运行httpd镜像的service执行如下命令。(-p 可以暴露端口,让容器外可以访问)
docker service create --name web server httpd
部署service的命令形式与运行容器的dockerrun很相似,-name为service命名,httpd为镜像的名字。
[root@swarm-manager ~]# docker service create --name web_server httpd
r6b1gole12n3t351zijsoxxz9
overall progress: 1out of 1 tasks
1/1:running [==================================================>]verify:Service converged
通过docker servicels可以查看当前swarm 中的service。
[root@swarm-manager -]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
r6blgole12n3 web_server replicated 1/1 httpd:latest
REPLICAS显示当前副本信息0/1的意思是web_server这个service期望的容器副本数量为1,目前已经启动的副本数量为0。也就是当前service还没有部署完成。命令docker serviceps可以查看 service 每个副本的状态。
可以看到service唯一的副本被分派到swarm-worker1,当前的状态是Preparing,还没达到期望的状态Running,我们不仅要问,这个副本在Preparing什么呢?
其实答案很简单swarm-worker1是在pull镜像,下载完成后,副本就会处于Running状态了。 service的运行副本数也正常了。
[rootlswarm-manager-]# docker service ps web_server ID NAME IMAGE NODE
DESIRED STATE CURRENT STATE ERROR
PORTS
kmphjjmtlpgk web_server.1 httpd:latest
swarm-manager
Running Running 3 minutes ago
如何实现service伸缩
上一节部署了只有一个副本的Service不过对于web服务,我们通常会运行多个实例。这样可以负载均衡,同时也能提供高可用。
swarm要实现这个目标非常简单,增加service的副本数就可以了。在swarm-manager上执行如下命令:
docker service scale web_server=5
[rooteswarm-manager ~]# docker service scale web_server=5 web server 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
副本数增加到5,通过docker servicels和docker serviceps查看副本的详细信息。
[rooteswarm-manager ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
r6blqole12n3 web_server replicated 5/5 httpd:latest
[rootlswarm-manager~1# docker service ps web_serverID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS kmphjjmt1pqk web_server.1 httpd:latest swarm-manager Running Running 43 minutes ago tipv42u0c72y web_server.2 httpd:latest swarm-manager Running Running about a minute ago 2zlyk1i44elq web_server.3 httpd:latest swarm-worker1 Running Running about a minute ago ji4nls51y9sv web_server.4 httpd:latest swarm-worker2 Running Running about a minute ago de79sh71mqm7 web_server.5 httpd:latest swarm-worker2 Running Running about a minute 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
通过docker node ls查看个节点现在的状态:
Drain 表示swarm-manager 已经不负责运行service,之前swarm-manager运行的副本会如何处理呢?用docker service ps查看一下。
[root@swarm-manager ~]# docker service ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sysvltl5miry web_server.1 httpd:latest swarm-worker1 Running Running about a minute ago
kmphjjmt1pqk \_ web_server.1 httpd:latest swarm-manager Shutdown Shutdown about a minute ago
mdgv63jpfst5 web_server.2 httpd:latest swarm-worker1 Running Running about a minute ago
tipv42u0c72y \_ web_server.2 httpd:latest swarm-manager Shutdown Shutdown about a minute ago
2zlyk1i44elq web_server.3 httpd:latest swarm-worker1 Running Running 7 minutes ago
ji4nls51y9sv web_server.4 httpd:latest swarm-worker2 Running Running 7 minutes ago
de79sh71mqm7 web_server.5 httpd:latest swarm-worker2 Running Running 7 minutes ago
swarm-manager上的副本web_server.2已经被shutdown了,为了达到5个副本数的目标,在swarm-worker1上添加了副本web_server=3
[root@swarm-manager ~]# docker service scale web_server=3 web_server 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 ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS sysvltl5miry web_server.1 httpd:latest swarm-worker1 Running Running 2 minutes ago kmphjjmt1pqk \_ web_server.1 httpd:latest swarm-manager Shutdown Shutdown 2 minutes ago mdgv63jpfst5 web_server.2 httpd:latest swarm-worker1 Running Running 2 minutes ago tipv42u0c72y \_ web_server.2 httpd:latest swarm-manager Shutdown Shutdown 2 minutes ago ji4nls51y9sv web_server.4 httpd:latest swarm-worker2 Running Running 9 minutes ago
可以看到,web_server.4和web——server.5这两个副本已经被删除了。
Swarm如何实现Failover
故障是在所难免的 容器可能崩溃,DockerHost可能宕机,不过幸运的是, Swarm已经内置了
failover 策略。
创建service的时候,我们没有告诉swarm发生故障时该如何处理,只是说明了我们期望的状态(比如运行3个副本),swarm会尽最大的努力达成这个期望状态,无论发生什么状况。
以上一节我们部署的Service为例,当前3个副本分布在swarm-worker1和 swarm-worker2 上。现在我们测试swarm的failover特性,关闭swarm-worker1
现在我们测试swarm的failover的特性,关闭swarm-worker1。
Swarm会检测到swarm-worker1的故障,并标记Down。
[root@swarm-manager ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
j6p2rf6fpcw4l6tkn2ps0jji0 * swarm-manager Ready Drain Leader 19.03.2
19.03.2 g6z8cbpxend7tu55asz2q9f3z swarm-worker1 Down Active 19.03.2
19.03.2 raa8bbtyukqqdk3pa0vfvhtwp swarm-worker2 Ready Active 19.03.2
Swarm会将swarm-worker1上的副本调度到其他可用节点。我们可以通过docker service ps观察这个failover过程。
可以看到,web_server.1和web_server.2已经从swarm-worker1迁移到了swarm-worker2,之前运行在故障节点swarm-worker1上的副本状态被标记为Shutdown。