Docker 集群自建方案
前言
嗯,这其实是一篇弃坑文。原先打算针对小白用户搞一系列博文,由浅入深地讲解如何玩转 Docker,但考虑到时间和精力实在有限,放弃了这个念头。当然也不排除,后面有时间和精力的时候,会用 readthedocs 再实现这个想法。
不过,本文也不能一点干货都没有,思考了一番,决定把自己自建 Docker 集群的一些经历分享一下,可能对后人会有点帮助。
自建集群目的
比较清楚地认识到,自己是一个爱折腾的家伙,目前在管理的网站及非网站的应用(包括线上环境与测试环境)起码有 15 个,而且总会有一些新的想法冒出来要去尝试。在自建集群之前,由于不同应用的依赖环境千差万别,每添加一个应用,不得不考虑主机上的现有环境和已经在跑的服务,而且部署和测试也是比较繁琐,没有办法满足我快速尝试新点子的需求。
而 Docker 恰好可以解决应用部署的环境问题,自建 Docker 集群可以充分利用我手上的闲置 VPS,并且提高应用的可用性。
硬件资源
自建集群有 8 台 VPS,其中 4 台性能比较好的 Vultr 中低配小鸡,一台腾讯云低配 VPS,一台搬瓦工 CN2 小鸡,一台 CloudCone 低配小鸡,还有一台朋友送的 VirMach 小鸡。手上还有几台谷歌云服务器,不过考虑到用不长久,而且流量贵,就没在自建集群的考虑范围内。
[root@ctrl-bwh-01 scripts]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
q0kllzk1ezr9h449cjatak17z GLBS-CC-01 Ready Active 18.09.5
ioacz1khl94m01wul1z70qatu * ctrl-bwh-01 Ready Active Reachable 18.09.5
1sj867qzlrgdp72705pcnvleb db-vultr-01 Ready Active 18.09.5
dua8lh9h6yt5b58jwwn5vtlu6 db-vultr-02 Ready Active 18.09.5
rk4newk0r7z51lvgjil564bar web-qq-01 Ready Active 18.09.5
xaq6d5n4kzcxj7phnvc32xw21 web-virmach-01 Ready Active 18.09.5
1e2a4scv0eskgq0og3xqpedco web-vultr-01 Ready Active Reachable 18.09.5
5jw8pe7vkzr2iwo3m1rrr1ef5 web-vultr-02 Ready Active Leader 18.09.5
集群方案
说到 Docker 集群,一般都会提到鼎鼎有名的 k8s。而我觉得 k8s 太重了,我的众多低配 VPS 跑 k8s 之后,能分配给应用的资源就少得可怜,所以 k8s 是直接就被我 pass 掉的。虽然也有 k3s 之类的轻量级 k8s 解决方案,不过我还是选择了原生的 docker swarm。VPS 安装好 Docker 之后,不需要额外安装软件,就可以马上建立集群。
# 集群初始化,节点成为 manager 节点
docker swarm init --advertise-addr=x.x.x.x
# 集群丢失 Leader 时,强制重建集群
docker swarm init --advertise-addr=x.x.x.x --force-new-cluster
# 获取作为 worker 节点加入集群的命令
docker swarm join-token worker
# 获取作为 manager 节点加入集群的命令
docker swarm join-token manager
# 加入集群
docker swarm join --token xxx x.x.x.x:xxx --advertise-addr=x.x.x.x
--advertise-addr
,该参数非必填。不过根据个人经验来看,还是强烈建议显式指定该参数,尤其当 VPS 有多个网卡时。统一的服务入口
在集群内部通过docker service create xxx
的命令创建服务之后,如果有设置对外暴露端口,那么可以向集群中任意一台 VPS 的指定端口请求服务。手上的应用还是 Web 应用居多,而它们都需要 80 或 443 端口,为了让它们都能正常提供服务,集群需要一个统一的前端应用提供负载均衡服务,根据一定的规则(比如域名)转发给后端应用。
虽然 nginx 也可以比较方便地实现负载均衡,但是我此处选用的是相对专业的、功能更完善的 traefik。traefik 提供服务自动发现、HTTPS 证书自动生成、服务监测指标数据 等功能,感兴趣的同学可以前往 官网 了解详情。


集群管理面板
虽然可以登录到 manager 节点,敲命令行管理集群节点、服务,但还是稍麻烦些,而且不太希望所有维护人员都有权限直接操作机器。可以管理 Docker Swarm 集群管理面板也不少,能入法眼的就 Rancher 和 Portainer,然而由于 Rancher 对宿主机配置要求比较高,消耗资源较多,我最终选择了轻量级的 Portainer。
Portainer 官网提供了比较多的管理方式,踩了比较多的坑之后,我采用的是 agent + portainer 这种方式。以 global 模式在每个节点部署 agent 服务,portainer 部署时需要指定连接 agent 服务。欲知详情,请看 官方文档。

当然,Portainer 也是有不完善的地方,比如查看集群服务时,会偶尔出现「无法连接」的报错,而且不能连接数据库,所以部署 Portainer 服务时,要限制它固定在某台宿主机上。不然,会出现频繁设置密码等现象。
监控和告警系统
traefik 提供的管理面板是非常简单的,仅能查看一些基础数据,比如某个服务有多少后端,整体的服务状态。为了能够定制化监控集群中的服务,并且在需要的时候触发告警,让物理人介入进行维护,需要一个监控和告警系统。而 Grafana 恰好可以满足需求,配合 Prometheus 以及 Prometheus 相关的 Exporter,一个五脏俱全的监控告警系统呼之欲出。
因为腾讯云主机的带宽极小、性能又比较一般,提供对外服务不太合适,为了充分利用资源,我便把监控和告警系统相关的大多服务都部署在上面。本来还想搞一个 ES 在腾讯上,无奈配置太低,只好作罢。
有了监控面板,可以清楚地知道具体服务的服务状态和服务数据规律,下图中的柱状图表示了以 5 分钟为统计周期的「每秒处理请求数」。注:该面板的标题
Total requests over
容易产生误解。

原服务改造
原有服务都是单机部署,需要改造并打包 Docker 镜像。改造难点其实在于持久化数据的存储与读写,比如 Web 应用的 Session 存储、图片存储、附件存储等。对于 Session 存储,可以搭建中心化的数据中心解决,或者是改用 token 方式进行登录验证。对于图片存储、附件存储,我的改造方案是,将全量数据存放在 BackBlaze 云存储中,每个应用按需从云存储中心拉取数据,并定期删除冷门数据。感兴趣的可以看看我另外一篇博文:开源图床 ImageX 介绍。
注意事项
Docker 不稳定
通过实践,我发现 Docker 还是挺容易挂的,尤其是长时间跑高之后。为了保证 Docker 服务的持续运行,除了要让 Docker 开机自启动之外,还需要对 Docker 服务进行监控,一旦发现服务挂了就马上重启服务。可以通过一条简单的 crontab 定时任务解决:
# 适用于 CentOS 7,如果 Docker 正在服务,不会产生负面影响
* * * * * systemctl start docker
定期清理
时间长了,宿主机会有很多不需要的镜像、停止的容器等,如果有需要,同样可以通过定时任务进行清理。# 每天凌晨 2 点清理容器和镜像
0 2 * * * docker container prune --force && docker image prune --force
# 更凶残地方式
0 2 * * * docker system prune --force
实现细节与踩坑
本文仅大概介绍了集群建设的整体思路和实现方式,实际上要真正搭建和运行起来,踩坑是避免不了的。如果读者有兴趣,我会抽时间说明一些实现细节。来源:https://zhuanlan.zhihu.com/p/64528812
评论