资讯动态

K8s高可用集群部署实战:基于Rocky Linux与KubeKey的完整指南

发布时间:2026/9/29 17:27:54 来源:尧图企业网站定制
做生产环境运维这些年“高可用”三个字是我在方案评审会上听到最多、也在故障复盘时懊恼最多的词。它涵盖的范围远比你想象的宽Kubernetes控制面要不要三台Masteretcd怎么选主数据层MySQL和SQL Server怎么同步后端代码在故障切换的时候能不能扛住连接池、超时、幂等这些坑。任何一个环节没考虑到线上事故就会精准地打在那个最薄弱的点位上。这篇文章我就以Rocky Linux 9上用KubeKey基于Docker部署K8s高可用集群为主线把从架构原理、部署实操到代码编写、问题排查的完整链路讲一遍都是我在生产环境里实际验证过的经验和教训供大家参考。1. 生产高可用的本质先想清楚要防什么挂1.1 单点故障高可用要消灭的敌人所有的高可用设计本质上都在做同一件事把系统里的每一个单点故障打成冗余。所谓单点故障是指任何一个组件崩溃都会导致整个业务不可用的那种部署结构。经典例子是网站只有一台Nginx入口这台Nginx挂了后面应用服务器再多也没用流量全部进不来。我刚做运维时处理过一次很典型的故障数据库主库CPU飙升订单服务大面积超时。当时我们确实做了MySQL主从复制但没有配置自动切换机制从库只是躺在那里业务根本不知道要把流量切过去。那次事故让我彻底明白高可用不是“备份了一份数据”就完事而是从故障发现、切换决策、流量迁移到恢复验证的一整套自动化流程。生产环境里的单点故障往往不在你盯得最紧的环节而在那些“平时不显眼”的组件上。比如K8s集群的Ingress控制器、配置中心、消息队列单节点、定时任务调度器甚至是没有做冗余的NTP服务。工程上我习惯默认一条规则凡是没有两个以上副本的组件都当它明天会挂。这样想问题方案设计时自然会多留几条后路。1.2 可用性指标怎么算高可用的目标一般用一个数字描述SLA。业内常说的“三个九”“四个九”对应99.9%和99.99%的可用性换算成每年的停机时间分别约是8.76小时和52.6分钟。公式很简单每年停机时间 365天 × 24小时 × 60分钟 × (1 - 可用性百分比)别小看这个换算现实中很多团队把“99.9%”挂在嘴边真正拿计算器一算发现自己根本做不到。99.9%意味着一年最多不能超过8.76小时故障平均每个月不到44分钟。一次跨机房网络抖动加上两次发布回滚可能就把这个额度消耗光了。我在评估集群方案时习惯把可用性预算拆到组件层级。K8s集群里重点看三个层次控制面可用性、工作负载可用性、数据层可用性。控制面高可用保证集群管理面不出问题工作负载高可用靠多副本、反亲和和PodDisruptionBudget来维持数据层高可用则依赖MySQL、SQL Server这类存储的同步复制和故障自动切换。三层里任何一层缺位最终的业务可用性都会掉档这不是靠堆机器就能解决的。1.3 把故障域拆开控制面、数据面、数据层彻底的故障域拆解是我看完大量事故复盘后养成的习惯。拿Kubernetes集群来说它天然分成控制面和数据面。控制面由API Server、Controller Manager、Scheduler和etcd组成数据面由Worker节点上的kubelet、kube-proxy以及实际运行业务的Pod组成。两者职责不同故障影响面也完全不同。控制面故障最典型的场景是API Server不可用。这个时候集群里已有的Pod还在运行但用户无法执行kubectl控制器无法调节期望状态节点心跳上报也会出问题。如果故障持续kubelet会因为无法续约租约而认为控制面失联甚至触发Pod驱逐的连锁反应。这个细节很多人没意识到控制面不只是“管理工具”它挂了会直接影响数据面稳定性。数据层又是另一套逻辑。MySQL、SQL Server这类有状态组件仅仅部署在多台机器上是不够的数据必须有一套同步机制还要保证主备切换时数据不丢或少丢。所以数据层高可用的设计往往比应用层复杂得多。后面我会单独开一章细说MySQL和SQL Server的同步方案这里先把框架立起来控制面要消灭“指挥系统”的单点数据面要多副本加自动调度数据层要聚焦复制和切换。2. 控制面高可用架构三台Master背后的道理2.1 为什么是奇数节点做过K8s部署的都知道生产环境至少三台Master节点但很多人只是照着文档配置并不清楚“为什么三台而不是两台或四台”。答案藏在etcd的Raft一致性算法里。etcd是K8s的“唯一事实来源”所有集群状态都通过它读写。Raft算法要求成员节点投票选举只有获得超过半数节点n/21确认操作才算成功。三节点集群允许挂掉一个节点剩余两个仍然超过半数可以继续运转如果挂掉两个剩下一个达不到半数集群只能读不能写。由此可以理解为什么“偶数节点”很尴尬。两个节点组成的集群挂掉一个就只剩一个达不到半数可用性和单节点没有本质区别四个节点的容错能力和五个节点一样都允许挂一个但资源多了一个性价比很低。Raft这种“多数派”机制决定了奇数且至少三个节点才是合理选择。生产环境用三台Master就是用最少的资源换“允许一台宕机而不影响集群”这层保障。2.2 API Server无状态加负载均衡三台Master节点上每台都会运行kube-apiserver。API Server本身是无状态的它把状态全部放在etcd里自己只负责处理请求、校验权限、读写etcd。无状态组件天然适合水平扩展多起几个实例前面加一个负载均衡器把请求分摊到多台API Server上。实际生产架构里三台Master前面一般会配一个四层负载均衡器VIP或者域名指向6443端口。这个LB解决两个问题一是流量分发让三台API Server都有请求处理减轻单台压力二是故障隔离某台API Server不可用时LB自动把它摘掉客户端无感知。这里有个常见误区有人习惯用七层负载均衡比如Nginx来转发Kubernetes API请求。API Server涉及HTTP/2和长连接Nginx配置会变得很别扭而且API Server天然适合四层TCP分发不需要七层的高级功能。四层方案更简单、更可靠。我自己的做法是外部云LB或者自建HAProxy加Keepalived保持一个VIP域名解析到VIP客户端访问域名:6443。2.3 Controller Manager和Scheduler的选主机制与API Server无状态不同kube-controller-manager和kube-scheduler是“有状态派”。这两个组件如果同时有多个实例工作会导致同一个控制器被多次执行产生重复操作甚至状态错乱。K8s的解法是Leader Election也就是选主。三台Master上会同时运行三个controller-manager实例但只有抢到leader的那个真正执行控制逻辑其余两个一直处于热备状态。一旦当前leader失联其他实例会在租约超时后重新选举成为新的leader继续干活。这个过程一般几十秒内完成不需要人工干预。选主依赖的是etcd里的一个小资源对象每个实例定期刷新租约。这个机制很精妙它把“主备切换”变成了一种分布式协调而不是依赖Keepalived式的外部脚本。但副作用也很明显如果etcd本身出了问题选主也会乱套。所以一切回到etcd的稳定性上这也是为什么etcd节点必须跟Master节点一起做冗余并且对硬件可靠性不能妥协。3. Rocky Linux 9 Docker KubeKey 高可用集群实操3.1 环境规划与前置准备KubeKey是KubeSphere社区开源的集群部署工具最大优势是一条命令拉起K8s集群同时对高可用场景做了封装。相比手动用kubeadm初始化再自己去配置负载均衡KubeKey把HAProxy、Keepalived、etcd集群、证书管理这些步骤都收敛了能少踩很多坑。先看环境规划。我以三台Master、两台Worker为例所有节点装Rocky Linux 9额外准备一台独立LB节点用于部署HAProxy和Keepalived。如果你追求简单也可以在其中一台Master上跑HAProxy和Keepalived但生产环境我更推荐独立LB节点避免负载均衡器和Master节点同时故障的极端情况。系统基础配置是所有环节里最不能省的一步按顺序确认每台节点修改主机名配置hosts解析保证节点之间通过主机名互通。永久关闭swapK8s对swap很敏感开着会直接影响Pod的内存QoS。加载br_netfilter、ip_vs等内核模块设置ip_forward和bridge-nf-call-iptables为1。处理SELinux和firewalld。Rocky Linux 9默认SELinux Enforcing部署期间建议先设为permissive稳定后再根据审计日志精细化放行防火墙要么严格配置放行端口要么在可控内网环境中先关掉态度不能模糊。配置Docker或containerd的镜像源和存储驱动。虽然Kubernetes从1.24版本起把Docker作为运行时标记为废弃但KubeKey对容器管理做了兼容处理使用Docker作为运行时依然能顺利部署。这些前置配置里最容易出问题的就是SELinux和防火墙。我遇到过好几起kubectl连接异常、Pod调度失败的案子追到最后全是firewalld规则把端口挡住了。生产环境一定要把端口策略做成文档逐项核对不要凭感觉放行。3.2 编写KubeKey配置的关键参数KubeKey的部署配置用YAML描述核心是把hosts、roleGroups、controlPlaneEndpoint和kubernetes这几块填对。以下是高可用集群配置的骨架示例不同版本的KubeKey字段可能微调以实际生成的默认配置为准apiVersion: kubekey.kubesphere.io/v1beta1 kind: Cluster metadata: name: sample spec: hosts: - {name: master1, address: 192.168.1.11, user: root, password: YourPassword} - {name: master2, address: 192.168.1.12, user: root, password: YourPassword} - {name: master3, address: 192.168.1.13, user: root, password: YourPassword} - {name: worker1, address: 192.168.1.21, user: root, password: YourPassword} - {name: worker2, address: 192.168.1.22, user: root, password: YourPassword} roleGroups: etcd: - master1 - master2 - master3 control-plane: - master1 - master2 - master3 worker: - worker1 - worker2 controlPlaneEndpoint: domain: lb.k8s.local address: 192.168.1.100 port: 6443 kubernetes: version: v1.26.5 clusterName: cluster.local autoRenewCerts: true containerManager: docker这个配置里有三个地方需要格外留意。第一是roleGroupsetcd和控制面节点建议保持一致一般就是三台Master不要图省事让etcd只跑在单台上那样控制面高可用就名存实亡了。第二是controlPlaneEndpoint里的address它指向负载均衡入口通常是一个VIP或独立LB的IP这个地址就是将来所有kubectl请求的入口。第三是autoRenewCerts务必开启为true否则一年后证书到期会让你深夜爬起来处理集群不可用。3.3 部署执行和验证配置文件准备好之后执行步骤很直接下载KubeKey二进制赋权后执行创建命令指定配置文件让其创建集群。整个过程视网络情况可能持续10到30分钟主要花在拉镜像和初始化组件上。这里强烈建议提前把容器运行时和KubeKey依赖的镜像源切换成内网镜像或加速地址能省下一大截时间。我遇到过好几次部署到一半卡住的情况排查后全是网络拉包超时。生产环境还是那句话宁可提前把网络环境准备好也不要赌公网拉取速度。集群起来之后验证方法很简单但必须做完整执行kubectl get nodes三台Master的Ready状态必须稳定。查看kube-system下的核心Pod重点确认etcd、kube-apiserver、kube-controller-manager、kube-scheduler、coredns没有CrashLoopBackOff。通过LB的VIP或域名执行kubectl get nodes确认从外部访问控制面正常。手动模拟一次故障停掉一台Master上的kubelet服务观察集群是否还能正常工作。模拟故障的验证非常关键。高可用架构不是搭出来就自动具备能力很多方案在方案评审会上完美实际一停节点才发现VIP不漂移、负载均衡不摘除故障节点、etcd选主混乱。我自己的习惯是每次搭建完都强制做一次故障演练把某个Master直接关机记录从服务不可用到恢复的时间再把这个时间作为“真实可用性”的基线后续每次变更后重新演练对比。4. 数据层高可用MySQL与SQL Server的同步方案4.1 MySQL高可用主流方案选型K8s集群里的业务跑得再稳数据层可靠性跟不上生产高可用就只是空话。MySQL的高可用方案我实际用过的有三类适用场景不同。第一类是主从复制加MHA。MHA是经典的自动切换方案监控主库状态在主库故障时选择数据最新的从库提升为主库通过脚本把VIP漂移过来同时通知其他从库同步新主。优点是成熟稳定、对版本兼容好缺点是切换依赖外部脚本介入切换时间通常要10到30秒极端场景下有数据丢失风险因为传统复制默认是异步的。第二类是MySQL Group ReplicationMGR基于组复制的Paxos协议多个节点组成一个组自动协商支持多主或单主模式。MGR在一致性上比传统主从强很多配置single-primary模式后发生故障会自动选新主应用基本无感。缺点是要求所有节点版本一致、网络稳定表结构也有限制更适合标准化程度高的新业务。第三类是用Orchestrator做复制拓扑管理。Orchestrator本身不参与数据复制只负责管理MySQL的复制关系能自动发现主库故障把最合适的从库提升上去还提供Web管理界面。优点是灵活、便于做故障切换演练缺点是需要自己搭建MySQL复制链路属于“半自动”方案。如果业务主库压力不算极端我倾向于优先使用MGR的single-primary模式事务冲突少切换自动化程度高。如果业务是传统大单量写入、对MGR限制无法接受就选择MHA加半同步复制开启半同步后至少能保证主库已提交的事务在备份节点有日志落盘记录切换时数据更安全。4.2 SQL Server Always On同步提交的关键点SQL Server的高可用方案中常见的是Always On可用性组AG。它把主数据库和至少一个辅助副本组成一个可用性组通过Windows Server Failover Clustering实现故障自动切换。AG有两种同步模式同步提交和异步提交。同步提交模式下主副本的事务提交前必须等待辅助副本确认日志已经固化这保证了故障切换时不会丢已提交的事务但代价是写入延迟变高因为每次提交都要多等一次网络往返。异步模式延迟低但如果主副本瞬间宕机未同步的日志就会丢失切换后必然有数据缺口。生产环境选型时我的建议是交易支付类业务必须用同步提交模式写入延迟可以靠减少网络跳数、升级万兆网来弥补日志、分析、报表类数据则用异步模式换取性能。AG还有一个容易被忽略的优点辅助副本可以做只读路由把只读查询分流到备库明显缓解主库压力。配置AG时务必调整健康检查和故障检测超时时间默认值在部分场景下过于敏感网络抖动会引发不必要的自动切换。4.3 数据层切换时业务怎么配合数据层高可用方案配好以后真正的考验是数据库主备切换那一瞬间业务端的表现。数据库切换不是瞬间完成的从故障检测到新主库accept写流量中间总有一个短暂时间窗口可能是几秒也可能是几十秒业务必须能扛住这个窗口。作为后端开发者我总结出四个必做项。第一数据库连接池要配置合理的最大连接数和等待时间防止切换时应用疯狂建连把新主库打垮。第二连接池要有探活机制自动把死连接剔除不能等连接池自身超时才恢复。第三业务代码里的数据库连接都应该是短期获取而不是长期持有不然切换后代码还握着旧主库的连接。第四写操作要有重试机制但必须设置退避策略不要一失败就瞬间重试几百次。数据层切换还有一点常被忽视应用应该在启动时读取配置中心并订阅数据库地址的动态变化。如果架构里使用了VIP漂移应用不需要感知地址变化但如果切换模式是直接切连接地址那配置中心的自动推送和应用的动态感知就非常关键。把数据库地址写死在代码里的做法在单机时代勉强能用在讲究高可用的生产环境里完全不可接受。5. 高可用场景下后端代码的注意事项5.1 连接池、超时与重试后端代码在系统高可用中扮演的角色很多人低估了。架构上有三台Master和能自动切换的数据库如果代码写得粗心一样会在故障切换时制造新的故障。最常见的是连接池配置失当。连接池要关注三组参数初始连接数、最大连接数和空闲回收时间。最大连接数太大会浪费资源太小会在流量高峰时抛异常空闲回收时间太短会让连接频繁重建太长则让连接长期处于即将失效的状态。生产环境我习惯根据压测结果来确定初始值取max的20%左右最大连接数要对照数据库实例的max_connections不是拍脑袋。超时配置同样重要。一次数据库请求极慢可能是数据库正在主备切换也可能是网络故障。如果把超时设成30秒服务会选择一直等待所有请求堆积在等待队列里最终把整个线程池耗尽。正确的做法是分层设置超时连接获取超时3秒、读超时5秒、写超时5秒所有外部调用都带超时。超时后触发重试但重试必须是退避式的第一次等500毫秒第二次1秒第三次2秒而不是无脑循环。这里最危险的反模式是一个异常请求在主库故障期间被反复重试几万个客户端同时重试把尚且健康的备库直接压垮。业界把这叫“重试风暴”。要避免它必须在客户端做全局限流和熔断比如用Resilience4j或者Sentinel这类框架把重试次数和并发隔离控制在合理范围。5.2 幂等设计与重复请求防御故障切换会带来一个经典问题请求到底执行了没有典型场景是支付回调。你的服务向数据库提交了订单事务但还没返回给调用方数据库主库切换了请求超时。调用方按重试策略重新发起请求此时事务已经在旧主库提交成功新主库里已经有这笔订单如果代码不处理幂等就会重复创建订单。幂等设计并不复杂核心是“用一个唯一标识换取一次执行”。在正式业务逻辑执行前先检查这个标识是否已经存在如果存在直接返回之前的结果。可以在数据库表里加一个唯一键比如订单号、流水号也可以在Redis里用SETNX做一个幂等键。K8s环境里更通用的做法是给每个请求生成一个requestId后端在入口处做去重校验。另一个容易忽略的细节是消息队列的消费端。K8s中Pod重新调度后消息可能已经被消费但没来得及提交offset重新拉起来后会再消费一次。消费端逻辑必须设计成能接受重复消息一般还是靠幂等键兜底。高可用做得越彻底重复消息和重复请求出现得越频繁幂等这套机制是必备品不是可选项。5.3 优雅上下线与健康检查K8s运行时Pod被驱逐、重新调度、滚动发布都是常态。如果代码没有实现优雅下线每一次发版都会看到请求报错因为Pod收到SIGTERM后进程立即退出但负载均衡还在把流量打给它。优雅下线要处理三件事一是收到SIGTERM后先从服务注册中发现下线通知让负载均衡停止分发新流量二是对已经分发的请求设置一个缓冲期通常是几秒到30秒处理完再退出三是配置K8s的preStop钩子在真正停止容器前触发一个短暂的sleep把流量摘干净。健康检查是另一个必须做好的点。K8s里livenessProbe和readinessProbe的区别经常被混淆liveness决定要不要重启容器readiness决定要不要把流量放进来。正确姿势是readiness探针不仅要探进程活着还要确认它依赖的下游比如Redis、数据库能连通避免出现“进程活着但业务不可用”的假健康状态。如果把这种探针错误地用作liveness下游一抖动就会触发批量重启容器这是我在生产里踩过的坑务必分开对待。5.4 配置中心和leader选举感知高可用场景下业务侧还有一个隐性问题配置如何动态刷新。如果数据库主库地址要变、功能开关要调整、某台机器要下线都依赖改代码重启故障恢复时间会被无限拉长。线上实践应该把配置收敛到配置中心比如Apollo、Nacos或者K8s的ConfigMap热更新应用启动时拉取一份同时订阅变更事件动态刷新内存中的配置。还有一类特殊业务需要业务侧感知“谁是主”。比如定时任务如果部署了多副本所有Pod都会同时执行这时候就要在任务代码里做分布式选主。常见做法是用Redis的分布式锁或者ZooKeeper的临时节点拿到锁的实例才执行任务其余实例等待。否则你前面搞了一堆高可用基础设施结果一个定时任务在每个副本上都执行了一遍数据被重复写入。同理如果业务里有类似“每隔几秒扫描某张表”的常驻逻辑也建议设计成选主模式避免多副本同时扫描产生并发问题。高可用架构会把单实例变成多实例业务代码就必须跟随这个变化重写掉那些基于“只有一个实例在跑”的假设逻辑。6. 常见问题与排查技巧实录6.1 VIP切换期间连接中断的问题我在生产环境最常见的第一个坑是Keepalived的VIP漂移时间太长。HAProxy加Keepalived这套方案中Master节点宕机后VIP从故障节点漂移到备用节点通常需要2到3秒。这个时间窗口里客户端TCP连接会全部断开如果业务代码没有重试机制这些请求就直接报错。排查这类问题时先确认VIP是否漂移成功在备用节点上执行ip addr看VIP是否出现再确认HAProxy健康检查有没有把故障的API Server摘掉。提高切换速度可以调整Keepalived的VRRP脚本间隔时间和HAProxy的rise、fall参数但不能无脑调小否则网络抖动也会触发频繁切换得不偿失。另一个容易踩的坑是客户端DNS缓存。如果LB通过域名暴露客户端长时间缓存旧IPVIP漂移后新解析不到新IP同样会报连接超时。生产中建议让DNS的TTL设小一些在客户端代码里不要使用无限长连接定期重建连接这样能在一定程度上规避VIP切换带来的TCP断连影响。6.2 etcd性能与节点故障排查etcd是整个集群高可用的灵魂它的性能直接决定集群稳定性。最典型的故障是磁盘延迟过高。etcd对写入延迟极其敏感官方建议数据目录使用SSDfsync延迟要低于10毫秒。如果把etcd放在机械盘或共享存储上IO延迟一升高etcd的心跳和租约就会超时Master节点互相认为对方失联进而触发选主选来选去整个集群状态就开始抖动。排查etcd问题时先看etcd Pod的状态和日志再用etcdctl检查成员列表和端点健康状态。注意etcdctl连接需要指定证书和CA路径在KubeKey部署的环境里证书通常在/etc/kubernetes/pki/etcd/目录。如果发现节点持续报leader changed优先排查网络和磁盘资源而不是急着改etcd参数。还有一个长期维护的坑etcd数据目录磁盘占用率超过90%以后etcd可能拒绝写入必须做历史版本压缩和碎片整理。集群运行一两年不管etcd会积累大量历史版本数据磁盘占用率逐年上涨。建议在运维层面加定时任务执行etcd碎片整理同时配置自动压缩策略。这属于“平时看不见出事就是大事”的问题。6.3 证书过期与集群长期维护Kubernetes集群长期运行有一个绕不开的坑证书过期。默认情况下kubeadm初始化的集群admin等核心证书有效期是1年KubeKey部署的集群可以通过配置开启自动续期。证书一旦过期kubectl访问会直接报认证失败节点上报也会被拒绝整个集群处于半瘫痪状态。KubeKey的配置中已经提供自动续期选项但我实际维护中仍见过不少老集群没开这个选项或者因为版本原因自动续期不生效。所以生产维护清单里必须有“证书巡检”这一项建议每季度查一次证书有效期信息。续期操作本身不复杂但注意续期后要重启kube-apiserver、kube-controller-manager、kube-scheduler这些控制面组件并且把新的admin证书更新到本地kubeconfig里。如果使用KubeKey部署建议直接升级到支持自动续期的版本并开启这是治本的方式。还有我特别想强调的长久维护习惯任何高可用集群都要有周期性的故障演练不能只在搭建时测一次就完。很多团队在集群刚建成的时候热热闹闹做了切换演练之后半年一年不再演练等真正出事时VIP漂移脚本可能被某次升级覆盖了健康检查可能被防火墙堵了证书可能已经过期了。高可用是动态能力不是静态配置定期验证、故障演练和备份恢复才是长期稳定的保障。这几年做生产环境的体会是高可用从来不是某个组件的事也不只是运维的事而是一条从基础设施到应用代码的完整链路。架构上做三台Master数据层做同步复制和自动切换后端代码处理超时、重试、幂等和优雅下线每一环都要咬合到位。真正开始做的时候不要指望某个“神器”解决所有问题把每一层细节打磨扎实故障自然会少很多。最后分享一个小技巧每次做完故障演练一定要把过程写成文档记录每个环节的耗时和问题。下次再遇到类似故障照着文档走心里就有底手也不会抖。高可用这条路没有终点持续演练、持续复盘才是生产环境最靠谱的护身符。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑