Docker Swarm是官方发布的集群容器管理工具。它的特点是:比较轻量级,无缝支持标准的docker API。深入浅出Swarm一文很清晰地讲解了它的架构和命令。本文从零开始搭建并管理一个swarm集群。
准备工作
我们需要先安装virtualBox和vagrant。通过vagrant来驱动virtualBox搭建一个虚拟测试环境。首先在本地任意路径新建一个空文件夹比如test,运行以下命令:
1 | mkdir test |
里面应该有一句config.vm.box = "minimum/ubuntu-trusty64-docker",在它的下面添加如下几行代码,相当于给它分配三台虚拟机,一台叫做manager,它的IP是192.168.33.17;另两台叫做node1和node2,它们的IP是192.168.33.18和192.168.33.19。
1 | config.vm.define "manager" do | host | |
这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后分别在三个终端运行以下命令启动并连接三台虚拟机。
1 | vagrant up |
1 | vagrant ssh node1 |
1 | vagrant ssh node2 |
搭建环境
想要让swarm管理node,首先得让docker daemon支持TCP。在三台虚拟机上运行以下命令:
1 | sudo sh -c 'echo DOCKER_OPTS=\"-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock\" >> /etc/default/docker' |
接下来,我们用最简单的静态节点列表方式来启动swarm环境。把node1和node2的节点信息都写到参数里即可:
1 | docker run -t -p 2376:2375 swarm:1.1.0 manage nodes://192.168.33.18:2375,192.168.33.19:2375 |
2376是我随便设的一个端口,可以改成2375外的其他可用端口,因为2375已经被docker daemon占用了。可以Ctrl+C后,用下面这个命令查看:
1 | sudo netstat -tulnp | grep 2375 |
随便在哪台机器运行以下命令就能看到这个集群的信息和节点信息。这里的2376就是上面随便设出来的2376:
1 | docker -H tcp://192.168.33.17:2376 info |
运行容器
Swarm的环境已经搭建完成,现在我们可以用swarm来运行容器了。随便在哪台机器运行以下命令来创建一个busybox容器:
1 | docker -H tcp://192.168.33.17:2376 run -d busybox sleep 3000 |
可能一开始的时候有点儿慢,这是因为需要下载镜像的缘故。如果等不及,就到两个node里分别先把镜像下载下来:docker pull busybox。之所以说无缝支持docker API,那是因为swarm里能运行所有的docker命令。跑一下docker -H tcp://192.168.33.17:2376 ps就能看到一个busybox的容器已经启动起来了。容器的NAMES属性里有node的信息,所以不需要分别在两个node运行docker ps就能看到这个busybox的容器在哪个node运行。之后,再运行3次上面的命令,共创建4个busybox的容器,就能看到它们被均匀分配到两个node上了。这是因为swarm默认的调度策略所致。目前swarm支持三种调度策略:
- spread:默认,swarm会把任务分配到目前运行的容器数量最少的node上去。这里说的容器数量包括已经停止的容器。
- binpack:把任务分配到目前最大负荷的node上去。目的是把其他机器的资源留给将来可能要运行的大容器。
- random:随机分配。
如果看到的容器分配不均匀,那很可能是存在着非运行中的容器,可以用docker ps -a看一下。如果想要修改调度策略,可以在manager启动的时候指定--strategy参数,比如修改成binpack:
1 | docker rm -f `docker ps -aq` |
这回再试试启动4个新的busybox,是不是都跑到同一个node上去了?Swarm的过滤器功能还允许我们指定让容器运行在哪个node上。目前,swarm支持如下的过滤器:
node过滤器
constraint:限制新任务只能在label符合的node上执行health:限制新任务只能在“健康”的node上执行
容器过滤器
affinity:使新任务在已运行某个名字或label的容器,或者有某个镜像的node上执行dependency:使新任务在有依赖(–volumes-from=dependency、–link=dependency:alias或–net=container:dependency)的node上执行
port:使新任务在某个端口可用的node上执行
可以在manager启动的时候指定--filter参数来启用过滤器功能。我们先来试验一下constraint。由于Label是docker daemon的属性,所以我们又要修改/etc/default/docker并重启docker daemon。假设node1为ssd,node2为普通disk:
1 | docker rm -f `docker ps -aq` |
1 | docker rm -f `docker ps -aq` |
随便在哪台机器运行以下命令,看看两个node是不是分别多出来storage==ssd和storage==disk:
1 | docker -H tcp://192.168.33.17:2376 info |
如果不是,就重启一下manager的swarm容器。然后就可以指定storage=ssd的node运行:
1 | docker -H tcp://192.168.33.17:2376 run -d -e constraint:storage==ssd busybox sleep 3000 |
随便再创建几个容器玩玩,再换constraint:storage==disk试试看。熟悉之后,我们再试验一下affinity:
1 | docker -H tcp://192.168.33.17:2376 run -d -e constraint:storage==disk --name=bb busybox sleep 3000 |
要的就是bb这个名字。然后运行以下命令让新容器运行在有bb容器的node上,也就是node2:
1 | docker -H tcp://192.168.33.17:2376 run -d -e affinity:container==bb busybox sleep 3000 |
在node2上运行docker ps应该能看到新容器已经启动起来了。如果constraint和affinity冲突会怎样呢?试试看:
1 | docker -H tcp://192.168.33.17:2376 run -d -e affinity:container==bb -e constraint:storage==ssd busybox sleep 3000 |
不出意外的话,应该能看见unable to find a node that satisfies storage==ssd的错误消息了吧。
主机发现
Swarm共支持下面几种主机发现方式:
分布式键值存储
Consul 0.5.1或更高版本Etcd 2.0或更高版本
ZooKeeper 3.4.5或更高版本
静态方式
文件节点列表
Docker Hub
我们刚才搭建环境用到的是静态的节点列表方式,现在我们再试试其它几种方式。
文件
先从简单的开始。文件的主机发现方式和节点列表很类似,它们都是静态的。首先把node的信息都写到一个临时文件/tmp/cluster里,然后让swamp容器管理这个文件即可:
1 | echo 192.168.33.[18:19]:2375 > /tmp/cluster |
随便选台虚拟机检查一下:
1 | docker -H tcp://192.168.33.17:2376 info |
是不是有换汤不换药的感觉?
ZooKeeper
接下来试验一下ZooKeeper。先在manager上启动一个ZooKeeper的服务:
1 | docker run -d \ |
然后运行swarm manager:
1 | docker run -t -p 2376:2375 swarm:1.1.0 manage zk://192.168.33.17:2181/swarm |
由于这回不像文件和节点列表方式那样静态,我们需要把两个node加入到集群里:
1 | docker run -d swarm:1.1.0 join --addr=192.168.33.18:2375 zk://192.168.33.17:2181/swarm |
1 | docker run -d swarm:1.1.0 join --addr=192.168.33.19:2375 zk://192.168.33.17:2181/swarm |
随便选台虚拟机检查一下:
1 | docker -H tcp://192.168.33.17:2376 info # 由于是动态加载,可能需要等待半分钟左右才能看见node |
Consul和Etcd也都很类似,这里就不一一列举了。
Docker Hub
Docker Hub的主机发现方式,就是在docker hub上使用发现服务来生成一个唯一的集群ID(别在生产环境上这么干!)。Docker hub会为我们保留大概一个星期。在任意一台机器上运行以下命令:
1 | docker run --rm swarm:1.1.0 create |
然后我们就能看见上面的命令生成了一个字符串,这就是我们的集群ID。在我的机器上是这样的:3137ebf83d771f1db06bf4eab7ccc73b。我大天朝的网络,有时候会出现TLS handshake timeout,那就再运行一次吧。
这时候可以把manager启动起来了,别忘了替换成你自己的token:
1 | docker run -t -p 2376:2375 swarm:1.1.0 manage token://3137ebf83d771f1db06bf4eab7ccc73b |
然后可以在两个node上分别运行以下命令,启动swarm的代理并加入到集群中,别忘了替换成你自己的token:
1 | docker run -d swarm:1.1.0 join --addr=192.168.33.18:2375 token://3137ebf83d771f1db06bf4eab7ccc73b |
1 | docker run -d swarm:1.1.0 join --addr=192.168.33.19:2375 token://3137ebf83d771f1db06bf4eab7ccc73b |
随便选台虚拟机检查一下:
1 | docker -H tcp://192.168.33.17:2376 info # 可能比较慢,下面那条命令更快 |