可靠性工程:不只是"不出故障"

可靠性工程的目标不是追求零故障——那既不现实也不经济。真正的目标是:在故障不可避免的前提下,让系统具备快速发现、自动恢复、持续学习的能力。

Google SRE 提出的核心公式:

MTTR << MTBF / (MTBF + MTTR) × (1 - SLO)

这个公式揭示了一个关键事实:当故障间隔(MTBF)远大于修复时间(MTTR)时,系统的可用性自然趋近于 SLO 目标。因此,可靠性工程的发力方向是双重的——延长无故障时间(提升 MTBF)缩短故障恢复时间(降低 MTTR)

可靠性层级模型

参考 CMMI 能力成熟度模型的思想,可靠性工程同样存在层级递进关系。从低到高分为五个层级:

层级特征典型表现
L1 被动响应故障发生后人工处理告警轰炸 → 人工排查 → 手动恢复
L2 监控覆盖关键指标可观测仪表盘完备,告警有阈值,但恢复仍靠人
L3 自动化恢复常见故障自动处理健康检查自动重启、HPA 自动扩容
L4 主动预防提前发现风险压测、容量规划、混沌工程主动注入故障
L5 自愈系统闭环自适应故障自动感知 → 诊断 → 恢复 → 学习

大部分团队的真实水平在 L2 到 L3 之间。从 L3 到 L4 的跨越是最关键的——它意味着从"等故障来"转变为"主动找故障"。L5 自愈是理想态,也是持续演进的方向。

层级跃迁的关键

  • L1 → L2:建设可观测性体系(指标、日志、链路追踪)。
  • L2 → L3:引入自动化恢复机制(Kubernetes 健康检查、HPA、自动故障转移)。
  • L3 → L4:引入混沌工程,主动验证系统的弹性。
  • L4 → L5:建设自愈闭环,将人工经验沉淀为自动化策略。

每一层级的跨越都需要对应的技术投入和组织能力建设,不能跳级。

MTTR/MTBF 优化策略

降低 MTTR:让故障恢复更快

MTTR(Mean Time To Repair,平均修复时间)由四个子阶段构成:

MTTR = MTTD + MTTRI + MTTF + MTTRR
       检测    识别    定位    修复

每个阶段的优化手段:

MTTD(检测时间)

  • 完善告警覆盖:基于 SLO 的告警而非固定阈值,避免告警疲劳。
  • 黑盒监控:从用户视角探测服务可用性(如 Prometheus Blackbox Exporter)。
  • 黄金信号:聚焦延迟、流量、错误、饱和度四大黄金信号。

MTTRI(识别时间)

  • 告警关联与去重:同一故障可能触发数十条告警,需要聚合关联。
  • 统一观测平台:指标、日志、链路追踪一体化,快速跳转上下文。
  • 变更关联:故障发生时自动关联近期变更(“最近改了什么?")。

MTTF(定位时间)

  • 分布式链路追踪:Jaeger/Tempo 追踪请求链路,快速定位瓶颈节点。
  • 结构化日志:统一 JSON 格式日志,支持字段级检索。
  • 诊断 Runbook:将常见故障的排查步骤固化为可执行 Runbook。

MTTRR(修复时间)

  • 一键回滚:代码和配置变更支持一键回滚。
  • 自动故障转移:数据库主从切换、服务实例自动摘除。
  • 预热与缓存:冷启动场景提前预热,减少恢复延迟。

提升 MTBF:让故障发生更少

提升 MTBF 的本质是减少变更引入的故障

  • 灰度发布:小流量验证,控制爆炸半径(详见变更管理:灰度发布与回滚策略)。
  • 代码评审与自动化测试:CI 流水线集成单元测试、集成测试、安全扫描。
  • 容量规划:基于历史趋势预测资源需求,提前扩容避免容量故障。
  • 依赖治理:定期审计依赖版本,及时修复安全漏洞和已知 Bug。
  • 预案演练:定期进行故障演练,验证预案有效性。

故障注入与混沌工程

为什么需要混沌工程

分布式系统的故障是必然的——网络会抖动、磁盘会满、节点会宕机、依赖会超时。混沌工程的核心思想是:与其等故障在生产环境自然发生,不如主动、可控地注入故障,提前发现系统的脆弱点。

Netflix 是混沌工程的开创者,其 Chaos Monkey 工具会在生产环境随机杀掉实例,强制团队保障服务的冗余和容错能力。Principles of Chaos 定义了混沌工程的四条原则:

  1. 假设稳态行为(定义系统正常运行的指标)。
  2. 假设故障场景(多样化真实世界故障)。
  3. 在生产环境实验(真实流量、真实环境)。
  4. 自动化持续运行(集成到 CI/CD)。

Chaos Mesh 实践

Chaos Mesh 是 CNCF 孵化的混沌工程平台,原生支持 Kubernetes,提供了丰富的故障注入类型。

安装 Chaos Mesh

# 创建命名空间
kubectl create ns chaos-testing

# 使用 Helm 安装
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm install chaos-mesh chaos-mesh/chaos-mesh \
  -n chaos-testing \
  --set chaosDaemon.runtime=containerd \
  --set dashboard.create=true

网络延迟注入实验

模拟服务间网络延迟,验证超时重试机制是否生效:

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: api-server-network-delay
  namespace: chaos-testing
spec:
  action: delay          # 故障类型:网络延迟
  mode: all              # 目标 Pod 选择模式
  selector:
    namespaces:
    - production
    labelSelectors:
      "app": "api-server"
  delay:
    latency: "200ms"     # 注入 200ms 延迟
    correlation: "50"
    jitter: "50ms"       # 添加抖动
  duration: "5m"         # 持续 5 分钟
  scheduler:
    cron: "@every 1h"    # 每小时执行一次

Pod 故障注入实验

模拟 Pod 被随机杀掉,验证副本集和自动恢复能力:

apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: api-server-pod-kill
  namespace: chaos-testing
spec:
  action: pod-kill       # 故障类型:杀掉 Pod
  mode: one              # 每次杀一个 Pod
  selector:
    namespaces:
    - production
    labelSelectors:
      "app": "api-server"
  scheduler:
    cron: "@every 10m"   # 每 10 分钟杀一个 Pod

磁盘压力注入实验

模拟磁盘 IO 压力,验证服务在高 IO 场景下的表现:

apiVersion: chaos-mesh.org/v1alpha1
kind: IOChaos
metadata:
  name: api-server-disk-pressure
  namespace: chaos-testing
spec:
  action: latency        # 故障类型:IO 延迟
  mode: all
  selector:
    namespaces:
    - production
    labelSelectors:
      "app": "api-server"
  volumePath: "/data"
  delay: "100ms"         # IO 操作延迟 100ms
  percent: 50            # 50% 的 IO 操作受影响
  duration: "3m"

实验工作流:串联多故障场景

Chaos Mesh 支持 Workflow 将多个实验串联,模拟更真实的故障链:

apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
  name: resilience-test-workflow
  namespace: chaos-testing
spec:
  entry: main
  templates:
  - name: main
    templateType: Serial
    children:
    - network-delay-phase
    - pod-kill-phase
    - recovery-check
  - name: network-delay-phase
    templateType: NetworkChaos
    networkChaos:
      action: delay
      mode: all
      selector:
        namespaces: ["production"]
        labelSelectors:
          "app": "api-server"
      delay:
        latency: "300ms"
      duration: "3m"
  - name: pod-kill-phase
    templateType: PodChaos
    podChaos:
      action: pod-kill
      mode: one
      selector:
        namespaces: ["production"]
        labelSelectors:
          "app": "api-server"
  - name: recovery-check
    templateType: Serial
    children:
    - verify-slo
  - name: verify-slo
    templateType: Task
    task:
      container:
        image: curlimages/curl:latest
        command:
        - /bin/sh
        - -c
        - |
          # 检查 SLO 是否满足:成功率 > 99.9%
          SUCCESS_RATE=$(curl -s http://prometheus:9090/api/v1/query \
            --data-urlencode 'query=sum(rate(http_requests_total{service="api-server",code!~"5.."}[5m]))/sum(rate(http_requests_total{service="api-server"}[5m]))' \
            | jq -r '.data.result[0].value[1]')
          echo "Current success rate: $SUCCESS_RATE"
          if (( $(echo "$SUCCESS_RATE < 0.999" | bc -l) )); then
            echo "SLO violated! Need investigation."
            exit 1
          fi          

这个 Workflow 模拟了一个完整的韧性测试流程:先注入网络延迟,再杀掉 Pod,最后自动检查 SLO 是否仍然满足。如果 SLO 被打破,实验会标记失败,提示需要加固系统。

混沌工程的落地节奏

混沌工程不能一上来就在生产环境"放火”。推荐按以下节奏推进:

  1. 测试环境验证:先在非生产环境跑通实验,验证故障注入本身安全可控。
  2. 工作时间小范围实验:在业务低峰期、工作时间内进行小规模实验,团队随时待命。
  3. 常态化自动化运行:实验稳定后,纳入 CI/CD 流水线或定时任务,持续验证。
  4. 游戏日(Game Day):定期组织全团队参与的故障演练,模拟大规模故障场景。

自愈系统设计

自愈系统是可靠性工程的终极目标——让系统在无人干预的情况下自动从故障中恢复。一个完整的自愈闭环包含四个环节:

健康检查 → 自动诊断 → 自动恢复 → 反馈学习

第一层:健康检查(感知层)

健康检查是自愈的"感觉神经"。Kubernetes 提供三类探针:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  template:
    spec:
      containers:
      - name: api-server
        livenessProbe:          # 存活探针:失败则重启容器
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
          failureThreshold: 3
        readinessProbe:         # 就绪探针:失败则摘除流量
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        startupProbe:           # 启动探针:慢启动应用专用
          httpGet:
            path: /healthz
            port: 8080
          failureThreshold: 30
          periodSeconds: 10

三个探针的协作关系:

  • startupProbe 优先执行,保护慢启动应用在就绪前不被 livenessProbe 杀掉。
  • readinessProbe 控制流量:通过则接收流量,失败则从 Endpoints 中摘除。
  • livenessProbe 控制生命周期:失败则 kubelet 重启容器。

第二层:自动重启(恢复层)

当健康检查失败时,Kubernetes 会自动重启容器。但单纯重启不够,需要配合策略:

spec:
  template:
    spec:
      containers:
      - name: api-server
        restartPolicy: Always    # Pod 级别重启策略
        lifecycle:
          preStop:               # 优雅终止
            exec:
              command:
              - /bin/sh
              - -c
              - "sleep 15 && curl -X POST http://localhost:8080/deregister"
      terminationGracePeriodSeconds: 30

对于 Pod 反复 CrashLoopBackOff 的场景,需要更高层级的处理:

# 使用 Kubernetes Event Driven Automation (KEDA) 或自定义控制器
# 当 Pod 连续重启超过阈值时,触发告警并执行预设恢复动作
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: crash-loop-alert
  namespace: production
spec:
  groups:
  - name: pod-health
    rules:
    - alert: PodCrashLooping
      expr: |
        rate(kube_pod_container_status_restarts_total[15m]) > 0
        and
        kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1        
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Pod {{ $labels.pod }} is in CrashLoopBackOff"
        runbook: "https://wiki.example.com/runbooks/crash-loop"

第三层:自动扩容(弹性层)

当流量激增导致资源不足时,HPA(Horizontal Pod Autoscaler)自动扩容:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-server-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-server
  minReplicas: 3
  maxReplicas: 50
  metrics:
  # CPU 使用率驱动扩容
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  # 自定义指标驱动:基于 QPS 扩容
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "1000"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0    # 扩容立即响应
      policies:
      - type: Percent
        value: 100                      # 每次最多翻倍
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300   # 缩容延迟 5 分钟,避免抖动
      policies:
      - type: Percent
        value: 10                       # 每次最多缩 10%
        periodSeconds: 60

第四层:流量切换(故障转移层)

当整个节点或可用区故障时,需要更高层级的自动故障转移:

# 使用 Kubernetes Pod Disruption Budget 保障最小可用副本
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-server-pdb
  namespace: production
spec:
  minAvailable: 2          # 自愿驱逐时至少保留 2 个副本
  selector:
    matchLabels:
      app: api-server
---
# 多可用区部署,避免单点故障
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 6
  template:
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: api-server
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: api-server
              topologyKey: kubernetes.io/hostname

自愈闭环的编排

将以上四层串联起来,一个完整的自愈流程如下:

1. 健康检查探测异常(readinessProbe 失败)
   → Pod 自动从 Endpoints 摘除,流量不再进入

2. 容器级恢复(livenessProbe 失败)
   → kubelet 自动重启容器

3. 实例级恢复(Pod 反复失败)
   → 控制器重新调度到其他节点

4. 容量级恢复(流量激增)
   → HPA 自动扩容增加副本数

5. 节点级恢复(节点故障)
   → Pod 自动重新调度 + 反亲和性分散部署

6. 可用区级恢复(可用区故障)
   → 多可用区部署 + 流量自动切换

每一层都有明确的触发条件和恢复动作,形成层层递进的防御纵深。

可靠性度量仪表盘

没有度量就没有管理。可靠性仪表盘是将可靠性"可视化"的关键工具。

核心指标体系

一个完整的可靠性仪表盘应包含以下指标维度:

可用性指标

  • SLO 达成率(30 天滚动窗口)
  • 错误预算消耗速率
  • 服务可用性百分比

性能指标

  • P50 / P95 / P99 延迟趋势
  • 吞吐量(QPS)
  • 错误率(按错误码分类)

弹性指标

  • MTTR(平均修复时间)
  • MTBF(平均故障间隔时间)
  • 故障频率
  • 自动恢复成功率

容量指标

  • 资源利用率(CPU / 内存 / 磁盘 / 网络)
  • 容量余量
  • HPA 扩缩容事件

Grafana 仪表盘配置示例

以下是一个基于 Prometheus + Grafana 的可靠性仪表盘核心 PromQL 查询:

// 1. SLO 达成率(30天滚动窗口)
1 - (
  sum(rate(http_requests_total{service="api-server",code=~"5.."}[30d]))
  /
  sum(rate(http_requests_total{service="api-server"}[30d]))
)

// 2. 错误预算消耗速率(每小时)
(
  sum(rate(http_requests_total{service="api-server",code=~"5.."}[1h]))
  /
  sum(rate(http_requests_total{service="api-server"}[1h]))
) / 0.001 * 100

// 3. P99 延迟
histogram_quantile(0.99,
  sum(rate(http_request_duration_seconds_bucket{service="api-server"}[5m])) by (le)
)

// 4. MTTR(基于告警恢复时间)
avg_over_time(
  (time() - alertmanager_resolved_timestamp{alertname=~".*"})[30d:1h]
)

// 5. 当前可用副本数 vs 期望副本数
sum(kube_deployment_status_replicas_available{deployment="api-server"})
/
sum(kube_deployment_spec_replicas{deployment="api-server"})

仪表盘设计原则

设计可靠性仪表盘时,遵循以下原则:

  1. 分层展示:顶部是全局健康概览(红/黄/绿),中间是服务级 SLO,底部是详细指标。一目了然,逐层下钻。
  2. 时间维度对比:每个指标都展示当前值与历史趋势的对比,能看出"是在变好还是变差"。
  3. 告警与度量联动:仪表盘上标注告警事件时间点,方便关联指标变化。
  4. 面向受众定制:管理层看 SLO 达成率和错误预算,工程师看延迟分布和错误明细,运维看资源利用率和扩缩容事件。

总结

可靠性工程是一个系统工程,不是单点技术的堆砌。从被动响应到自愈系统,每一步跃迁都需要技术能力、流程机制和组织文化的协同演进。

核心要点回顾:

  • 可靠性是设计出来的,不是运维出来的——架构层面考虑冗余、隔离、降级,远比事后补救有效。
  • 混沌工程是验证可靠性的唯一可靠方式——不主动找故障,故障就会主动找你。
  • 自愈是渐进式的——从健康检查到自动扩容再到故障转移,层层递进,不要试图一步到位。
  • 度量驱动改进——MTTR、MTBF、SLO 达成率是可靠性工程的指南针,持续度量、持续改进。

记住:系统的可靠性上限不是由做得最好的部分决定的,而是由最薄弱的环节决定的。 可靠性工程的价值,正是持续找到并加固这些薄弱环节。

参考

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Chaos Mesh — Chaos Mesh 项目,参考了Chaos Mesh相关内容
  2. Chaos Mesh 官方文档 — Chaos Mesh 项目,参考了Chaos Mesh 官方文档相关内容
  3. Kubernetes Horizontal Pod Autoscaler — Kubernetes 官方,参考了Kubernetes Horizontal Pod Autoscaler相关内容
  4. Principles of Chaos — 混沌工程社区,参考了Principles of Chaos相关内容
  5. Google SRE Book - Monitoring Distributed Systems — Google SRE 团队,参考了Google SRE Book - Monitoring Distributed Systems相关内容
  6. The Four Golden Signals — Google SRE 团队,参考了The Four Golden Signals相关内容