Swarm 在 Docker 1.12 版本之前属于一个独立的项目,在 Docker 1.12 版本发布之后,该项目合并到了 Docker 中,成为 Docker 的一个子命令。目前,Swarm 是 Docker 社区提供的唯一一个原生支持 Docker 集群管理的工具。它可以把多个 Docker 主机组成的系统转换为单一的虚拟 Docker 主机,使得容器可以组成跨主机的子网网络。
Swarm 认识
Swarm 是目前 Docker 官方唯一指定(绑定)的集群管理工具。Docker 1.12 内嵌了 swarm mode 集群管理模式。
为了方便演示跨主机网络,我们需要用到一个工具——Docker Machine,这个工具与 Docker Compose、Docker Swarm 并称 Docker 三剑客,下面我们来看看如何安装 Docker Machine:
1 | $ curl -L https://github.com/docker/machine/releases/download/v0.9.0-rc2/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine && |
安装过程和 Docker Compose 非常类似。现在 Docker 三剑客已经全部到齐了。
在开始之前,我们需要了解一些基本概念,有关集群的 Docker 命令如下:
- docker swarm:集群管理,子命令有 init, join,join-token, leave, update
- docker node:节点管理,子命令有 demote, inspect,ls, promote, rm, ps, update
- docker service:服务管理,子命令有 create, inspect, ps, ls ,rm , scale, update
- docker stack/deploy:试验特性,用于多应用部署,等正式版加进来再说。
创建集群
首先使用 Docker Machine 创建一个虚拟机作为 manger 节点。
1 | $ docker-machine create --driver virtualbox manager1 |
查看虚拟机的环境变量等信息,包括虚拟机的 IP 地址:
1 | $ docker-machine env manager1 |
然后再创建一个节点作为 work 节点。
1 | $ docker-machine create --driver virtualbox worker1 |
现在我们有了两个虚拟主机,使用 Machine 的命令可以查看:
1 | $ docker-machine ls |
但是目前这两台虚拟主机并没有什么联系,为了把它们联系起来,我们需要 Swarm 登场了。
因为我们使用的是 Docker Machine 创建的虚拟机,因此可以使用 docker-machine ssh 命令来操作虚拟机,在实际生产环境中,并不需要像下面那样操作,只需要执行 docker swarm 即可。
把 manager1 加入集群:
1 | $ docker-machine ssh manager1 docker swarm init --listen-addr 192.168.99.100:2377 --advertise-addr 192.168.99.100 |
用 –listen-addr 指定监听的 ip 与端口,实际的 Swarm 命令格式如下,本例使用 Docker Machine 来连接虚拟机而已:
1 | $ docker swarm init --listen-addr <MANAGER-IP>:<PORT> |
接下来,再把 work1 加入集群中:
1 | $ docker-machine ssh worker1 docker swarm join --token \ |
上面 join 命令中可以添加 –listen-addr $WORKER1_IP:2377 作为监听准备,因为有时候可能会遇到把一个 work 节点提升为 manger 节点的可能,当然本例子没有这个打算就不添加这个参数了。
注意:如果你在新建集群时遇到双网卡情况,可以指定使用哪个 IP,例如上面的例子会有可能遇到下面的错误。
1 | $ docker-machine ssh manager1 docker swarm init --listen-addr $MANAGER1_IP:2377 |
发生错误的原因是因为有两个 IP 地址,而 Swarm 不知道用户想使用哪个,因此要指定 IP。
1 | $ docker-machine ssh manager1 docker swarm init --advertise-addr 192.168.99.100 --listen-addr 192.168.99.100:2377 |
集群初始化成功。
现在我们新建了一个有两个节点的“集群”,现在进入其中一个管理节点使用 docker node 命令来查看节点信息:
1 | $ docker-machine ssh manager1 docker node ls |
现在每个节点都归属于 Swarm,并都处在了待机状态。Manager1 是领导者,work1 是工人。
现在,我们继续新建虚拟机 manger2、worker2、worker3,现在已经有五个虚拟机了,使用 docker-machine ls 来查看虚拟机:
1 | NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS |
然后我们把剩余的虚拟机也加到集群中。
- 添加 worker2 到集群中:
1 | $ docker-machine ssh worker2 docker swarm join \ |
- 添加 worker3 到集群中:
1 | $ docker-machine ssh worker3 docker swarm join \ |
- 添加 manager2 到集群中:
先从 manager1 中获取 manager 的 token:
1 | $ docker-machine ssh manager1 docker swarm join-token manager |
然后添加 manager2 到集群中:
1 | $ docker-machine ssh manager2 docker swarm join \ |
现在再来查看集群信息:
1 | $ docker-machine ssh manager2 docker node ls |
建立跨主机网络
为了演示更清晰,下面我们把宿主机也加入到集群之中,这样我们使用 Docker 命令操作会清晰很多。
直接在本地执行加入集群命令:
1 | $ docker swarm join \ |
现在我们有三台 manager,三台 worker。其中一台是宿主机,五台虚拟机。
1 | $ docker node ls |
查看网络状态:
1 | $ docker network ls |
可以看到在 swarm 上默认已有一个名为 ingress 的 overlay 网络, 默认在 swarm 里使用,本例子中会创建一个新的 overlay 网络。
1 | $ docker network create --driver overlay swarm_test |
这样一个跨主机网络就搭建好了,但是现在这个网络只是处于待机状态,下一小节我们会在这个网络上部署应用。
在跨主机网络上部署应用
首先我们上面创建的节点都是没有镜像的,因此我们要逐一 pull 镜像到节点中,这里我们使用前面搭建的私有仓库。
1 | $ docker-machine ssh manager1 docker pull reg.example.com/library/nginx:alpine |
上面使用 docker pull 分别在五个虚拟机节点拉取 nginx:alpine 镜像。接下来我们要在五个节点部署一组 Nginx 服务。
部署的服务使用 swarm_test 跨主机网络。
1 | $ docker service create --replicas 2 --name helloworld --network=swarm_test nginx:alpine |
查看服务状态:
1 | $ docker service ls |
查看 helloworld 服务详情(为了方便阅读,已调整输出内容):
1 | $ docker service ps helloworld |
可以看到两个实例分别运行在两个节点上。
进入两个节点,查看服务状态(为了方便阅读,已调整输出内容):
1 | $ docker-machine ssh manager1 docker ps -a |
上面输出做了调整,实际的 NAMES 值为:
1 | helloworld.1.ay081uome3eejeg4mspa8pdlx |
记住上面这两个实例的名称。现在我们来看这两个跨主机的容器是否能互通:
首先使用 Machine 进入 manager1 节点,然后使用 docker exec -i 命令进入 helloworld.1 容器中 ping 运行在 worker2 节点的 helloworld.2 容器。
1 | $ docker-machine ssh manager1 docker exec -i helloworld.1.ay081uome3eejeg4mspa8pdlx \ |
然后使用 Machine 进入 worker2 节点,然后使用 docker exec -i 命令进入 helloworld.2 容器中 ping 运行在 manager1 节点的 helloworld.1 容器。
1 | $ docker-machine ssh worker2 docker exec -i helloworld.2.16cvore0c96rby1vp0sny3mvt \ |
可以看到这两个跨主机的服务集群里面各个容器是可以互相连接的。
为了体现 Swarm 集群的优势,我们可以使用虚拟机的 ping 命令来测试对方虚拟机内的容器。
1 | $ docker-machine ssh worker2 ping helloworld.1.ay081uome3eejeg4mspa8pdlx |
上面我们使用了虚拟机内部的 ping 去测试容器的延迟,可以看到延迟明显比集群内部的 ping 值要高。
Swarm 集群负载
现在我们已经学会了 Swarm 集群的部署方法,现在来搭建一个可访问的 Nginx 集群吧。体验最新版的 Swarm 所提供的自动服务发现与集群负载功能。
首先删掉上一节我们启动的 helloworld 服务:
1 | $ docker service rm helloworld |
然后在新建一个服务,提供端口映射参数,使得外界可以访问这些 Nginx 服务:
1 | $ docker service create --replicas 2 --name helloworld -p 7080:80 --network=swarm_test nginx:alpine |
查看服务运行状态:
1 | $ docker service ls |
不知你有没有发现,虽然我们使用 –replicas 参数的值都是一样的,但是上一节中获取服务状态时,REPLICAS 返回的是 0/2,现在的 REPLICAS 返回的是 2/2。
同样使用 docker service ps 查看服务详细状态时(下面输出已经手动调整为更易读的格式),可以看到实例的 CURRENT STATE 中是 Running 状态的,而上一节中的 CURRENT STATE 中全部是处于 Preparing 状态。
1 | $ docker service ps helloworld |
这就涉及到 Swarm 内置的发现机制了,目前 Docker 1.12 中 Swarm 已经内置了服务发现工具,我们不再需要像以前使用 Etcd 或者 Consul 这些工具来配置服务发现。对于一个容器来说如果没有外部通信但又是运行中的状态会被服务发现工具认为是 Preparing 状态,本小节例子中因为映射了端口,因此有了 Running 状态。
现在我们来看 Swarm 另一个有趣的功能,当我们杀死其中一个节点时,会发生什么。
首先 kill 掉 worker2 的实例:
1 | $ docker-machine ssh worker2 docker kill helloworld.2.7acmhj0udzusv1d7lu2tbuhu4 |
稍等几秒,再来看服务状态:
1 | $ docker service ps helloworld |
可以看到即使我们 kill 掉其中一个实例,Swarm 也会迅速把停止的容器撤下来,同时在节点中启动一个新的实例顶上来。这样服务依旧还是两个实例在运行。
此时如果你想添加更多实例可以使用 scale 命令:
1 | $ docker service scale helloworld=3 |
查看服务详情,可以看到有三个实例启动了:
1 | $ docker service ps helloworld |
现在如果想减少实例数量,一样可以使用 scale 命令:
1 | $ docker service scale helloworld=2 |
至此,Swarm的主要用法都已经介绍完了,主要讲述了 Swarm 集群网络的创建与部署。介绍了 Swarm 的常规应用,包括 Swarm 的服务发现、负载均衡等,然后使用 Swarm 来配置跨主机容器网络,并在上面部署应用。