今天摸索了一下docker的swarm集群管理技术。摸索的过程中走了两次弯路。
第一次弯路:
玩集群首先得虚拟出几台服务器,vmware上的ubuntu桌面版作为工作和开发环境,可以配置为docker主控节点。还需要再虚拟两台从节点服务器,看swarm的教程都是用的virtualbox跑boot2docker进行测试,尝试了virtualbox后发现virtualbox和vmware并存时,因为所有虚拟节点都需要独立IP,所以网络都设置成桥接模式,virtualbox内的虚拟系统可以和宿主机互相ping通,vmware内的系统也可以和宿主机互相ping通,但virtualbox内的虚拟系统和vmware内的虚拟系统之间不能ping通,这就没法使用vmware中原先已有的ubuntu桌面版作为docker master节点去管理virtualbox中的docker slave节点了。折腾了半天,此路不通。
第二次大弯路:
权衡利弊后决定放弃virtualbox。vmware和virtualbox功能相似,跑boot2docker也没问题。自行下载一个docker2boot.iso,只有不到50M,vmware新建一个虚拟机,将这个ISO文件挂载到光驱后启动,boot2docker的欢迎界面很快就出现了。麻雀虽小五脏俱全,以后跑docker再也不用安装一个完整版的Ubuntu。用boot2docker创建两个docker虚拟机,分分钟的事情,加上桌面版的ubuntu,一主二仆的swarm测试环境就搭好了。
直到这里,进展都很顺利,对发现这个轻量版的boot2docker也很满意,以后的集群部署,从节点都可以用boot2docker部署了。docker2boot每次都是从光盘ISO加载引导,文件系统也都虚拟在内存上,这意味着其运行效率非常高,虽然对系统内文件的任何修改在重启后就会被复位,但这似乎并不影响其被用作swarm的从节点。然而正是这一点,限制了我对自动化部署的追求,因为有些配置文件的确需要在重启后依然有效,甚至docker修改registry后需要重启刷新也成为奢望。其原因将在后面讲到用registry部署私有镜像库时谈到。折腾了半天,此路不是很通畅,只能老老实实又安装了两个ubuntu20.04 server版,server版没有图形化界面,操作起来有诸多不便,bash也不支持复制粘贴和鼠标滚动,但可以通过windows下的cmd建立ssh连接,在cmd窗口下操作就方便多了。
康庄大道:
好了,正式进入swarm主题,包括创建集群和管理节点、配置私有库、部署服务。
一、创建集群和管理节点
- 初始化集群:docker swarm init --advertise-addr 192.168.31.111
- 查看集群中节点:docker node ls
- 查看从节点加入指令:docker swarm join-token worker
- 查看管理节点加入指令:docker swarm join-token manager
- 移除一个节点:docker node rm ID|HOSTNAME
- 主动退出集群:docker swarm leave
执行初始化集群指令时,--advertise-addr参数配置的IP地址就是执行这条指令的主控节点的IP。这条指令会输出一些信息,其中有一行类似这样的内容:
docker swarm join --token SWMTKN-1-20ym67akkte6qhnbo0omhgyhmr75kko2m3jpek2am695dex91l-2myld05a4o7iwt3rjg3gae0h6 192.168.31.111:2377
复制这行,并粘贴到另外两个作为从节点的ubuntu系统中去执行。服务器版ubuntu没法在bash里粘贴,可以用windows cmd ssh连接后操作。
待两个从节点都加入集群后,可在主控节点通过docker node ls指令查看当前集群中的节点,可以看到连同自己和刚加进来的,共有三个节点,自己属于管理节点,且为leader。
swarm的管理节点可以有多个,官方建议是奇数个,推测可能是采用了类似纠删码的技术,只要这些管理节点中有超过一半的节点未宕机,系统仍能正常运行。刚才输入的那段加入集群的指令是给从节点使用,若想以管理节点的身份加入,则需要在现有的管理节点上输入docker swarm join-token manager,将会返回一串加入指令,复制到相应节点执行就可以加入管理层。leader是从管理层中按照一定算法推选出来的。使用docker node ls看一下,目前有一个节点的manager status是leader,刚加进来的那个是reachable,我想储君的英文翻译可能就是reachable吧。
节点退出集群有两种方法,第一种比较常用,是在管理节点上执行docker node rm ID|HOSTNAME,ID或者HOSTNAME可以从节点列表中查到,如果存在多个节点有相同的HOSTNAME,则删除会报歧义错误,只能通过ID删除。第二种是节点主动退出,对于从节点,只能执行swarm的两条指令,即加入指令和退出指令docker swarm leave。
二、配置私有库
- 拉取registry镜像:docker pull registry:latest
- 启动私有库:docker run -d -v /home/registry:/var/lib/registry -p 5000:5000 --restart=always --name registry registry:latest
- 修改docker配置文件daemon.json: 将私有库IP添加到insecure-registries
- 修改本地镜像名和tag:docker tag 192.168.31.111:5000/镜像名:标签
- 向私有库上传镜像:docker push 192.168.31.111:5000/镜像名:标签
- 从私有库拉取镜像:docker pull 192.168.31.111:5000/镜像名:标签
*192.168.31.111:5000是我的registry容器部署的网址
swam集群中如果管理节点使用一个本地镜像创建服务,在给子节点分配任务时,子节点就无法从公共docker hub上拉取到这个自建的镜像。此时就需要配置本地私有仓库。我们可以通过registry镜像来构建自己的私有仓库。
首先在管理节点上安装私有库:docker pull registry:latest
启动该私有库:docker run -d -v /home/registry:/var/lib/registry -p 5000:5000 --restart=always --name registry registry:latest
在这段指令中,-d代表以后台进程启动; -v /home/registry:/var/lib/registry表示将本地/home/registry挂载到registry容器的/var/lib/registry上,这样挂载后上传到registry的镜像文件就保存在本地/home/registry目录中;-p 5000:5000是端口映射,registry默认启用5000端口。
私有库启动后就可以向其中push镜像了。但push镜像使用的是ssl连接,由于并没有给registry配置密钥,所以直接push会报错。可以修改docker的配置文件/etc/docker/daemon.json,添加下面的insecure-registries内容,使得可以通过非加密连接该registry服务。修改daemon.json配置文件后记得要sudo service docker restart。
{
"registry-mirrors": [ ...],
"insecure-registries": ["192.168.31.111:5000"]
}
其它的所有slave节点也都需要修改该配置文件,并重启docker服务。这样,管理节点将本地镜像上传到该私有registry库中,在启动服务时,slave节点才能从私有registry库中拉取到镜像。
刚才说的第二个弯路就是在这里被卡住的。如果是boot2docker,修改daemon.json文件后就没法重启docker,boot2docker将service、systemctl、apt、yum这类指令都裁剪掉了(难怪只有50M不到),docker没法软重启,只能重启boot2docker系统,但这个内存系统重启后一切又复位了,这是个悖论。只有在不断折腾的过程中才能逐渐找到大方向。
三、部署服务
- 查看集群中已有服务:docker service ls
- 启动一个服务:docker service create --replicas 3 --name 服务名 -p 81:80 192.168.31.111:5000/镜像名:标签
- 查看服务状态:docker service ps 服务名
- 服务更新镜像:docker service update --image 镜像路径 服务名
- 服务增加|删除端口映射:docker service update --publish-add|rm 80:80 服务名
- 服务回滚到上一次更新前的状态:docker service update --rollback 服务名
- 服务弹性伸缩:docker service scale 服务名=10
- 删除服务:docker service rm 服务名
集群建好了,私有镜像库搭起来了,各节点镜像库源配置好了,万事俱备,就等在上面跑服务了。用任何一个管理节点,使用docker service create指令建立服务,其中参数--replicas 3表示扩展为三个并行服务,--name给服务起名字,-p进行端口隐射,后面跟的镜像名是刚才上传到registry的镜像。
可以用docker service ls看一下,集群中已经跑起了一个服务。想看一看这个服务分布在哪些节点上,可用docker service ps 服务名查一下。如果服务做了修改,修改后的镜像也已经上传到registry上了,则可以通过docker service update --iimage 升级后的镜像名 服务名进行升级。若发现错误,可以通过docker service update --rollback 服务名进行回滚。服务还可以根据需求进一步做弹性伸缩,指令是docker service scale 服务名=数目。最后还可以通过docker service rm 服务名回收集群资源。
对于分布在各子节点的服务,往往需要共享一个公共的存储,可以通过NFS挂载网络磁盘的方式实现,去docker hub官网搜一下NFS,惊喜的发现这个功能也有现成的镜像。再进一步的,随着业务的扩张,可以逐步实施更多的分布式系统架构。比如分布式部署服务还需要通过DNS轮询来实现负载均衡。所幸的是Docker也已经提供了现成的DNS镜像,很容易部署出一个DNS轮询系统。这一切都要感谢容器化部署,减轻了开发的重复工作,也大大降低了运维的复杂度,让一切变得像搭积木一样容易。