在现代分布式系统中,服务数量多、链路长、依赖复杂,传统 “救火式” 运维已经无法满足业务对稳定性的要求。SRE(Site Reliability Engineering,站点可靠性工程)和混沌工程(Chaos Engineering)作为两种互补的实践方法论,分别从 主动防御 和 主动进攻 两个维度构建系统的韧性。本文将从 SRE 文化、SLI/SLO/SLA 体系、错误预算、监控告警、混沌工程原理到 Chaos Mesh / Litmus / Chaos Monkey 三大工具实战,完整覆盖生产级可靠性建设路径。
一、SRE 文化与核心原则
1.1 SRE 是什么
SRE 由 Google 在 2003 年提出,核心理念是:用软件工程的方法解决运维问题。SRE 团队不是传统意义上的 “运维人员”,而是兼具开发能力的可靠性工程师。他们的工作围绕以下几个核心原则展开:
- 拥抱故障(Embrace Failure):承认系统会出故障,关键不是零故障,而是快速恢复和降低影响
- 自动化优先(Automate Everything):任何人工操作超过两次就应该自动化
- 错误预算(Error Budget):允许系统在一定范围内出错,用预算驱动发布节奏
- 监控一切(Monitor Everything):没有监控的系统就是黑盒,无法做可靠决策
- 简化复杂度(Reduce Toil):消除重复性、无差异化的人工劳动
1.2 开发与 SRE 的关系
在传统组织中,开发和运维往往是对立的:开发希望快速发布新功能,运维希望系统稳定少变更。SRE 通过 错误预算 机制将两者统一:
// 错误预算协调函数示例
// 当错误预算充足时,允许加快发布速度
// 当错误预算耗尽时,暂停非紧急发布
func ShouldAllowRelease(errorBudgetRemaining float64) bool {
// 错误预算低于 20% 时冻结发布
if errorBudgetRemaining < 0.2 {
return false
}
// 错误预算低于 50% 时要求增加 Canary 比例
if errorBudgetRemaining < 0.5 {
return true // 但会触发额外审批流程
}
return true
}
// 获取当前错误预算状态
func GetErrorBudgetStatus(slo float64, actualAvailability float64, windowDays int) *BudgetStatus {
// 计算当前可用性
budgetUsed := 1.0 - (actualAvailability / slo)
return &BudgetStatus{
SLI: slo,
Actual: actualAvailability,
BudgetUsed: budgetUsed,
BudgetRemaining: 1.0 - budgetUsed,
WindowDays: windowDays,
}
}
type BudgetStatus struct {
SLI float64
Actual float64
BudgetUsed float64
BudgetRemaining float64
WindowDays int
}
这种机制让开发团队在追求速度的同时必须承担稳定性责任,而 SRE 则通过工程手段提供工具、平台和规范,而非简单做 “守门员”。
二、SLI / SLO / SLA 定义与计算
2.1 三者的区别
| 术语 | 全称 | 定义 | 示例 |
|---|---|---|---|
| SLI | Service Level Indicator | 服务等级指标,用于衡量服务健康度的具体指标 | 请求延迟 P99 < 200ms |
| SLO | Service Level Objective | 服务等级目标,SLI 在特定时间窗口内应达到的目标值 | 一个月内 99.9% 的请求延迟 < 200ms |
| SLA | Service Level Agreement | 服务等级协议,面向客户的法律承诺,违约需赔偿 | 可用性低于 99.9% 时赔付代金券 |
关键关系:SLI 是测量值,SLO 是内部目标,SLA 是外部承诺。通常 SLA 会比 SLO 宽松,留出缓冲空间。
2.2 SLI 的选择方法
选择 SLI 时应遵循 用户旅程 原则,关注用户真正在意的体验指标:
- 可用性:服务是否可响应(请求成功率)
- 延迟:响应速度(P50 / P95 / P99)
- 吞吐量:处理容量(QPS / 每分钟请求数)
- 错误率:失败请求占比(HTTP 5xx 比例)
Google SRE Book 推荐的 四大黄金信号(Four Golden Signals) 就是基于此:
- 延迟(Latency):服务处理请求所需时间
- 流量(Traffic):系统承载的请求量
- 错误(Errors):请求失败的比例
- 饱和度(Saturation):资源使用接近满载的程度
# Prometheus 记录规则:四大黄金信号 SLI
# 保存到 prometheus-rules/golden-signals.yml
groups:
- name: golden_signals
rules:
# 1. 可用性 SLI:请求成功率
- record: sli:availability:ratio_rate5m
expr: |
sum(rate(http_requests_total{status=~"2..|3.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# 2. 延迟 SLI:P99 延迟
- record: sli:latency:p99_rate5m
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
# 3. 错误率 SLI
- record: sli:error:ratio_rate5m
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# 4. 饱和度 SLI:CPU 使用率
- record: sli:saturation:cpu_rate5m
expr: |
avg(irate(node_cpu_seconds_total{mode="idle"}[5m]))
# 5. 流量 SLI:每秒请求数
- record: sli:traffic:qps_rate5m
expr: |
sum(rate(http_requests_total[5m]))
2.3 SLO 的具体计算
设定 SLO 需要明确三个要素:指标定义、目标值、时间窗口。
#!/usr/bin/env python3
# slo_calculator.py — SLO 计算与错误预算推导工具
class SLOCalculator:
def __init__(self, target: float, window_days: int):
"""
初始化 SLO 计算器
:param target: SLO 目标值,如 0.999 表示 99.9%
:param window_days: 计算窗口,如 30 天
"""
self.target = target
self.window_days = window_days
# 月度总分钟数
self.total_minutes = window_days * 24 * 60
def calculate_error_budget_minutes(self) -> float:
"""计算月度允许的停机分钟数(错误预算)"""
error_rate = 1.0 - self.target
return self.total_minutes * error_rate
def calculate_error_budget_requests(self, total_requests: int) -> int:
"""基于总请求数计算允许的错误请求数"""
error_rate = 1.0 - self.target
return int(total_requests * error_rate)
def status(self, actual_availability: float) -> dict:
"""检查 SLO 状态"""
budget_remaining = self.target - (1.0 - actual_availability)
return {
"slo_target": f"{self.target*100:.3f}%",
"actual": f"{actual_availability*100:.3f}%",
"budget_remaining_pct": f"{budget_remaining*100:.3f}%",
"is_met": actual_availability >= self.target
}
# 使用示例
if __name__ == "__main__":
# 99.9% SLO,30 天窗口
calc = SLOCalculator(target=0.999, window_days=30)
print(f"30 天总分钟数: {calc.total_minutes}")
print(f"99.9% SLO 允许的停机: {calc.calculate_error_budget_minutes():.2f} 分钟")
print(f"99.9% SLO 允许的停机: {calc.calculate_error_budget_minutes()/60:.2f} 小时")
# 对比不同 SLO 级别
for target in [0.9, 0.99, 0.999, 0.9999]:
c = SLOCalculator(target, 30)
mins = c.calculate_error_budget_minutes()
print(f"SLO {target*100:>6.2f}% — 允许停机 {mins:>10.2f} 分钟 ({mins/60:>7.3f} 小时)")
运行结果:
30 天总分钟数: 43200
99.9% SLO 允许的停机: 43.20 分钟
99.9% SLO 允许的停机: 0.72 小时
SLO 90.00% — 允许停机 4320.00 分钟 ( 72.000 小时)
SLO 99.00% — 允许停机 432.00 分钟 ( 7.200 小时)
SLO 99.90% — 允许停机 43.20 分钟 ( 0.720 小时)
SLO 99.99% — 允许停机 4.32 分钟 ( 0.072 小时)
2.4 常见 SLO 等级参考
| 服务等级 | 可用性目标 | 月度允许停机 | 适用场景 |
|---|---|---|---|
| 基础服务 | 99.9% | 43.2 分钟 | 内部工具、非核心后台 |
| 标准服务 | 99.95% | 21.6 分钟 | 一般业务系统 |
| 重要服务 | 99.99% | 4.32 分钟 | 核心交易、支付链路 |
| 关键服务 | 99.999% | 26 秒 | 金融核心、医疗急救 |
三、错误预算策略
3.1 错误预算的核心思想
错误预算是 SRE 最具革命性的概念之一。它的逻辑是:如果服务要求 99.9% 可用性,那 0.1% 的 “错误额度” 就是预算。当预算充足时可以大胆发布,当预算耗尽时必须停止变更直到恢复。
这种机制实现了三个目标:
- 统一目标:开发和 SRE 对 “可接受的故障” 有了一致的量化标准
- 数据驱动发布:不再凭感觉决定能否发布,而是看错误预算余额
- 减少无效争论:当出现故障时,用预算消耗情况评估影响
3.2 错误预算政策设计
# 错误预算政策配置示例
# 保存到 policies/error-budget-policy.yml
error_budget_policy:
# 30 天滚动窗口
window: "30d"
# 各等级服务的 SLO 与对应策略
tiers:
critical:
slo: 99.99
# 预算消耗比例 -> 采取措施
actions:
- threshold: 0.75 # 消耗 75%
action: "增加 Canary 验证时间至 24 小时"
severity: "warning"
- threshold: 1.0 # 全部消耗
action: "暂停所有非紧急发布,启动专题复盘"
severity: "critical"
standard:
slo: 99.9
actions:
- threshold: 0.5
action: "发布需 SRE 审批"
severity: "info"
- threshold: 1.0
action: "冻结发布两周"
severity: "critical"
internal:
slo: 99.0
actions:
- threshold: 1.0
action: "周会通报"
severity: "warning"
# 异常加速消耗检测
burn_rate_alerts:
# 1 天内消耗 2% 预算(按正常速度 30 倍)
fast_burn:
multiplier: 30
window: "1d"
action: "立即通知 on-call 工程师"
# 3 天内消耗 5% 预算(按正常速度 10 倍)
medium_burn:
multiplier: 10
window: "3d"
action: "创建高优先级工单"
3.3 预算消耗的监控告警
# Alertmanager 规则:错误预算消耗告警
# 保存到 alertmanager-rules/error-budget.yml
groups:
- name: error_budget_alerts
rules:
# 快速消耗:1 天消耗了 30 天预算的 2% = 加速 30 倍
- alert: ErrorBudgetFastBurn
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[1d]))
/
sum(rate(http_requests_total[1d]))
) > 2 * (
1 - sum(slo_target)
)
for: 5m
labels:
severity: critical
team: sre
annotations:
summary: "错误预算快速消耗中"
description: "服务 {{ $labels.service }} 的错误预算正在以 30 倍速度消耗"
action: "立即调查近期变更或异常流量"
# 预算耗尽预警
- alert: ErrorBudgetExhausted
expr: error_budget_remaining_percent < 0
for: 1m
labels:
severity: critical
team: sre
annotations:
summary: "错误预算已耗尽"
description: "服务 {{ $labels.service }} 30 天错误预算已耗尽,冻结非紧急发布"
四、监控与告警设计:黄金信号实践
4.1 告警分层体系
生产环境的告警应该分层设计,避免噪音和遗漏:
# 告警分层配置示例(Grafana Alerting)
# 保存到 grafana/alerts/layered-alerts.yml
apiVersion: 1
groups:
- orgId: 1
name: layered_alerting
folder: SRE
interval: 30s
rules:
# ====== 第 1 层:页面级告警(Page)======
# 用户直接影响,需要立即人工响应
- uid: page-latency-p99
title: "P99 延迟超过 SLO"
condition: C
data:
- refId: A
relativeTimeRange:
from: 300
to: 0
datasourceUid: prometheus
model:
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
) > 0.5
refId: A
- refId: C
relativeTimeRange:
from: 0
to: 0
datasourceUid: __expr__
model:
type: threshold
expression: A
conditions:
- evaluator:
type: gt
params: [0.5]
noDataState: NoData
execErrState: Error
for: 5m
annotations:
severity: "page"
runbook_url: "https://wiki/runbooks/high-latency"
# ====== 第 2 层:工单级告警(Ticket)======
# 趋势异常,需要当天处理
- uid: ticket-error-rate-trend
title: "错误率呈上升趋势"
condition: C
data:
- refId: A
datasourceUid: prometheus
model:
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[1h]))
/
sum(rate(http_requests_total[1h]))
) > 0.001
refId: A
for: 30m
annotations:
severity: "ticket"
action: "创建工单,上班后排查"
# ====== 第 3 层:信息级(Info)======
# 供分析和报告使用
- uid: info-budget-weekly
title: "周度错误预算报告"
condition: C
data:
- refId: A
datasourceUid: prometheus
model:
expr: |
(1 - sli:availability:ratio_rate7d) / (1 - slo_target)
refId: A
for: 1h
annotations:
severity: "info"
action: "自动发送到周会报告"
4.2 SLO 监控看板
{
"dashboard": {
"title": "SRE SLO Dashboard",
"panels": [
{
"title": "可用性合规状态",
"type": "stat",
"targets": [
{
"expr": "sli:availability:ratio_rate30d",
"legendFormat": "{{service}} — 30天可用性"
}
],
"fieldConfig": {
"defaults": {
"thresholds": {
"steps": [
{"color": "red", "value": 0},
{"color": "yellow", "value": 0.999},
{"color": "green", "value": 0.9995}
]
}
}
}
},
{
"title": "错误预算消耗趋势",
"type": "timeseries",
"targets": [
{
"expr": "(1 - sli:availability:ratio_rate30d) / (1 - 0.999)",
"legendFormat": "{{service}} 预算消耗比"
}
]
},
{
"title": "四大黄金信号",
"type": "graph",
"targets": [
{"expr": "sli:traffic:qps_rate5m", "legendFormat": "流量(QPS)"},
{"expr": "sli:latency:p99_rate5m * 1000", "legendFormat": "P99 延迟(ms)"},
{"expr": "sli:error:ratio_rate5m * 100", "legendFormat": "错误率(%)"},
{"expr": "100 - sli:saturation:cpu_rate5m * 100", "legendFormat": "CPU 使用率(%)"}
]
}
]
}
}
五、混沌工程基础
5.1 混沌工程的定义
混沌工程是在分布式系统上进行 受控实验 的学科,通过有意注入故障来验证系统在各种异常条件下的行为,从而提前发现弱点并增强系统韧性。
Netflix 提出的混沌工程原则:“混沌工程是在分布式系统上进行实验的学科,目的是建立对系统承受生产环境中湍流条件能力的信心。”
5.2 五大核心原则
建立稳态假设(Build a Hypothesis around Steady State Behavior)
- 先定义系统正常运行时的可观测指标(如 QPS、错误率、延迟)
多样化真实世界事件(Vary Real-world Events)
- 故障类型应覆盖硬件故障、网络异常、依赖失效、流量激增等真实场景
生产环境运行(Run Experiments in Production)
- 唯有在生产环境才能观察到真实的系统行为和用户影响
自动化持续运行(Automate Experiments to Run Continuously)
- 混沌实验不应是一次性的手工活动,而应集成到 CI/CD 流程中
最小化爆炸半径(Minimize Blast Radius)
- 通过金丝雀、流量分割、快速回滚等手段控制实验影响范围
5.3 实验安全护栏
#!/usr/bin/env python3
# chaos_guardrails.py — 混沌实验安全保护机制
from dataclasses import dataclass
from enum import Enum
from typing import Optional, List
class ExperimentStatus(Enum):
PENDING = "pending"
RUNNING = "running"
PAUSED = "paused"
ROLLEDBACK = "rolled_back"
COMPLETED = "completed"
@dataclass
class AbortCondition:
"""实验自动终止条件"""
metric: str # 监控指标名称
threshold: float # 触发阈值
duration_sec: int # 持续超过阈值多长时间后终止
@dataclass
class BlastRadius:
"""爆炸半径控制"""
max_affected_percent: float # 最大影响流量百分比
target_service: str # 目标服务
excluded_pods: List[str] # 排除的关键 Pod(如 leader)
namespace: str # 限制在特定命名空间
class ChaosGuardrail:
"""混沌实验安全控制器"""
def __init__(
self,
blast_radius: BlastRadius,
abort_conditions: List[AbortCondition],
experiment_duration_sec: int = 300,
auto_rollback: bool = True
):
self.blast_radius = blast_radius
self.abort_conditions = abort_conditions
self.experiment_duration = experiment_duration_sec
self.auto_rollback = auto_rollback
self.status = ExperimentStatus.PENDING
def check_pre_conditions(self, current_metrics: dict) -> bool:
"""实验前检查:确保系统处于健康状态"""
# 检查当前错误率是否已偏高
if current_metrics.get("error_rate", 0) > 0.01:
print("错误率已高于 1%,跳过本次实验")
return False
# 检查是否处于业务高峰
if current_metrics.get("qps", 0) > current_metrics.get("peak_qps", 1) * 0.8:
print("当前流量接近峰值,推迟实验")
return False
# 检查是否有未恢复的告警
if current_metrics.get("active_alerts", 0) > 0:
print("存在活跃告警,终止实验")
return False
return True
def should_abort(self, metrics: dict) -> Optional[str]:
"""运行时检查:判断是否触发终止条件"""
for condition in self.abort_conditions:
value = metrics.get(condition.metric)
if value is not None and value > condition.threshold:
return (
f"终止条件触发: {condition.metric}={value} "
f"超过阈值 {condition.threshold}"
)
return None
def validate_blast_radius(self, selected_targets: List[str]) -> bool:
"""验证爆炸半径是否超出许可范围"""
total_pods = self._get_total_pods(self.blast_radius.target_service)
affected_percent = len(selected_targets) / max(total_pods, 1)
if affected_percent > self.blast_radius.max_affected_percent:
print(
f"影响范围 {affected_percent:.1%} 超出限制 "
f"{self.blast_radius.max_affected_percent:.1%}"
)
return False
# 检查是否排除了关键 Pod
for excluded in self.blast_radius.excluded_pods:
if excluded in selected_targets:
print(f"选中了被排除的 Pod: {excluded}")
return False
print(f"爆炸半径验证通过: 影响 {len(selected_targets)}/{total_pods} Pod")
return True
def _get_total_pods(self, service: str) -> int:
# 实际实现会查询 Kubernetes API
return 10
# 使用示例:网络延迟实验的安全配置
if __name__ == "__main__":
guardrail = ChaosGuardrail(
blast_radius=BlastRadius(
max_affected_percent=0.10, # 最多影响 10% 的实例
target_service="payment-service",
excluded_pods=["payment-0"], # 排除主节点
namespace="production"
),
abort_conditions=[
# 错误率超过 5% 立即终止
AbortCondition("error_rate", 0.05, 10),
# P99 延迟超过 2 秒持续 30 秒终止
AbortCondition("latency_p99", 2.0, 30),
# 订单成功率低于 95% 终止
AbortCondition("order_success_rate", 0.95, 60),
],
experiment_duration_sec=300, # 实验运行 5 分钟
auto_rollback=True
)
print("混沌实验安全护栏已配置")
print(f"自动回滚: {guardrail.auto_rollback}")
print(f"实验时长: {guardrail.experiment_duration} 秒")
六、Chaos Mesh 实战
Chaos Mesh 是 PingCAP 开源的云原生混沌工程平台,专为 Kubernetes 设计,支持丰富的故障注入类型和友好的 Web UI。
6.1 环境准备
# 安装 Chaos Mesh(使用 Helm)
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
# 安装到 chaos-mesh 命名空间
helm install chaos-mesh chaos-mesh/chaos-mesh \
--namespace chaos-mesh \
--create-namespace \
--set dashboard.create=true \
--set chaosDaemon.runtime=containerd \
--set chaosDaemon.socketPath=/run/containerd/containerd.sock
# 确认安装
kubectl get pods -n chaos-mesh
6.2 Pod 故障注入实验
# 实验 1:随机杀死 Pod(模拟实例故障)
# 保存到 chaos-experiments/pod-kill.yml
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: pod-kill-payment
namespace: chaos-mesh
spec:
action: pod-kill
mode: one
selector:
namespaces:
- production
labelSelectors:
app: payment-service
scheduler:
cron: "@every 10m" # 每 10 分钟执行一次
duration: "30s" # 故障持续 30 秒
---
# 实验 2:Pod 网络分区(模拟网络隔离)
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-partition-order-db
namespace: chaos-mesh
spec:
action: partition
mode: one
selector:
namespaces:
- production
labelSelectors:
app: order-service
direction: both
target:
selector:
namespaces:
- production
labelSelectors:
app: order-db
mode: one
duration: "3m"
6.3 网络延迟与丢包实验
# 实验 3:注入网络延迟(模拟跨区域网络抖动)
# 保存到 chaos-experiments/network-delay.yml
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-delay-api
namespace: chaos-mesh
spec:
action: delay
mode: fixed-percent
value: "50" # 影响 50% 的目标实例
selector:
namespaces:
- production
labelSelectors:
app: api-gateway
delay:
latency: "200ms" # 增加 200ms 延迟
correlation: "75" # 75% 的相关性(抖动时序关联)
jitter: "50ms" # 随机抖动范围
duration: "5m"
---
# 实验 4:网络丢包(模拟弱网环境)
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-loss-redis
namespace: chaos-mesh
spec:
action: loss
mode: all
selector:
namespaces:
- production
labelSelectors:
app: cache-service
loss:
loss: "10" # 10% 丢包率
correlation: "50"
duration: "3m"
6.4 CPU 和内存压力测试
# 实验 5:CPU 压力测试
# 保存到 chaos-experiments/stress-cpu.yml
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
name: cpu-stress-inventory
namespace: chaos-mesh
spec:
mode: fixed-percent
value: "33" # 影响 33% 的目标
selector:
namespaces:
- production
labelSelectors:
app: inventory-service
stressors:
cpu:
workers: 4 # 4 个 worker 线程
load: 80 # 每个核心负载 80%
options: ["--cpu-load-slice", "10"]
duration: "5m"
---
# 实验 6:内存压力测试
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
name: memory-stress-search
namespace: chaos-mesh
spec:
mode: one
selector:
namespaces:
- production
labelSelectors:
app: search-service
stressors:
memory:
workers: 2
size: "512MB" # 每个 worker 分配 512MB
options: ["--mem-stride-size", "4096"]
duration: "3m"
6.5 使用工作流编排复杂实验
# 实验 7:多步骤混沌工作流
# 保存到 chaos-experiments/workflow-cascade.yml
apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
name: cascade-failure-test
namespace: chaos-mesh
spec:
entry: entry
templates:
- name: entry
templateType: Serial
deadline: 15m
children:
- delay-before # 等待 2 分钟
- kill-payment-pod # 先杀掉支付 Pod
- delay-middle # 等待 1 分钟
- delay-inventory-network # 再添加库存服务网络延迟
- verify-recovery # 验证恢复
- name: delay-before
templateType: Schedule
deadline: 2m
schedule:
type: Cron
cron: "0/1 * * * * *"
concurrencyPolicy: Forbid
- name: kill-payment-pod
templateType: PodChaos
podChaos:
action: pod-kill
mode: fixed-percent
value: "25"
selector:
namespaces: [production]
labelSelectors:
app: payment-service
- name: delay-middle
templateType: Schedule
deadline: 1m
- name: delay-inventory-network
templateType: NetworkChaos
networkChaos:
action: delay
mode: one
selector:
namespaces: [production]
labelSelectors:
app: inventory-service
delay:
latency: "500ms"
target:
mode: one
selector:
namespaces: [production]
labelSelectors:
app: inventory-db
- name: verify-recovery
templateType: AWSChaos
awsChaos:
action: stop-count
secretName: "cloud-key-secret"
awsRegion: "ap-northeast-1"
ec2Instance: "i-1234567890abcdef0"
七、Litmus 混沌实验
Litmus 是 CNCF 沙箱项目,采用 Kubernetes CRD 定义混沌实验,强调声明式配置和 GitOps 集成。
7.1 安装 Litmus
# 安装 Litmus Control Plane
kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v3.0.0.yaml
# 创建混沌服务账号
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: litmus-admin
namespace: litmus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: litmus-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: litmus-admin
subjects:
- kind: ServiceAccount
name: litmus-admin
namespace: litmus
EOF
7.2 Litmus 实验定义
# 保存到 litmus-experiments/pod-delete-experiment.yml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: payment-chaos
namespace: litmus
spec:
appinfo:
appns: 'production'
applabel: 'app=payment-service'
appkind: 'deployment'
# 停止条件:如果辅助应用 app-check 失败则停止
auxiliaryAppInfo: ''
engineState: 'active'
chaosServiceAccount: litmus-admin
# 监控:使用 prometheus 进行监控
monitoring: true
# 按顺序执行实验
annotationCheck: 'true'
experiments:
- name: pod-delete
spec:
components:
env:
# 随机杀死 Pod
- name: TARGET_CONTAINER
value: ''
# 杀掉随机 Pod
- name: PODS_AFFECTED_PERC
value: '50'
# 总 Chaos 持续时间
- name: TOTAL_CHAOS_DURATION
value: '60'
# 混沌间隔
- name: CHAOS_INTERVAL
value: '10'
- name: FORCE
value: 'false'
- name: LIB
value: 'litmus'
nodeSelector: {}
experimentImagePullSecrets: []
---
# 保存到 litmus-experiments/network-latency-experiment.yml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: network-latency-chaos
namespace: litmus
spec:
appinfo:
appns: 'production'
applabel: 'app=api-gateway'
appkind: 'deployment'
engineState: 'active'
chaosServiceAccount: litmus-admin
annotationCheck: 'true'
experiments:
- name: network-latency
spec:
components:
env:
# 接口名称(空表示所有网卡)
- name: NETWORK_INTERFACE
value: 'eth0'
# 延迟时间(毫秒)
- name: NETWORK_LATENCY
value: '2000'
# 目标端口(空表示所有端口)
- name: TARGET_PORT
value: ''
# 受影响的 Pod 百分比
- name: PODS_AFFECTED_PERC
value: '100'
# 总混沌时长
- name: TOTAL_CHAOS_DURATION
value: '120'
# 混沌间隔
- name: CHAOS_INTERVAL
value: '10'
- name: LIB
value: 'litmus'
- name: LIB_IMAGE
value: 'litmuschaos/go-runner:3.0.0'
7.3 Litmus 工作流与 Probe
Litmus 支持在实验中插入 Probe(探针) 进行健康检查,确保实验在安全范围内运行。
# 保存到 litmus-experiments/probe-health-check.yml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: probe-example
namespace: litmus
spec:
appinfo:
appns: 'production'
applabel: 'app=cart-service'
appkind: 'deployment'
engineState: 'active'
chaosServiceAccount: litmus-admin
experiments:
- name: pod-cpu-hog
spec:
probe:
# 实验前探针:确保服务健康才进行
- name: "health-check-before"
type: "httpProbe"
mode: "Edge"
runProperties:
probeTimeout: "5s"
retry: 2
interval: "5s"
initialiseTimeout: "5s"
httpProbe/inputs:
url: "http://cart-service.production.svc.cluster.local:8080/health"
insecureSkipVerify: false
method:
get:
criteria: "=="
responseCode: "200"
# 持续探针:实验中持续检查
- name: "order-success-during"
type: "cmdProbe"
mode: "Continuous"
runProperties:
probeTimeout: "5s"
retry: 1
interval: "5s"
cmdProbe/inputs:
command: "curl -s -o /dev/null -w '%{http_code}' http://cart-service.production.svc.cluster.local:8080/cart/add"
source: "inline"
comparator:
type: "string"
criteria: "equals"
value: "200"
# 实验后探针:确认恢复
- name: "recovery-check"
type: "promProbe"
mode: "Edge"
runProperties:
probeTimeout: "10s"
retry: 3
interval: "5s"
promProbe/inputs:
endpoint: "http://prometheus.monitoring.svc.cluster.local:9090"
query: "up{job='cart-service'}"
comparator:
type: "float"
criteria: ">"
value: "0"
components:
env:
- name: CPU_CORES
value: '1'
- name: PODS_AFFECTED_PERC
value: '50'
- name: TOTAL_CHAOS_DURATION
value: '120'
八、Chaos Monkey 与 AWS 故障注入
8.1 Chaos Monkey 简介
Chaos Monkey 是 Netflix 最早开源的混沌工程工具,核心理念很简单:随机终止生产环境的虚拟机实例。虽然功能看似简单,但它验证了分布式系统的核心假设——单点故障不应影响整体可用性。
8.2 Chaos Monkey 配置(Spinnaker 集成)
{
"jsonNET": "chaos-monkey.json",
"description": "Chaos Monkey 配置 - AWS EC2 生产环境",
"config": {
"enabled": true,
"regions": ["us-east-1", "us-west-2"],
"probability": 1.0,
"schedule": "0 9-17 * * 1-5",
"exceptions": [
{
"type": "cluster",
"value": "production-critical-payment-v001",
"reason": "金融核心集群,受监管要求限制"
},
{
"type": "cluster",
"value": "production-db-primary-v001",
"reason": "数据库主节点"
}
],
"mandatoryGroup": {
"name": "default",
"probability": 1.0,
"grouping": "cluster"
},
"leashed": false
}
}
8.3 AWS Fault Injection Simulator (FIS)
AWS 官方提供了故障注入服务 FIS,可以安全地在 AWS 基础设施上进行混沌实验。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS FIS 实验模板 — EC2 实例终止 + 网络延迟",
"Resources": {
"ChaosExperimentTemplate": {
"Type": "AWS::FIS::ExperimentTemplate",
"Properties": {
"Description": "终止 20% 的 API 服务实例并注入网络延迟",
"RoleArn": {
"Fn::GetAtt": ["FISRole", "Arn"]
},
"StopConditions": [
{
"Source": "cloudwatch-alarms",
"Value": {
"Fn::Join": [",", [
{"Ref": "ErrorRateAlarm"},
{"Ref": "LatencyAlarm"}
]]
}
}
],
"Tags": {
"Experiment": "api-resilience-test",
"Team": "sre"
},
"Targets": {
"APIInstances": {
"ResourceType": "aws:ec2:instance",
"SelectionMode": "PERCENT(20)",
"Filters": [
{
"Path": "State.Name",
"Values": ["running"]
},
{
"Path": "Tags[?Key=='Service']",
"Values": ["api-gateway"]
}
]
}
},
"Actions": {
"TerminateInstances": {
"ActionId": "aws:ec2:stop-instances",
"Description": "停止选中的 EC2 实例",
"Targets": {
"Instances": "APIInstances"
},
"StartAfter": ["NetworkLatency"]
},
"NetworkLatency": {
"ActionId": "aws:ssm:send-command",
"Description": "注入网络延迟",
"Parameters": {
"documentArn": "arn:aws:ssm:::document/AWSFIS-Run-Network-Latency",
"documentParameters": {
"DurationSeconds": "300",
"DelayMilliseconds": "200",
"Interface": "eth0"
},
"maxDuration": "PT5M"
},
"Targets": {
"Instances": "APIInstances"
}
}
},
"LogConfiguration": {
"CloudWatchLogsConfiguration": {
"LogGroupArn": {
"Fn::Join": ["", [
"arn:aws:logs:",
{"Ref": "AWS::Region"}, ":",
{"Ref": "AWS::AccountId"},
":log-group:/aws/fis/experiments:*"
]]
}
},
"LogSchemaVersion": 2
}
}
}
}
}
8.4 使用 AWS CLI 启动实验
#!/bin/bash
# run-fis-experiment.sh — 启动 AWS FIS 故障注入实验
EXPERIMENT_TEMPLATE_ID="EXTxxxxx12345"
REGION="us-east-1"
# 检查是否存在告警
ACTIVE_ALARMS=$(aws cloudwatch describe-alarms \
--alarm-names "API-ErrorRate" "API-LatencyP99" \
--state-value ALARM \
--region $REGION \
--query 'MetricAlarms | length(@)')
if [ "$ACTIVE_ALARMS" -gt 0 ]; then
echo "存在活跃告警,取消实验"
exit 1
fi
# 启动实验
echo "启动故障注入实验..."
EXPERIMENT=$(aws fis start-experiment \
--experiment-template-id $EXPERIMENT_TEMPLATE_ID \
--region $REGION \
--tags Team=SRE,Environment=Production)
EXPERIMENT_ID=$(echo $EXPERIMENT | jq -r '.experiment.id')
echo "实验已启动,ID: $EXPERIMENT_ID"
# 等待实验完成(最长 10 分钟)
echo "监控实验状态..."
for i in {1..60}; do
STATUS=$(aws fis get-experiment \
--id $EXPERIMENT_ID \
--region $REGION \
--query 'experiment.state.status' \
--output text)
echo "当前状态: $STATUS"
if [ "$STATUS" == "completed" ] || [ "$STATUS" == "stopped" ]; then
echo "实验结束"
break
fi
if [ "$STATUS" == "failed" ]; then
echo "实验失败,检查日志"
aws fis get-experiment \
--id $EXPERIMENT_ID \
--region $REGION
exit 1
fi
sleep 10
done
echo "实验执行完成"
九、故障注入代码实战
9.1 应用层网络延迟注入
#!/usr/bin/env python3
# fault_injection_middleware.py — Flask 混沌工程中间件
import random
import time
import logging
from functools import wraps
from flask import Flask, request, jsonify
app = Flask(__name__)
logger = logging.getLogger(__name__)
class ChaosMiddleware:
"""Flask 混沌工程中间件"""
def __init__(self, app, enabled: bool = False):
self.app = app
self.enabled = enabled
self.latency_injection_ms = 0
self.error_rate = 0.0
self.cpu_spike = False
# 注册请求钩子
self.app.before_request(self.before_request)
self.app.after_request(self.after_request)
def before_request(self):
"""请求前注入故障"""
if not self.enabled:
return
# 1. 注入网络延迟
if self.latency_injection_ms > 0:
# 只影响特定百分比的请求
if random.random() < 0.3: # 30% 的请求受影响
delay = self.latency_injection_ms / 1000.0
jitter = random.uniform(-0.05, 0.05) * delay
actual_delay = max(0, delay + jitter)
logger.warning(
f"[混沌] 注入延迟 {actual_delay*1000:.0f}ms "
f"到 {request.endpoint}"
)
time.sleep(actual_delay)
# 2. 注入错误响应
if self.error_rate > 0 and random.random() < self.error_rate:
logger.error(f"[混沌] 注入错误到 {request.endpoint}")
return jsonify({"error": "chaos_injected_error", "code": 503}), 503
def after_request(self, response):
"""响应后处理"""
if self.enabled:
response.headers['X-Chaos-Enabled'] = 'true'
return response
def set_latency(self, ms: int):
"""设置延迟注入量(毫秒)"""
self.latency_injection_ms = ms
logger.info(f"混沌延迟设置为 {ms}ms")
def set_error_rate(self, rate: float):
"""设置错误率(0.0 ~ 1.0)"""
self.error_rate = rate
logger.info(f"混沌错误率设置为 {rate:.1%}")
def enable(self):
self.enabled = True
logger.info("混沌工程已启用")
def disable(self):
self.enabled = False
logger.info("混沌工程已禁用")
# 初始化中间件
chaos = ChaosMiddleware(app, enabled=False)
@app.route('/health')
def health():
return jsonify({"status": "healthy", "chaos": chaos.enabled})
@app.route('/chaos/control', methods=['POST'])
def control_chaos():
"""混沌控制接口(仅内部使用)"""
data = request.get_json() or {}
if 'enabled' in data:
if data['enabled']:
chaos.enable()
else:
chaos.disable()
if 'latency_ms' in data:
chaos.set_latency(data['latency_ms'])
if 'error_rate' in data:
chaos.set_error_rate(data['error_rate'])
return jsonify({
"status": "ok",
"chaos": {
"enabled": chaos.enabled,
"latency_ms": chaos.latency_injection_ms,
"error_rate": chaos.error_rate
}
})
@app.route('/api/order', methods=['POST'])
def create_order():
"""模拟下单接口"""
data = request.get_json()
# 正常业务逻辑...
return jsonify({
"order_id": f"ORD-{random.randint(10000, 99999)}",
"status": "created"
})
if __name__ == '__main__':
# 示例:启用混沌工程
# chaos.enable()
# chaos.set_latency(200)
# chaos.set_error_rate(0.05)
app.run(host='0.0.0.0', port=5000)
9.2 gRPC 服务故障注入拦截器
// chaos_interceptor.go — gRPC 混沌工程拦截器
package main
import (
"context"
"math/rand"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ChaosConfig 定义混沌注入配置
type ChaosConfig struct {
Enabled bool // 是否启用
LatencyDelay time.Duration // 注入延迟
ErrorRate float64 // 错误率 (0.0 - 1.0)
AffectedRatio float64 // 受影响请求比例
}
// UnaryChaosInterceptor 一元拦截器注入故障
func UnaryChaosInterceptor(config *ChaosConfig) grpc.UnaryServerInterceptor {
return func(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
// 未启用则直接通过
if !config.Enabled {
return handler(ctx, req)
}
// 按比例决定是否影响当前请求
if rand.Float64() > config.AffectedRatio {
return handler(ctx, req)
}
// 注入延迟
if config.LatencyDelay > 0 {
jitter := time.Duration(rand.Int63n(int64(config.LatencyDelay / 10)))
actualDelay := config.LatencyDelay + jitter - config.LatencyDelay/20
time.Sleep(actualDelay)
}
// 注入错误
if config.ErrorRate > 0 && rand.Float64() < config.ErrorRate {
return nil, status.Errorf(
codes.Unavailable,
"chaos injected: service temporarily unavailable",
)
}
return handler(ctx, req)
}
}
// StreamChaosInterceptor 流式拦截器注入故障
func StreamChaosInterceptor(config *ChaosConfig) grpc.StreamServerInterceptor {
return func(
srv interface{},
ss grpc.ServerStream,
info *grpc.StreamServerInfo,
handler grpc.StreamHandler,
) error {
if !config.Enabled {
return handler(srv, ss)
}
// 流式请求注入延迟(在第一条消息时)
if config.LatencyDelay > 0 {
time.Sleep(config.LatencyDelay)
}
if config.ErrorRate > 0 && rand.Float64() < config.ErrorRate {
return status.Errorf(codes.Internal, "chaos injected stream error")
}
return handler(srv, ss)
}
}
// 使用示例
func main() {
chaosConfig := &ChaosConfig{
Enabled: true,
LatencyDelay: 100 * time.Millisecond,
ErrorRate: 0.02, // 2% 错误率
AffectedRatio: 0.5, // 50% 请求受影响
}
server := grpc.NewServer(
grpc.UnaryInterceptor(UnaryChaosInterceptor(chaosConfig)),
grpc.StreamInterceptor(StreamChaosInterceptor(chaosConfig)),
)
_ = server
}
9.3 数据库连接故障注入
// ChaosDataSource.java — 数据源代理故障注入
package com.example.chaos;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class ChaosDataSource extends HikariDataSource {
private volatile boolean chaosEnabled = false;
private volatile double connectionFailureRate = 0.0;
private volatile long connectionDelayMs = 0;
private final Random random = new Random();
public void enableChaos(double failureRate, long delayMs) {
this.chaosEnabled = true;
this.connectionFailureRate = failureRate;
this.connectionDelayMs = delayMs;
}
public void disableChaos() {
this.chaosEnabled = false;
}
@Override
public Connection getConnection() throws SQLException {
// 注入连接延迟
if (chaosEnabled && connectionDelayMs > 0 && random.nextDouble() < 0.3) {
try {
long actualDelay = connectionDelayMs + random.nextInt(50);
Thread.sleep(actualDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 注入连接失败
if (chaosEnabled && random.nextDouble() < connectionFailureRate) {
throw new SQLException(
"Chaos injected: connection refused"
);
}
return super.getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return getConnection();
}
}
十、事故响应与事后复盘
10.1 事故响应流程
生产事故发生时,清晰的分级响应流程比技术能力更重要。
# 事故响应流程定义
# 保存到 incident-response/playbook.yml
incident_response:
# 严重等级定义
severity_levels:
sev1:
description: "关键业务完全中断,大量用户受影响"
response_minutes: 5 # 5 分钟内必须响应
war_room: true # 需要成立作战室
executive_notify: true # 通知高管
postmortem_required: true # 必须写复盘报告
sla: "99.999%"
sev2:
description: "核心功能降级,部分用户受影响"
response_minutes: 15
war_room: true
executive_notify: false
postmortem_required: true
sla: "99.99%"
sev3:
description: "非核心功能异常,影响有限"
response_minutes: 30
war_room: false
executive_notify: false
postmortem_required: false
sla: "99.9%"
sev4:
description: "轻微问题,无用户影响"
response_minutes: 120
war_room: false
executive_notify: false
postmortem_required: false
sla: "99%"
# 响应阶段
phases:
- name: "发现 (Detection)"
steps:
- "确认告警是否真实(降噪)"
- "评估影响范围(用户、区域、功能)"
- "根据影响确定严重等级"
- "在事故通道中发布公告"
- name: "响应 (Response)"
steps:
- "on-call 工程师接手指挥权"
- "如需要升级为 sev1/sev2,立即组建 war room"
- "启动相关通知(用户、客服、高管)"
- "执行回滚或缓解措施"
- name: "恢复 (Recovery)"
steps:
- "验证系统是否恢复正常"
- "确认所有依赖服务健康"
- "保持监控,确保不会反复"
- "通知相关方事故已解决"
- name: "复盘 (Post-Mortem)"
steps:
- "24 小时内完成初版时间线"
- "72 小时内完成完整复盘文档"
- "召开无追责复盘会议"
- "创建可执行的改进项并分配 owner"
10.2 事后复盘模板
# 事后复盘报告模板
## [日期] [服务名] 事故复盘
### 基本信息
- **事故编号**: INC-2026-09-01-001
- **发现时间**: 2026-09-01 14:32 CST
- **持续时间**: 18 分钟
- **严重等级**: sev2
- **影响范围**: 支付接口延迟升高,约 15% 用户下单受阻
- **错误预算消耗**: 23%(月度)
### 时间线(精确到分钟)
| 时间 | 事件 | 操作人 |
|------|------|--------|
| 14:32 | 告警触发:P99 延迟超过 1s | 自动化 |
| 14:33 | on-call 工程师(张三)收到页面 | PagerDuty |
| 14:35 | 确认是订单服务 Pod 异常重启 | 张三 |
| 14:38 | 启动回滚到上一个版本 | 张三 |
| 14:45 | 服务恢复正常 | 张三 |
| 14:50 | 初步根因:新版本数据库连接池配置错误 | 张三 |
### 根因分析(5 Whys)
1. **为什么支付延迟高?** 订单服务响应变慢
2. **为什么订单服务变慢?** Pod 频繁重启导致请求排队
3. **为什么 Pod 频繁重启?** OOMKilled,内存超限
4. **为什么内存超限?** 新版本将连接池 maxSize 从 50 改为 200
5. **为什么配置被错误修改?** PR 评审时未关注配置变更,缺少配置变更检查清单
### 影响评估
- 受影响请求数:约 12,000 次
- 用户投诉:23 条
- 直接收入影响:估计 ¥3,500
- 错误预算消耗:23%
### 应对措施(已执行)
- 回滚到稳定版本 v2.3.1
- 扩容订单服务至 10 个实例
### 改进项
| 改进项 | Owner | 优先级 | 截止时间 | 状态 |
|--------|-------|--------|----------|------|
| 增加数据库连接池配置校验规则 | 李四 | P0 | 2026-09-08 | 待办 |
| PR 模板增加配置变更检查清单 | 王五 | P1 | 2026-09-15 | 待办 |
| 增加 OOM 告警阈值 | 张三 | P1 | 2026-09-10 | 待办 |
| 混沌工程增加连接池满载场景 | 赵六 | P2 | 2026-09-30 | 待办 |
### 经验总结
- 配置变更和代码变更一样危险,需要同等级别的审查
- OOM 场景需要在混沌工程中覆盖
- 错误预算消耗 23%,本月剩余发布风险升高
10.3 自动化复盘数据收集
#!/usr/bin/env python3
# incident_reporter.py — 自动化生成事故时间线
import requests
from datetime import datetime, timedelta
from dataclasses import dataclass
from typing import List
@dataclass
class TimelineEvent:
timestamp: datetime
source: str # 来源:alert / log / trace / manual
description: str
severity: str
class IncidentReporter:
def __init__(self, prometheus_url: str, alertmanager_url: str):
self.prometheus_url = prometheus_url
self.alertmanager_url = alertmanager_url
def generate_timeline(
self,
service: str,
start: datetime,
end: datetime
) -> List[TimelineEvent]:
"""根据时间范围自动生成事故时间线"""
timeline = []
# 1. 获取 Prometheus 告警记录
alerts = self._get_alerts_in_range(service, start, end)
for alert in alerts:
timeline.append(TimelineEvent(
timestamp=alert['startsAt'],
source='alert',
description=f"告警触发: {alert['labels']['alertname']}",
severity=alert['labels'].get('severity', 'unknown')
))
# 2. 获取异常日志
logs = self._get_error_logs(service, start, end)
for log in logs:
timeline.append(TimelineEvent(
timestamp=log['timestamp'],
source='log',
description=f"异常日志: {log['message'][:100]}",
severity='error'
))
# 3. 获取部署记录
deployments = self._get_deployments(service, start, end)
for deploy in deployments:
timeline.append(TimelineEvent(
timestamp=deploy['timestamp'],
source='deployment',
description=f"部署: {deploy['version']} by {deploy['user']}",
severity='info'
))
# 按时间排序
timeline.sort(key=lambda x: x.timestamp)
return timeline
def _get_alerts_in_range(self, service: str, start: datetime, end: datetime):
"""从 Alertmanager 获取告警"""
try:
resp = requests.get(
f"{self.alertmanager_url}/api/v1/alerts",
params={
'filter': f'service="{service}"',
'start': start.isoformat(),
'end': end.isoformat()
}
)
return resp.json().get('data', [])
except Exception as e:
print(f"获取告警失败: {e}")
return []
def _get_error_logs(self, service: str, start: datetime, end: datetime):
"""从 Loki 获取错误日志"""
# 实际实现调用 Loki API
return []
def _get_deployments(self, service: str, start: datetime, end: datetime):
"""获取部署历史"""
# 实际实现调用 Argo CD / GitLab API
return []
def print_timeline(self, timeline: List[TimelineEvent]):
"""打印格式化时间线"""
print("\n=== 自动生成的事故时间线 ===\n")
for event in timeline:
time_str = event.timestamp.strftime("%Y-%m-%d %H:%M:%S")
print(f"[{time_str}] [{event.source:12}] [{event.severity:8}] {event.description}")
print("\n" + "="*60 + "\n")
# 使用示例
if __name__ == "__main__":
reporter = IncidentReporter(
prometheus_url="http://prometheus.monitoring.svc.cluster.local",
alertmanager_url="http://alertmanager.monitoring.svc.cluster.local"
)
end = datetime.now()
start = end - timedelta(hours=2)
timeline = reporter.generate_timeline("order-service", start, end)
reporter.print_timeline(timeline)
FAQ
Q1:错误预算耗尽后还能发布吗?
不建议。错误预算的设计目的就是用数据约束发布决策。当预算耗尽时,说明系统在当前周期内已经无法承受更多风险。此时正确做法是:暂停非紧急发布,集中精力修复已知稳定性问题,等到新周期预算重置后再恢复正常节奏。紧急安全补丁通常作为例外流程处理,但需要 SRE 负责人和 VP 级别共同审批。
Q2:混沌工程会不会导致真实故障?
任何生产环境实验都存在风险,但混沌工程通过三大机制将风险降至最低:(1)爆炸半径控制,只影响一小部分流量或实例;(2)自动终止条件,当监控指标超过阈值时实验自动停止;(3)演练时段选择,避开业务高峰期和重大活动期间。实践证明,规范执行的混沌工程导致的故障远少于它预防的故障。
Q3:初创公司有必要做 SRE 和混沌工程吗?
有必要,但实施方式应匹配公司阶段。初创公司团队规模小,可以采用以下轻量方案:使用云厂商的托管监控(如 CloudWatch / APM),设定简单的 SLO(如 99.9% 可用性),利用 AWS FIS 或开源 Chaos Mesh 做简单的 Pod 终止实验。关键是先建立 “可观测 + 可恢复” 的基础意识,再随规模扩大逐步完善体系。
Q4:SLA 和 SLO 差别多大合适?
通常建议 SLA 比 SLO 宽松半个到一个数量级。例如内部 SLO 设定为 99.99%,面向客户的 SLA 可以承诺为 99.9%。这个缓冲空间用于应对:不可控因素(如云厂商故障)、临时容量短缺、以及实验性的新功能发布。缓冲太小会导致频繁违约赔偿,太大则失去承诺意义。一般 Buffer = 2x ~ 10x(即内部目标比外部承诺严格 2 到 10 倍)。
Q5:如何选择 Chaos Mesh、Litmus 和 AWS FIS?
选择依据主要看基础设施环境:(1)Chaos Mesh 最适合 Kubernetes 环境,CRD 丰富、UI 友好、社区活跃;(2)Litmus 同样面向 Kubernetes,但在 GitOps 集成和探针(Probe)机制方面更成熟,适合需要声明式实验管理的团队;(3)AWS FIS 是 AWS 托管服务,适合纯 AWS 基础设施,与 CloudWatch、Auto Scaling 等原生集成,无需维护额外集群。许多企业采用组合方案:Kubernetes 层用 Chaos Mesh,AWS 基础设施层用 FIS。
总结
SRE 和混沌工程是构建高可靠分布式系统的两大支柱。SRE 通过 SLI/SLO/错误预算 建立量化的可靠性目标,用监控告警和自动化减少人为失误;混沌工程则通过 主动故障注入 验证系统在真实故障下的韧性,避免 “平时不烧香,临时抱佛脚”。
两者的结合形成了完整的可靠性闭环:
- 设定 SLO 定义 “好” 的标准
- 建设监控 度量当前状态
- 混沌实验 主动发现弱点
- 事故响应 提升修复速度
- 复盘改进 持续提升 SLO
建议读者从以下实践开始:先选定一个核心服务的四大黄金信号作为 SLI,设定 30 天 99.9% 的 SLO,配置错误预算告警,然后使用 Chaos Mesh 做一次 Pod Kill 实验。这个最小可行实践(MVP)将帮助团队在真实场景中理解 SRE 和混沌工程的价值,并在此基础上逐步扩展体系。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。