☰
K8s上部署MySQL、Kafka、Redis高可用中间件实战与踩坑总结
2026/10/22 8:15:01 网站建设 项目流程

中间件实战做过不少,但把 MySQL、Kafka、Redis 这三件套高可用地跑在 K8s 上,还是让我结结实实踩了一轮坑。上个月帮一个内部项目做中间件平台改造,刚开始我的想法很简单:有现成镜像,写几个 Deployment 一推不就行了?结果第一次故障演练就把我打醒了——Kill 掉一个 MySQL 主库 Pod,业务直接不可用,等了快两分钟新主都选不出来。后来重新设计了整套部署方案,把三个中间件分别交给 Operator 管理,才把 RTO 压到可接受范围。这篇内容就把我最终沉淀下来的高可用部署方案、参数细节和踩坑过程完整写出来,给正准备碰有状态服务的同学做个参考。

1. 先别急着安装:搞懂 MySQL、Kafka、Redis 各自的高可用底线

1.1 三种中间件的故障模型差异

高可用部署最忌讳一刀切。很多人在 K8s 上部署 MySQL、Kafka、Redis 时,习惯性地把三个都当成“有状态应用”来处理,最后配置出来的集群看似都有副本,实际上故障恢复逻辑完全不同,出了问题根本无从排查。

MySQL 走的是主从复制模型,主库承担写流量,从库异步或半同步复制数据。它的高可用核心在于“选主”和“防脑裂”,不能出现两个主库同时接收写入。

Kafka 走的是分区多副本模型,每个分区有多个副本,其中一个成为 Leader,其余是 Follower。它的高可用核心在于“ISR 集合”和“最小写入副本”,消息只有在满足副本写入要求后才算提交。

Redis 则是另一种逻辑,主从加哨兵或者集群分片。哨兵负责监控和故障转移,集群模式则把数据分片到多个主节点,每个主节点带副本。

三种中间件对故障的容忍度也不一样:MySQL 丢一条事务可能直接导致数据不一致,Kafka 丢消息会导致消费者逻辑错乱,Redis 丢数据则取决于持久化配置和业务是否能容忍缓存重建。下面这张表可以快速理解差异:

中间件数据模型高可用核心手段故障恢复的关键点
MySQL主从复制主库故障后从库提升为新主库选主速度、数据一致性、脑裂防护
Kafka分区副本复制副本机制 + ISR 机制Leader 重选举、消息不丢、分区平衡
Redis主从复制 + 分片哨兵故障转移 / 集群分片哨兵多数派、网络地址感知、持久化选择

1.2 先给 RPO 和 RTO 定一个能达到的数字

在配置任何东西之前,我建议先跟业务方确认两个数字:RPO(最多能丢多少数据)和 RTO(故障后多久恢复)。没有这两个数字,高可用方案就是空谈。我们这个内部项目定的目标是 RPO 接近 0,RTO 控制在 30 秒内。听起来不难,实际操作起来会发现,光是一个 MySQL 的选主过程,默认配置就可能超过 10 秒,再算上客户端连接切换,30 秒已经相当紧张。

很多刚接触 K8s 的同学会忽略一点:高可用不是越多副本越好。副本越多,数据同步开销越大,出故障时恢复的复杂程度也越高。MySQL 三副本和五副本在数据安全上没有本质区别,可能只在极端分区场景下有差异。Kafka 的副本因子为 3 时,可以容忍一个副本同时不可用,副本因子为 5 则可以容忍两个副本同时不可用,但磁盘和网络开销直线上升。

我们当时的结论是:MySQL 和 Redis 都用三副本,Kafka 用副本因子 3,broker 节点数至少 5。这样既能满足 RPO/RTO 目标,又不至于让成本翻倍。如果你只是一个测试环境,没必要一上来就上五副本,否则后面每次滚动更新都会让你很痛苦。

1.3 部署之前先检查的五个前置条件

确定 RPO/RTO 之后,还有几个前置条件必须提前确认,否则真正部署时会卡住:

第一,K8s 集群版本。不同版本的 Kubernetes 对 StatefulSet、拓扑分布约束、Pod AntiAffinity 的支持力度不一样,旧版本可能缺少一些关键特性。

第二,存储类。你需要确认集群里存在至少一个支持动态扩容的 StorageClass,否则后面 PV 写满只能干瞪眼。

第三,网络插件是否支持无头服务(Headless Service)的稳定 DNS 解析。大多数 CNI 都支持,但有些特殊网络环境下,需要额外配置。

第四,节点标签。中间件最好部署在带特定标签的节点上,方便调度和管理。

第五,资源预留。K8s 调度器在做资源分配时,如果节点上已经有很多业务 Pod,那么中间件 Pod 可能拿不到足量 CPU 和内存。我们当时专门给中间件节点打了 taint,防止无状态业务挤进来。

2. 环境准备:存储、网络和 Operator 选型的三个关键决策

2.1 为什么有状态中间件不能只靠 Deployment 裸跑

在很多新手教程里,Redis 和 MySQL 可以用 Deployment 加 Service 跑起来,看起来也能用,但那是假高可用。Deployment 管理的 Pod 是可替换的,Pod 重建后 IP 会变、主机名会变,中间件集群需要重新感知节点。更关键的是,Deployment 无法保证 Pod 的启动顺序和持久化存储的绑定关系,数据卷几乎必然挂错。

有状态中间件应该使用 StatefulSet 来管理。StatefulSet 会为每个 Pod 分配稳定的序号和固定的网络标识,同时按顺序创建和更新 Pod。每个 Pod 的持久卷声明(PVC)也是独立绑定的,Pod 被调度到其他节点时,PVC 会自动跟随,数据不会丢。

但光用 StatefulSet 还不够。MySQL 的选主、Kafka 的 Topic 分区迁移、Redis 哨兵的故障转移,这些都是比较复杂的生命周期操作。手写脚本去管理,不仅工作量大,而且容易遗漏边界情况。所以我们最终选择为每个中间件引入对应的 Operator。Operator 做的事情本质上就是把“运维专家经验”写进控制器里:它能自动处理故障检测、主从切换、备份恢复、版本升级。

有状态中间件上 K8s 之前,一定要先搞清楚 Operator 的管理粒度。比如某些 MySQL Operator 不只管理 Pod,还会管理 PVC、Service、备份任务和监控配置。如果只管理 Pod,那它只能帮你拉起实例,不能帮你做故障自愈,那不如不用。

2.2 存储选型:本地卷、云盘和分布式存储的取舍

存储是中间件上 K8s 最容易被低估的一环。我见过有人把所有中间件都放在同一个默认 StorageClass 上,结果 Kafka 因为磁盘延迟过高持续产生告警,MySQL 因为底层存储不支持 fsync 导致性能暴跌。存储选型不能只看“能用”,要看中间件的 IO 特征。

存储类型适用场景优点缺点
本地卷(Local Volume)Kafka、追求低延迟的数据库性能好,延迟低数据固定在节点上,节点故障可能丢失,需要配合备份
云盘(云厂商块存储)MySQL、Redis、普通业务数据可随时扩展、支持跨节点迁移性能受网络影响,IOPS 可能不足
分布式存储(如 Ceph 这类方案)大规模多中间件平台扩展性强,数据分布均匀延迟相对较高,部署运维复杂

我们最终的选择是 MySQL 和 Redis 使用云盘类型的 StorageClass,Kafka 使用本地卷。为什么 MySQL 不用本地卷?因为 MySQL 需要快速的主从切换,如果主库所在节点宕机,使用本地卷的 PV 可能无法立刻挂载到新节点,恢复时间会被拉长。云盘类型的存储可以快速从旧节点解绑,挂载到新节点,配合 Operator 的选主逻辑,能把 RTO 控制在几十秒内。

Kafka 不一样,它本身就是分布式存储设计,消息的可靠性靠多副本机制保证,不依赖单块磁盘。因此它可以放心使用本地卷,把磁盘性能吃满。但前提是你要接受“某个 broker 节点宕机后,该节点上的本地卷数据暂时不可用”的情况,因为 Kafka 的副本机制会保证有其他副本兜底。万一某个分区所有副本都落在同一块本地卷上,那还是可能丢数据,所以后面还要做分区分布规划。

2.3 网络方案和故障域规划

网络方面,三个中间件都需要使用无头服务(Headless Service)来暴露每个 Pod 的稳定 DNS 名称。例如 MySQL Pod 的地址可以是mysql-0.mysql-internal.xxx.svc.cluster.local,这个地址在 Pod 重建后依然是同一个。中间件集群内部的节点相互通信,应该用这类 DNS 名称,而不是 IP 地址。

故障域规划上,我们当时把中间件节点按可用区分组,并给节点打了zone-a、zone-b、zone-c的标签。然后通过topologySpreadConstraints让同一中间件集群的 Pod 尽量分散到不同可用区。这样即使某个可用区的网络或者节点出问题,其他可用区的副本仍然能继续提供服务。注意这里说的是“尽量分散”,因为如果集群只有两个可用区,三个副本无论如何都会有至少两个落在同一区,这是物理约束,不要硬凑。

节点亲和性和反亲和性也要提前想清楚。我们给中间件节点打了独立污点(Taint),同时给中间件 Pod 加了对应的 Toleration,这样其他业务 Pod 默认不会被调度到这些节点上,避免资源争抢。另外,同一中间件集群内的多个副本之间需要加上 PodAntiAffinity,避免两个副本调度到同一个节点,否则节点一挂,副本同时没了,高可用直接失效。

3. MySQL 在 K8s 上的高可用部署:Operator 配置与故障切换实测

3.1 最小可用的 MySQL 集群配置长什么样

我这边用的是某个基于 CRD 的 MySQL Operator,无论用哪家的,建议至少关注几个核心配置:副本数、存储类、root 密码来源、半同步复制开关、探针参数。下面是一个简化后的配置示例,关键字段已经做了脱敏处理:

apiVersion: mysql.xxx.io/v1 kind: MySQLCluster metadata: name: internal-mysql namespace: middleware spec: replicas: 3 version: "8.0.36" rootPasswordSecret: name: mysql-root-secret podPolicy: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: internal-mysql topologyKey: kubernetes.io/hostname resources: requests: cpu: "2" memory: 4Gi limits: cpu: "4" memory: 8Gi volumeClaimTemplates: - metadata: name: data spec: storageClassName: standard-cloud accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 200Gi replication: semiSync: true

副本数选 3,是因为要保证奇数个节点,在选主和防脑裂时更容易形成多数派。半同步复制semiSync: true的意思是,主库至少需要一个从库确认收到了 binlog,才向客户端返回事务提交成功。如果从库全部不可用,主库写入会被阻塞,这是为了数据安全主动牺牲可用性。这个行为在业务承受范围内时,建议打开。

探针参数很多人会忽略。Operator 默认的健康检查可能间隔太长,导致故障发现延迟。我们当时把 liveness 探针的initialDelaySeconds调成 30,periodSeconds调成 10,这样故障后能在较短时间内触发选主。但你也要注意,探针过于激进容易误杀正在处理大事务的主库,所以不能拍脑袋调小。

3.2 主从故障切换的实测过程

配置完成后,我们做了一个故障注入实验:直接把 MySQL 主库对应的 Pod 删掉,观察整个恢复链路。当时的主库是internal-mysql-1,删除后大约 8 秒,Operator 检测到主库失联,然后开始执行选主流程。从库中必须有至少一个追上主库的 binlog 位置,才有可能被选为新主,否则数据会丢。

从日志看,选主流程经历了几个阶段:先是对所有从库做数据一致性校验,然后选择一个 lag 最小的从库提升为主库,再更新集群状态,最后把指向旧主库的 Service 端点切换到新主库。整个流程大约 12 秒完成。虽然超过了我们 30 秒的 RTO 目标,但我已经比较满意了。如果想让恢复更快,可以调整 Operator 的masterFailoverTimeout之类的参数,但要注意太短可能引起不必要的切换。

这里踩过最大的坑是客户端连接。很多应用直接使用了主库的固定地址,例如mysql-1.mysql-internal.xxx,而不是通过无头服务自动发现。当主库切换后,应用依然连接旧 Pod 的 DNS,导致连接被拒。正确做法是使用一个指向当前主库的 Service 地址,Operator 会自动修改 Service 的后端端点。如果你的应用使用了外部连接池,还要确保连接池能快速感知断连并重建,否则切换期间会出现大量报错。

3.3 备份恢复和数据一致性校验

高可用不能只依赖副本。万一整个集群都不可用,或者有人误执行了DROP TABLE,副本也会把错误复制过去,这时候必须依靠备份恢复。我们当时的备份策略是每天凌晨做一次全量备份,同时持续采集 binlog。全量备份通过 Operator 创建 CronJob 来完成,binlog 则靠内置的采集组件传到对象存储。

恢复演练也要定期做,不能只备份不恢复。我们每个月会在一个临时命名空间里把全量备份恢复出来,再回放到某个时间点,然后用pt-table-checksum之类的工具对比源端和恢复端的数据。第一次恢复演练我们就发现,由于全量备份和 binlog 之间有时间空隙,恢复后丢了一部分数据。后来调整为“先做一次短暂锁表的全量备份,确保与 binlog 的起始位置对齐”,才解决这个问题。

有一个细节值得分享:在 K8s 上恢复 MySQL 备份时,如果直接用 Operator 创建一个新集群,再导入备份,要注意字符集和 sql_mode 配置是否与生产一致,否则导入过程可能报错或产生数据差异。我们当时在生产集群配置了utf8mb4和STRICT_TRANS_TABLES,恢复集群如果没有同步这个配置,导入阶段很容易被数据截断问题绊住。

4. Kafka 在 K8s 上的高可用部署:副本因子、分区分布与滚动重启

4.1 KRaft 还是 ZooKeeper:不同版本的模式差异

Kafka 在 3.x 版本开始引入 KRaft 模式,把原本依赖 ZooKeeper 的元数据管理收进 Kafka 自身,控制器(Controller)角色可以直接内嵌到 broker 进程里。我们在 K8s 上部署 Kafka 时,优先用了 KRaft 模式,理由很简单:少维护一套 ZooKeeper,就少一个故障点。如果你用的是比较老的 Kafka 2.x 版本,那还是需要单独部署 ZooKeeper 集群。

KRaft 模式下的 Controller 数量和 ZooKeeper 节点数量的推荐值类似,通常部署 3 个或 5 个。Controller 需要单独的节点身份,它们不承担业务流量,只负责分区 Leader 选举和元数据变更。K8s 上可以用一个单独的 StatefulSet 跑 Controller 角色,也可以把 Controller 和 Broker 合并在同一个进程里,我更建议分开部署,至少可以在故障时避免相互影响。

无论哪种模式,Kafka 在 K8s 上的网络身份都非常重要。每个 Broker 都需要被客户端识别为稳定的 DNS 地址。我见过有人直接把advertised.listeners配成 Pod IP,结果 Pod 重建后 IP 变了,客户端缓存了旧 IP,直接连接失败。正确做法是配置advertised.listeners使用broker-0.kafka.xxx.svc.cluster.local这类稳定的 DNS。

4.2 配置副本因子和最小 ISR 的细节

Kafka 的高可用可以从两个层面看:Topic 的副本因子(replication.factor)和 Topic 的最小 ISR 值(min.insync.replicas)。这两者不是一回事,很多人会把它们混在一起谈。

副本因子决定某个分区有几个副本分散在不同 broker 上。如果副本因子是 3,那么最多可以容忍一个副本所在 broker 彻底宕机,而不丢失分区数据。最小 ISR 决定“写入多少个副本成功后才算提交成功”。假如min.insync.replicas=2,那么当分区 Leader 和至少一个 Follower 都在线时,写入才是安全的。如果 ISR 收缩到只剩下 Leader 一个副本,写入会直接报NotEnoughReplicasException。

在 K8s 上,我们一般配置 broker 级默认default.replication.factor=3和min.insync.replicas=2。Producer 端必须设置acks=all,否则即使 broker 配置了最小 ISR,客户端也可能因为只写到了 Leader 就认为成功了。Topic 创建时要检查分区副本是不是均匀分布在所有 broker 上。我们曾经遇到过一次新加 broker 后没有重新平衡分区,导致某一台 broker 上的副本特别多,磁盘使用率比其他节点高出一截。

4.3 滚动重启和故障转移时的分区 Leader 重选举

K8s 上不可避免会做版本升级、节点维护,这时候需要滚动重启 Kafka。滚动重启的关键是:不能一次把多个 broker 同时停掉,否则某个分区的所有副本可能同时离线。我们当时的经验是,每次只允许一个 broker 下线,等它完全重新加入集群并追平 ISR 后,再操作下一个。

如果使用 Kafka Operator,它会生成一个滚动更新策略,把 broker 逐个替换。但我们还是会在手动维护节点时先看下分区状态,确认没有UnderReplicatedPartitions告警后再继续。节点维护时,最好先把该节点上的分区 Leader 安全迁移到其他 broker。这可以通过 Kafka 自带的重平衡工具完成,也可以依赖kafka-preferred-replica-election脚本。

我们实际观察过滚动重启期间的现象:重启一个 broker 后,它上面的大约几十个分区 Leader 会被重新选举到其他 broker,然后该 broker 重新加入并追平副本数据。这个过程会有短暂的消息延迟,但只要 ISR 中的副本没有大量同步离线,就不会丢消息。如果你发现某些分区一直处于LeaderNotAvailable状态,多半是网络或者存储问题导致新 Leader 无法正常启动,这时要优先看日志而不是盲目重启。

5. Redis 在 K8s 上的高可用部署:哨兵、集群和持久化的实战选择

5.1 哨兵模式部署时的网络身份识别问题

Redis 的高可用最常用的是哨兵模式:一个主节点,一个或多个从节点,再加至少三个哨兵进程。哨兵负责监控主从状态,当主节点不可达时,通过投票把某个从节点提升为主节点。这种模式在 K8s 上部署起来并不复杂,但有个很隐蔽的坑:哨兵之间通信依赖 IP 和端口,而 K8s 里 Pod IP 是动态的。

如果哨兵配置中写了固定的sentinel monitor mymaster <ip> 6379 2,一旦主库 Pod 重建,IP 变了,哨兵就无法监控到新的主库。正确做法是把哨兵监控的地址配成无头服务提供的稳定 DNS 名。例如:

sentinel monitor mymaster redis-master.middleware.svc.cluster.local 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 15000 sentinel parallel-syncs mymaster 1

注意redis-master这个地址必须能始终指向当前的主节点。这通常需要一个额外的 Service 来跟踪当前主节点,或者在初始化时手动指定。我们用的 Redis Operator 会在主从切换后自动更新这个 Service 的 Endpoint,这样哨兵可以始终通过同一个 DNS 名访问当前的主节点。如果你是自己写脚本部署,这个动态映射逻辑必须想清楚。

5.2 Redis Cluster 模式和服务发现之间的关系

当数据量较大时,单主节点可能成为瓶颈,这时候需要 Redis Cluster。Cluster 模式把 key 空间分成 16384 个槽,每个主节点负责一部分槽,每个主节点可以有从节点。在 K8s 上部署 Cluster,除了常规的 StatefulSet 外,最麻烦的是节点之间的总线通信(Bus Port)。

Redis Cluster 节点之间除了6379这样的数据端口,还会使用16379这样的总线端口。Pod 重建后 IP 变化,其他节点可能因为旧 IP 无法连接。解决方法是设置cluster-announce-ip、cluster-announce-port、cluster-announce-bus-port三个参数,让节点把可被外部访问的地址广播出去。在 K8s 上,一般使用无头服务的 DNS 名作为 announce IP。

有一点容易忽略:客户端连接 Redis Cluster 时,如果只用其中一个节点地址,客户端会从集群返回的 MOVED 重定向中学习到其他节点的地址。这些返回的地址同样来自 cluster-announce-配置。如果这里配的是 Pod IP,客户端在 Pod 重建后就会连接失败。所以必须确保客户端能找到稳定的服务入口,而不是依赖临时 IP。

5.3 缓存场景下的持久化与快速重建权衡

Redis 高可用并不等于持久化一定能保证。很多团队把 Redis 当缓存用,丢了可以回源加载,所以干脆关闭持久化。但如果你在 Redis 里存了必须保留的短时状态,比如分布式锁、秒杀库存、用户会话,那就要认真考虑持久化策略。

我们当时做了一个折中:开启 AOF 持久化,并使用appendfsync everysec策略。这样每秒落盘一次,极端情况下最多丢失 1 秒的数据,但不会像每次都 fsync 那样严重影响性能。同时开启 RDB 快照作为兜底,因为 AOF 文件在长时间运行时可能会很大,加载慢,RDB 适合用于快速恢复。

在 K8s 上,Redis 数据卷的扩容也是一个常被忽略的问题。如果你的 Redis 内存配置是 8GB,但 PVC 只有 10GB,之后如果想同时保留 RDB 和 AOF,很可能磁盘不够用。我们要把 Redis 的maxmemory和 PVC 容量配成合理的比例,一般建议预留 2 到 3 倍内存大小的存储空间,别只看当前内存占用。

6. 踩过的坑:从调度、网络到存储的完整排障链路

6.1 Pod 调度到同一节点引发的资源争抢

第一次部署 Kafka 时,我偷懒没有配置 PodAntiAffinity,结果三个 broker 中的两个被调度到了同一个节点。当时节点资源充足,也没有立刻看出问题。直到某个晚上那台节点因硬件报警被自动排水,两个 broker 同时离线,部分分区的 ISR 立刻收缩,业务侧开始出现生产超时。

排查链路是这样的:先看kafka-cluster状态,发现有UnderReplicatedPartitions;再看节点上的 Pod,确认两个 Kafka Pod 都在故障节点上;最后才意识到是调度策略的问题。修复方案是在 Kafka CR 中强制加上requiredDuringSchedulingIgnoredDuringExecution的 PodAntiAffinity,并且把 topologyKey 设置为kubernetes.io/hostname。这样两个 broker 绝对不会落到同一节点。MySQL 和 Redis 也需要同样处理。

6.2 StatefulSet 固定网络标识在节点维护时失效

另一个印象深刻的问题是节点维护导致的无头服务解析异常。当时我们要对底层节点做内核升级,节点上的 Pod 被重新调度。StatefulSet 的 Pod 网络标识理论上应该保持稳定,但有个 Redis 从节点在重建后一直无法被哨兵发现。

我看了日志,发现哨兵尝试连接从节点的旧 DNS,但解析出来的是新 IP,而从节点自身启动时广播的又是旧主机名。原因是我们使用的网络插件在某些节点变更场景下,DNS 记录更新有时间差,导致 Redis 节点在启动时缓存了旧的主机名映射。排查思路是先看redis-trib或集群节点的node.conf文件,确认里面记录的是 DNS 名还是 IP。

修复方法是在 Redis 配置里显式指定 announce-address,并且确保它使用 StatefulSet 提供的稳定 DNS 名。同时建议在节点维护前手动触发redis-cli cluster reset或重启相关从节点,避免旧记录残留。这个坑排查了将近两个小时,根源就是“稳定网络标识”在不同插件实现里并不是绝对稳定。

6.3 存储扩容与节点维护时的数据迁移问题

第三个坑来自存储。我们最初为 Kafka 使用的是本地卷,某天磁盘使用率接近 85%,于是我打算给 broker 扩磁盘。本地卷扩容不像云盘那么方便,因为 PV 和节点是绑定的,直接改 PVC 容量可能会失败。我们只能临时在集群里加一台新节点,把该 broker 上的分区迁走,才能停机换卷。

整个迁移过程比较难受。我先通过重平衡工具把受影响的分区迁移到其他 broker,然后确认该 broker 没有活跃分区后再关机修改本地卷。这个过程花了大概一个晚上,业务侧的流量抖动虽然不大,但运维压力不小。后来我把 Kafka 换成了支持在线扩容的存储方案,才彻底解决这个问题。MySQL 和 Redis 因为用的是云盘,扩容时基本没有遇到类似的阻塞。

这个经历给我的教训是:存储选型不仅要考虑性能,还要考虑你是否有把握在故障场景下完成数据迁移。如果你的中间件集群需要经常扩缩容,尽量选择可以动态扩容的存储方案,甚至要考虑未来的性能升级路径。

7. 高可用不是部署完就结束:故障演练和日常巡检

7.1 我建议的故障注入演练清单

有了高可用部署之后,如果不做故障演练,方案大概率在关键时刻掉链子。演练的目的不是证明系统不会挂,而是验证系统在真实故障下是否能按预期恢复。下面是我整理的一份最小演练清单,覆盖了三个中间件的常见故障场景:

故障类型演练方法预期结果
MySQL 主库挂掉删除主库 Pod从库自动提升,应用连接自动切换,RTO 在目标范围内
Kafka 某个 broker 掉线删除 broker Pod分区 Leader 自动重选举,消息不丢失,ISR 保持健康
Redis 主节点挂掉删除主节点 Pod哨兵完成故障转移,新主对外提供读写
节点失联给节点加上node.kubernetes.io/unreachable污点各中间件对应的 Pod 被驱逐后,数据卷能挂载到新节点
网络分区用网络策略阻断中间件节点之间通信集群不会发生脑裂,恢复后能重新同步数据

这里要特别强调网络分区演练。很多人会跳过这一项,因为很难模拟。但我建议至少在测试环境用网络策略限制中间件 Pod 之间的访问,观察集群是否会分裂成两个不平等的群体。尤其是 Redis 哨兵,如果多数派选主逻辑没有正确实现,网络恢复后很容易出现数据分叉。

7.2 关键监控指标与告警阈值参考

故障演练之外,日常监控才是发现隐患的主力。我这边为每个中间件都设置了独立的监控 Dashboard,重点指标和参考阈值如下:

中间件重点指标告警阈值参考
MySQL主从复制延迟、活动连接数、慢查询数量复制延迟 > 10 秒告警,连接数 > 80% 上限告警
Kafka离线分区数、ISR 收缩次数、请求延迟离线分区 > 0 立即告警,ISR 多次收缩告警
Redis内存使用率、命中率、持久化失败次数、主从同步延迟内存 > 80% 告警,同步延迟 > 30 秒告警

刚开始你可能不知道怎么定阈值,我的经验是先看一周基数值,再在基数值上乘以 1.5 到 2 作为告警线。比如 MySQL 主从复制延迟平时只有 1 秒,那 10 秒告警就比较合理。如果平时就在 5 秒,那 10 秒可能太敏感,频繁刷屏没有意义。

还有一个容易被忽略的指标是 PVC 容量使用率。中间件跑久了,binlog、消息日志、AOF 文件都会变大,如果 PVC 满了,轻则告警,重则整个节点直接不可用。我们给所有中间件 PVC 都加了容量监控,使用率达到 80% 时触发告警,并且设置了 85% 时自动扩容的流程。

7.3 日常巡检的关键时间点

最后聊聊日常巡检的节奏。高可用方案不是部署完放那就行,定期巡检能发现很多问题。我们当时的巡检分三档:每小时的自动巡检、每天的定时巡检、每周的手动检查。

每小时的自动巡检主要是检查中间件 Pod 的存活状态和基本健康指标,可以用 Prometheus 抓取并设置告警。每天的定时巡检要看一下备份是否成功、各自的存储空间使用率、是否有慢日志或者错误日志。每周的手动检查就需要人工登录集群,查看 Operator 的日志和中间件集群自身的元数据状态。

我个人的一个小习惯是:每周选择一个中间件,主动看一遍它最近的滚动更新事件和错误日志归档。很多问题其实在日志里早就有苗头,只是没人关注。比如 Kafka 的控制器近期频繁切换 Leader,虽然业务没有明显感知,但往往意味着某个节点存在不稳定因素,提前处理比故障后再排查要高效得多。这样的巡检习惯坚持下来,中间件集群的稳定性会显著提高。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询