3. 故障类型与影响
在分布式系统中,节点故障并非单一形态。Kubernetes、Cassandra、etcd 等集群系统之所以需要复杂的故障检测与自愈机制,正是因为故障的类型多样、表现隐蔽、影响范围各异。若不能准确区分故障类型,就无法选择合适的自愈策略——例如,将网络分区误判为节点宕机,可能导致集群脑裂或数据丢失。本节将系统梳理四类核心故障:硬件故障、软件故障、网络分区、资源耗尽,并分析其典型特征、检测信号与影响路径。
3.1 硬件故障:底层失效的“硬伤”
硬件故障指物理设备(CPU、内存、磁盘、网卡、电源、主板等)发生不可逆或间歇性失效。这类故障最直接的表现是节点失联或性能骤降,但不同硬件故障的检测信号差异显著:
- 磁盘故障:表现为 I/O 等待时间飙升、SMART 报错、文件系统只读。在 etcd 中,磁盘 fsync 延迟超过 500ms 会触发健康检查失败;在 Cassandra 中,磁盘故障可能导致 CommitLog 无法写入,节点直接拒绝服务。
- 内存故障:ECC 校验错误、频繁 OOM(Out Of Memory)、内核 panic。内存故障往往先表现为进程被杀死,而非节点整体宕机。
- 网卡故障:链路 down、丢包率上升、带宽骤降。网卡故障与网络分区容易混淆,但前者通常只影响单节点,后者影响一组节点间的连通性。
- 电源/主板故障:节点直接断电,心跳完全消失,是最容易检测但也最严重的一类。
检测要点:硬件故障通常通过 带外管理(IPMI/BMC) 与 节点心跳 双重机制确认。仅靠心跳超时不足以区分硬件故障与网络分区,需结合硬件传感器数据(温度、电压、风扇转速)综合判断。
3.2 软件故障:逻辑错误的“暗病”
软件故障比硬件故障更隐蔽,节点进程仍在运行、心跳正常,但服务逻辑已异常。常见类型包括:
- 死锁与活锁:goroutine 泄漏、分布式锁未释放,导致请求堆积但 CPU 占用不高。
- 内存泄漏:JVM 堆或 Go heap 持续增长,最终触发 OOM-Kill,表现为周期性重启。
- 配置错误:证书过期、连接池配置过小、超时参数不合理,导致间歇性故障。
- 版本兼容问题:滚动升级时新旧版本协议不兼容,节点间通信异常。
软件故障的检测难点在于:“进程活着”不等于“服务正常”。因此需要引入 深度健康检查(Deep Health Check),而不仅仅是 TCP 端口探测。例如,etcd 的 /health 端点会检查 Raft 选举状态;Kubernetes 的 livenessProbe 可以执行 HTTP 请求验证业务逻辑。
# 模拟软件故障:连接池耗尽
# 现象:服务端口正常,但新请求全部超时
$ ss -s
TCP: 已建立 1024 个连接(连接池上限 1024)
$ curl -v http://node1:8080/health
* connect to node1:8080 timed out
常见误区:仅依赖进程监控(如 systemd 状态)判断节点健康。许多软件故障中进程持续存活,但已无法处理新请求。必须配置业务层面的健康检查端点,并设置合理的失败阈值。
3.3 网络分区:最危险的“孤岛”故障
网络分区(Network Partition)指集群中部分节点之间网络不可达,但节点自身运行正常。这是分布式系统中最难处理的一类故障,因为它直接挑战 CAP 定理 的一致性前提。以 etcd 为例,5 节点集群若发生分区(3+2),多数派(3 个节点)仍可选举 Leader 继续服务,少数派(2 个节点)则无法写入,必须等待分区恢复。
网络分区的典型特征:
- 心跳超时,但节点 CPU、内存、磁盘均正常。
- 部分节点间 ping 通,部分不通(非对称分区)。
- 节点日志出现大量 “leader election timeout” 或 “connection refused”。
| 分区场景 | 多数派行为 | 少数派行为 | 自愈策略 |
|---|---|---|---|
| 3+2 分区(etcd) | 继续选举,正常读写 | 拒绝写入,只读 | 等待网络恢复,自动合并 |
| 2+2+1 分区(Cassandra) | 各分区独立服务 | 数据冲突风险 | 启用 hinted handoff 修复 |
| 全节点孤立 | 无 Leader | 集群不可用 | 人工介入,检查交换机/防火墙 |
3.4 资源耗尽:渐进式的“慢性病”
资源耗尽与硬件故障不同,它通常是渐进式的,给运维人员留出了响应时间,但也容易被忽视。核心资源包括 CPU、内存、磁盘空间、文件描述符(FD)、网络带宽等。以 Kubernetes 节点为例,当磁盘使用率超过 85% 时,kubelet 会开始驱逐 Pod;当内存压力达到阈值,系统会触发 OOM-Killer。
关键指标速查:
- 磁盘使用率 > 85%:触发驱逐;> 95%:节点标记
DiskPressure - 内存可用 < 100MB:触发系统 OOM-Kill
- 文件描述符耗尽:
too many open files错误 - CPU 负载长期 > 核数 × 1.5:请求排队严重
# 查看节点资源压力状态
$ kubectl describe node node2 | grep -A5 Pressure
Conditions:
MemoryPressure False
DiskPressure True # 磁盘空间不足
PIDPressure False
Ready True
资源耗尽的典型影响路径是:资源不足 → 健康检查失败 → 节点标记 NotReady → Pod 被驱逐 → 集群负载转移 → 其他节点资源压力上升,形成雪崩效应。因此,资源耗尽的自愈不能仅靠节点重启,必须配合 资源配额(ResourceQuota) 和 水平自动伸缩(HPA) 从源头治理。
3.5 故障对比与自愈策略选择
面对不同类型的故障,自愈系统需要采取差异化的响应策略。下表总结了四类故障的关键判别维度与推荐自愈动作:
| 故障类型 | 判别信号 | 误判风险 | 推荐自愈策略 |
|---|---|---|---|
| 硬件故障 | IPMI 告警 + 心跳丢失 | 低(信号明确) | 节点隔离、Pod 迁移、硬件更换 |
| 软件故障 | 进程存活但健康检查失败 | 高(易漏判) | 滚动重启、回滚版本、动态调整参数 |
| 网络分区 | 部分节点互通,部分不通 | 极高(易误判为宕机) | 等待多数派恢复、启用 ReadOnly 模式 |
| 资源耗尽 | 监控指标持续增长 | 中(渐进式) | 驱逐低优先级 Pod、扩容节点、清理日志 |
实践建议:在自愈系统设计时,不要将所有故障都映射到“重启节点”这一动作。网络分区时重启少数派节点可能加剧脑裂;资源耗尽时盲目重启会导致雪崩。正确的做法是建立 故障分类树,根据判别信号逐层下钻,匹配最精确的自愈策略。
3.6 案例复盘:一次误判的教训
某生产集群(5 节点 etcd + 3 节点 Kubernetes Master)曾出现如下故障:
- 现象:节点 A 和 B 心跳丢失,kubelet 标记 NotReady。
- 初步判断:运维人员认为是硬件宕机,直接重启节点 A、B。
- 实际原因:交换机端口故障导致 A、B 与 C、D、E 之间网络分区,但 A、B 之间互通。
- 后果:重启后 A、B 重新加入集群,但由于 etcd 多数派(C、D、E)已选举新 Leader,A、B 的数据版本落后,触发大量数据同步,集群写入延迟飙升 5 倍。
复盘结论:如果当时先执行 etcdctl endpoint health 检查各节点连通性,或观察交换机端口状态,就能识别出网络分区而非硬件故障。正确的处理应是:保持少数派节点在线但不参与写入,等待网络自动恢复,而非盲目重启。