Docker Swarm 搞定高可用集群,生产环境再也不怕服务挂掉了
Docker Swarm是什么Docker Swarm 是 Docker 官方推出的容器集群管理工具基于 Go 语言实现。代码开源在https://github.com/docker/swarm 使用它可以将多个 Docker 主机封装为单个大型的虚拟 Docker 主机快速打造一套容器云平台。Docker Swarm 是生产环境中运行 Docker 应用程序最简单的方法。作为容器集群管理器Swarm 最大的优势之一就是 100% 支持标准的 Docker API。各种基于标准 API 的工具比如 Compose、docker-py、各种管理软件甚至 Docker 本身等都可以很容易的与 Swarm 进行集成。大大方便了用户将原先基于单节点的系统移植到 Swarm 上同时 Swarm 内置了对 Docker 网络插件的支持用户可以很容易地部署跨主机的容器集群服务。Docker Swarm 和 Docker Compose 一样都是 Docker 官方容器编排工具但不同的是Docker Compose是一个在单个服务器或主机上创建多个容器的工具而Docker Swarm则可以在多个服务器或主机上创建容器集群服务对于微服务的部署显然 Docker Swarm 会更加适合。为什么选 Docker Swarm 而不是 K8s说实话Kubernetes 确实强大但对我们这种中小团队来说K8s 的学习成本和维护成本都太高了。光是搭建一套高可用的 K8s 集群就得折腾好几天更别说日常维护了。Docker Swarm 就不一样了它是 Docker 原生自带的编排工具学过 Docker 的基本上一个小时就能上手。而且功能足够强大服务发现、负载均衡、滚动更新、健康检查这些该有的都有完全能满足大部分生产场景的需求。客户现在的业务规模大概有 30 多个微服务每天访问量在百万级别用 Docker Swarm 完全够用。如果你的规模跟我差不多真心建议别一上来就搞 K8sSwarm 真的够用了。环境准备5 台服务器配置如下3 台 Manager 节点16G 内存4 核 CPU2 台 Worker 节点8G 内存2 核 CPU为什么 Manager 要 3 台这个后面会详细说。系统用的是 CentOS 7.9Docker 版本是 20.10.14。在开始之前有几件事得先做好1. 主机名和 hosts 配置每台机器都要设置不同的主机名不然等会儿集群建起来都叫 localhost看着就头疼。# 在 manager1 上执行hostnamectl set-hostname manager1# 其他机器类似改成 manager2、manager3、worker1、worker2然后修改 /etc/hosts 文件把所有节点的 IP 和主机名都写进去192.168.1.101 manager1 192.168.1.102 manager2 192.168.1.103 manager3 192.168.1.104 worker1 192.168.1.105 worker22. 关闭防火墙或者开放端口Docker Swarm 需要几个端口2377/tcp集群管理通信7946/tcpudp节点间通信4789/udp覆盖网络流量生产环境建议还是开防火墙只开放这些端口。测试环境图省事直接关了也行systemctl stop firewalld systemctl disable firewalld3. 时间同步这个特别重要如果节点时间不同步集群选举会出问题服务调度也会乱套。我们用 chrony 做时间同步yuminstall-ychrony systemctl start chronyd systemctlenablechronyd初始化 Swarm 集群准备工作做完了现在开始正式搭建集群。在 manager1 上执行初始化命令dockerswarm init --advertise-addr192.168.1.101执行完会输出一串命令大概是这样的Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssm7h7g6w7br5d7vwlh 192.168.1.101:2377 To add a manager to this swarm, run docker swarm join-token manager and follow the instructions.这个 token 特别重要一定要保存好后面添加节点都要用它。现在集群已经有一个 Manager 节点了查看一下集群状态dockernodels输出类似这样ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION dxn1zf6l61qsb1josjja83ngz * manager1 Ready Active Leader 20.10.14能看到 manager1 是 Leader 状态。添加其他 Manager 节点前面说了要用 3 个 Manager 节点为什么要奇数个这个跟 Raft 共识算法有关。Docker Swarm 用 Raft 算法来管理集群状态需要半数以上的 Manager 节点在线才能正常工作。如果你有 2 个 Manager挂掉 1 个剩下 1 个不满足半数以上的条件集群就不可用了。但如果是 3 个 Manager挂掉 1 个剩下 2 个还能正常工作。所以奇数个 Manager 是最佳实践一般 3 个就够用了能容忍 1 个节点故障。如果想要更高的容错能力可以用 5 个能容忍 2 个节点故障。[2]在 manager1 上获取 Manager 加入令牌dockerswarm join-token manager会输出一个命令复制到 manager2 和 manager3 上执行dockerswarmjoin--tokenSWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssm7h7g6w7br5d7vwlh192.168.1.101:2377执行完后再看集群状态dockernodelsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION dxn1zf6l61qsb1josjja83ngz * manager1 Ready Active Leader 20.10.14 rfm5vi4vpxpmhjw6vz0t4wqdv manager2 Ready Active Reachable 20.10.14 y6jcg5z4m7x0tz1f0qz3n5k8s manager3 Ready Active Reachable 20.10.14看到 3 个 Manager 都在线了manager1 是 Leader其他两个是 Reachable 状态。添加 Worker 节点Worker 节点只负责运行容器不参与集群管理。在 manager1 上获取 Worker 加入令牌dockerswarm join-token worker复制输出的命令到 worker1 和 worker2 上执行dockerswarmjoin--tokenSWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssm7h7g6w7br5d7vwlh192.168.1.101:2377再看集群状态dockernodelsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION dxn1zf6l61qsb1josjja83ngz * manager1 Ready Active Leader 20.10.14 rfm5vi4vpxpmhjw6vz0t4wqdv manager2 Ready Active Reachable 20.10.14 y6jcg5z4m7x0tz1f0qz3n5k8s manager3 Ready Active Reachable 20.10.14 n8xkp2z3m5q1rt7v9w2j4h6c0 worker1 Ready Active 20.10.14 t3h7m9k2p8q5wv1n4x6j0r2b8 worker2 Ready Active 20.10.145 个节点的集群就搭建完成了。创建覆盖网络集群搭好了但服务之间怎么通信呢这就需要创建覆盖网络Overlay Network。覆盖网络允许不同节点上的容器互相通信就像在同一个局域网里一样。dockernetwork create--driveroverlay--attachablemy-overlay--attachable参数很重要它允许独立容器连接到这个网络。如果不加这个参数只有 Swarm 服务才能用这个网络。查看网络dockernetworklsNETWORK ID NAME DRIVER SCOPE n33wq0p5m6q7 ingress overlay swarm r2t5v8x1p4m9 my-overlay overlay swarm部署一个测试服务光搭集群没意思咱们部署一个实际的服务试试。我这里用 Nginx 做演示部署 3 个副本dockerservicecreate\--namenginx-test\--replicas3\--networkmy-overlay\--publish80:80\nginx:latest参数解释一下--name服务名称--replicas副本数量这里启动 3 个容器--network使用的网络--publish端口映射把容器的 80 端口映射到宿主机的 80 端口查看服务状态dockerservicelsID NAME MODE REPLICAS IMAGE PORTS k8z2m9p4n7q1 nginx-test replicated 3/3 nginx:latest *:80-80/tcpREPLICAS 显示 3/3说明 3 个副本都正常运行。查看服务详情dockerservicepsnginx-testID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS t3h7m9k2p8q5 nginx-test.1 nginx:latest worker1 Running Running 2 minutes ago r2t5v8x1p4m9 nginx-test.2 nginx:latest manager2 Running Running 2 minutes ago n8xkp2z3m5q1 nginx-test.3 nginx:latest worker2 Running Running 2 minutes ago能看到 3 个容器分别运行在 worker1、manager2 和 worker2 上。Swarm 会自动把容器调度到不同的节点保证负载均衡。现在随便访问哪个节点的 80 端口都能看到 Nginx 的欢迎页面。这就是 Swarm 的负载均衡功能外部请求会自动分发到各个容器。服务高可用测试说了半天高可用到底高可用在哪里我们来模拟一下节点故障。直接把 worker1 关机# 在 worker1 上执行shutdown-hnow然后等个几十秒再查看服务状态dockerservicepsnginx-testID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS t3h7m9k2p8q5 nginx-test.1 nginx:latest worker1 Shutdown Shutdown 30 seconds ago k7v2m9p4n7q1 nginx-test.1 nginx:latest manager3 Running Running 10 seconds ago r2t5v8x1p4m9 nginx-test.2 nginx:latest manager2 Running Running 5 minutes ago n8xkp2z3m5q1 nginx-test.3 nginx:latest worker2 Running Running 5 minutes ago看到了吗原来在 worker1 上运行的容器自动关闭了Swarm 在 manager3 上重新启动了一个新的容器。整个过程完全自动不需要人工干预。这就是高可用的核心当某个节点挂掉上面的服务会自动迁移到其他健康节点上运行。再测试一下 Manager 节点故障。我们把 manager1Leader关机# 在 manager1 上执行shutdown-hnow等一会儿在 manager2 或 manager3 上查看集群状态dockernodelsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION dxn1zf6l61qsb1josjja83ngz manager1 Down Active Unreachable 20.10.14 rfm5vi4vpxpmhjw6vz0t4wqdv * manager2 Ready Active Leader 20.10.14 y6jcg5z4m7x0tz1f0qz3n5k8s manager3 Ready Active Reachable 20.10.14 n8xkp2z3m5q1rt7v9w2j4h6c0 worker1 Ready Active 20.10.14 t3h7m9k2p8q5wv1n4x6j0r2b8 worker2 Ready Active 20.10.14manager1 显示 Down 和 Unreachable而 manager2 自动变成了 Leader。这就是 Raft 协议的功劳剩下的 2 个 Manager 节点重新选举manager2 成了新的 Leader。集群依然正常工作服务不受影响。部署真实业务服务测试服务跑通了现在部署一个真实的业务。我们有个用户中心服务用 Spring Boot 写的依赖 MySQL 和 Redis。1. 创建 MySQL 服务dockerservicecreate\--namemysql\--replicas1\--networkmy-overlay\--mounttypevolume,sourcemysql-data,target/var/lib/mysql\-eMYSQL_ROOT_PASSWORDyour_password\-eMYSQL_DATABASEuser_center\--constraintnode.roleworker\mysql:5.7这里用了--constraint node.roleworker强制 MySQL 只运行在 Worker 节点上。因为 Manager 节点已经够忙了数据库这种重服务还是放 Worker 上比较好。--mount参数挂载了数据卷保证数据持久化。如果不挂载卷容器重建后数据就丢了。2. 创建 Redis 服务dockerservicecreate\--nameredis\--replicas1\--networkmy-overlay\--mounttypevolume,sourceredis-data,target/data\--constraintnode.roleworker\redis:6.23. 创建用户中心服务dockerservicecreate\--nameuser-center\--replicas2\--networkmy-overlay\-eSPRING_PROFILES_ACTIVEprod\-eMYSQL_HOSTmysql\-eREDIS_HOSTredis\--publish8080:8080\your-registry.com/user-center:latest注意这里的环境变量MYSQL_HOSTmysql和REDIS_HOSTredis直接用服务名就能访问这就是服务发现的好处。Swarm 内置了 DNS 服务器会自动把服务名解析到对应的容器 IP。服务部署完了看看整体状态dockerservicelsID NAME MODE REPLICAS IMAGE PORTS k7v2m9p4n7q1 mysql replicated 1/1 mysql:5.7 r2t5v8x1p4m9 redis replicated 1/1 redis:6.2 t3h7m9k2p8q5 user-center replicated 2/2 your-registry.com/user-center:latest *:8080-8080/tcp所有服务都正常运行。使用 Docker Compose 文件部署上面的命令行方式适合单个服务如果服务多了管理起来很麻烦。Docker Swarm 支持用 Docker Compose 文件部署方便多了。创建一个 docker-compose.yml 文件version:3.8services:mysql:image:mysql:5.7environment:MYSQL_ROOT_PASSWORD:your_passwordMYSQL_DATABASE:user_centervolumes:-mysql-data:/var/lib/mysqlnetworks:-my-overlaydeploy:replicas:1placement:constraints:-node.role workerredis:image:redis:6.2volumes:-redis-data:/datanetworks:-my-overlaydeploy:replicas:1placement:constraints:-node.role workeruser-center:image:your-registry.com/user-center:latestenvironment:SPRING_PROFILES_ACTIVE:prodMYSQL_HOST:mysqlREDIS_HOST:redisports:-8080:8080networks:-my-overlaydeploy:replicas:2update_config:parallelism:1delay:10sfailure_action:rollbackrestart_policy:condition:on-failuredelay:5smax_attempts:3networks:my-overlay:external:truevolumes:mysql-data:redis-data:这个文件里加了几个重要的配置deploy.update_config滚动更新配置一次更新 1 个容器间隔 10 秒失败自动回滚deploy.restart_policy重启策略失败后重启最多重试 3 次部署服务dockerstack deploy-cdocker-compose.yml myapp查看 stackdockerstacklsNAME SERVICES myapp 3查看 stack 里的服务dockerstack services myapp用 stack 的好处是一次性部署多个服务而且更新的时候也很方便改完配置文件重新执行 deploy 命令就行。滚动更新实战业务迭代是常事更新服务是家常便饭。传统方式更新服务得先停掉旧版本再启动新版本中间会有服务中断。Docker Swarm 的滚动更新就优雅多了它会逐步用新版本替换旧版本保证服务始终可用。假设我们要把 user-center 服务从 v1.0 更新到 v2.0dockerserviceupdate\--imageyour-registry.com/user-center:v2.0\--update-parallelism1\--update-delay 10s\--update-failure-action rollback\myapp_user-center参数说明--update-parallelism 1一次更新 1 个容器--update-delay 10s更新间隔 10 秒--update-failure-action rollback更新失败自动回滚更新过程中查看状态dockerservicepsmyapp_user-centerID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS k7v2m9p4n7q1 myapp_user-center.1 your-registry.com/user-center:v2.0 worker1 Running Running 5 seconds ago t3h7m9k2p8q5 \_ myapp_user-center.1 your-registry.com/user-center:v1.0 worker1 Shutdown Shutdown 6 seconds ago r2t5v8x1p4m9 myapp_user-center.2 your-registry.com/user-center:v1.0 worker2 Running Running 2 minutes ago能看到 worker1 上的容器已经更新到 v2.0worker2 上的还是 v1.0。等 10 秒后worker2 上的也会更新。如果新版本有问题比如容器启动失败Swarm 会自动停止更新并回滚到旧版本。这个功能太实用了再也不用担心更新把服务搞挂了。服务扩缩容业务高峰期服务扛不住了需要快速扩容。Docker Swarm 扩容只需要一条命令dockerservicescale myapp_user-center5瞬间就把 user-center 服务扩展到 5 个副本。Swarm 会自动把新容器调度到空闲的节点上。查看扩容结果dockerservicelsID NAME MODE REPLICAS IMAGE PORTS t3h7m9k2p8q5 myapp_user-center replicated 5/5 your-registry.com/user-center:v2.0 *:8080-8080/tcp缩容也一样简单dockerservicescale myapp_user-center2Swarm 会自动选择要停止的容器保证负载均衡。私有镜像仓库配置生产环境的镜像一般都存在私有仓库里Docker Swarm 拉取私有镜像需要认证。假设我们用 Harbor 搭建了私有仓库地址是 registry.example.com。1. 创建 Docker 登录认证在任意 Manager 节点登录私有仓库dockerlogin registry.example.com输入用户名和密码登录成功后认证信息会保存在~/.docker/config.json文件里。2. 创建 Docker Secret把认证信息创建成 Swarm Secretdockersecret create registry-auth ~/.docker/config.json3. 创建服务时使用 Secretdockerservicecreate\--nameuser-center\--replicas2\--networkmy-overlay\--secretsourceregistry-auth,target/root/.docker/config.json\--publish8080:8080\registry.example.com/user-center:latest这样服务就能从私有仓库拉取镜像了。监控和日志集群跑起来了监控不能少。我们用 Portainer 来管理 Swarm 集群用 ELK 收集日志。部署 PortainerPortainer 是个轻量级的容器管理界面支持 Docker Swarm。dockerservicecreate\--nameportainer\--replicas1\--publish9000:9000\--mounttypebind,src/var/run/docker.sock,dst/var/run/docker.sock\--mounttypevolume,srcportainer-data,dst/data\portainer/portainer-ce:latest访问任意节点的 9000 端口就能看到 Portainer 的管理界面。可以查看所有服务、容器、网络、卷还能在 Web 界面直接操作非常方便。[5]日志收集容器的日志默认存在本地不方便查看和分析。我们用 ELKElasticsearch Logstash Kibana来收集日志。不过 ELK 部署比较复杂这里就不展开了。简单点的方案是用 Filebeat 收集日志发送到 Elasticsearch然后用 Kibana 查看。踩过的坑搭建过程中遇到不少坑记录下来给大家避雷。坑 1Manager 节点不要跑太多服务刚开始我把所有服务都往 Manager 节点上放结果 Manager 负载太高集群管理都受影响。后来加了--constraint node.roleworker把重服务都限制在 Worker 节点上问题解决。坑 2数据卷挂载要小心MySQL 这种有状态的服务一定要挂载数据卷不然容器重建后数据就丢了。而且挂载的时候要用typevolume不要用typebind因为 bind 挂载依赖宿主机的目录结构迁移起来很麻烦。坑 3端口冲突多个服务不能用同一个宿主机端口。我有次部署两个服务都用 8080 端口结果第二个服务启动失败。解决办法是每个服务用不同的端口或者用 Ingress 路由。[4]坑 4节点时间不同步前面说过节点时间不同步会导致集群选举出问题。我有次重启服务器后时间同步服务没起来结果集群状态一直不正常。后来加了监控定期检查时间同步状态。坑 5网络问题Docker Swarm 用覆盖网络实现跨节点通信如果网络配置有问题容器之间会无法通信。有次我忘了开放 4789/UDP 端口结果容器之间 ping 不通。排查了半天才发现是防火墙的问题。生产环境的一些建议在测试环境跑通了上生产环境还得注意几点1. 资源限制给每个服务设置资源限制防止某个服务占用太多资源影响其他服务dockerserviceupdate\--limit-cpu2\--limit-memory 4G\--reserve-cpu1\--reserve-memory 2G\myapp_user-center2. 健康检查给服务加上健康检查Swarm 会定期检查容器状态不健康就自动重启healthcheck:test:[CMD,curl,-f,http://localhost:8080/health]interval:30stimeout:10sretries:3start_period:40s3. 备份策略定期备份 Manager 节点的/var/lib/docker/swarm目录里面存着集群的状态数据。如果所有 Manager 都挂了可以用备份恢复集群。4. 监控告警部署监控Prometheus Grafana设置告警规则。集群状态、服务状态、资源使用情况都要监控。5. 灰度发布更新服务时不要一次性全部更新。可以先更新 1 个副本观察没问题后再继续更新。用--update-parallelism参数控制。写在最后Docker Swarm 虽然不如 Kubernetes 强大但对中小团队来说绝对够用了。搭建简单维护成本低功能也齐全。我们公司用了快一年除了刚开始踩了几个坑后面一直很稳定。最重要的是它真的解决了我们单点故障的问题。现在随便挂一台服务器服务都能自动迁移到其他节点再也不用半夜起来重启服务了。如果你们团队也在考虑容器编排方案不妨先试试 Docker Swarm。等业务规模大了再考虑迁移到 Kubernetes 也不迟。这篇文章写了好久把搭建过程和踩过的坑都详细记录下来了。如果你也在用 Docker Swarm或者准备搭建 Swarm 集群希望这篇文章能帮到你。有问题欢迎在评论区留言我会尽量回复。如果觉得文章有用点个赞再走呗也欢迎转发给需要的朋友。公众号运维躬行录个人博客躬行笔记关注公众号获取更多运维干货文章一起成长一起进步
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2507335.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!