第一章:为什么需要混沌工程?
1.1 传统测试的盲区
| 测试类型 | 覆盖场景 | 无法发现的问题 |
|---|---|---|
| 单元测试 | 函数逻辑 | 依赖服务宕机 |
| 集成测试 | 模块交互 | 网络分区、延迟 |
| E2E 测试 | 用户路径 | 第三方 API 超时 |
现实世界充满不确定性:
- 云服务商区域故障(AWS us-east-1 中断)
- 数据库主从切换延迟
- DNS 解析失败
1.2 混沌工程五大原则(来自 Netflix)
- 建立稳态假设:系统在正常情况下表现如何?(如 P99 < 500ms)
- 真实世界事件:模拟可能发生的故障(非随机破坏)
- 自动化实验:在生产或类生产环境自动运行
- 最小爆炸半径:控制影响范围(先单用户,再全量)
- 持续验证:将混沌融入发布流程
目标不是制造混乱,而是暴露脆弱点。
第二章:故障注入工具选型
2.1 工具矩阵
| 场景 | 开发环境 | 生产/K8s 环境 |
|---|---|---|
| 网络故障 | Toxiproxy | Chaos Mesh / LitmusChaos |
| 进程终止 | kill -9 |
Chaos Mesh PodKill |
| 资源耗尽 | stress-ng | Chaos Mesh CPU/Memory |
| 应用层异常 | 自定义装饰器 | Application-level hooks |
本篇重点:
- 开发/CI 环境:Toxiproxy + 自定义 Python 装饰器
- 生产预演:Chaos Mesh(K8s)
第三章:实验 1 ------ 数据库连接延迟
3.1 使用 Toxiproxy 模拟网络延迟
Toxiproxy 是一个 TCP 代理,可注入延迟、丢包、断连。
启动 Toxiproxy(Docker)
# docker-compose.chaos.yml
version: '3'
services:
toxiproxy:
image: shopify/toxiproxy
ports:
- "8474:8474" # API 管理端口
- "25432:25432" # 代理 PostgreSQL(原 5432 → 25432)
command: ["-host", "0.0.0.0"]
注入 2 秒延迟
# 创建代理规则
curl -X POST http://localhost:8474/proxies -d '{
"name": "postgres_delay",
"listen": "0.0.0.0:25432",
"upstream": "db:5432"
}'
# 添加延迟故障
curl -X POST http://localhost:8474/proxies/postgres_delay/toxics -d '{
"name": "latency_toxic",
"type": "latency",
"attributes": { "latency": 2000, "jitter": 500 }
}'
效果:所有数据库查询增加 2±0.5 秒延迟。
3.2 应用配置调整
Flask 应用连接字符串改为 Toxiproxy 地址:
# config.py
SQLALCHEMY_DATABASE_URI = 'postgresql://user:pass@toxiproxy:25432/mydb'
3.3 验证系统行为
-
预期:前端显示加载状态,后端返回 504 超时(若无熔断)
-
改进 :添加数据库查询超时(SQLAlchemy
statement_timeout)engine 配置
engine = create_engine(
DATABASE_URL,
connect_args={"options": "-c statement_timeout=5000"} # 5秒超时
)
第四章:实验 2 ------ Redis 缓存失效
4.1 模拟 Redis 宕机
直接停止 Redis 容器:
docker stop myapp-redis-1
4.2 应用降级策略
确保应用在 Redis 不可用时仍能工作(缓存穿透保护):
# utils/cache.py
from redis import Redis, ConnectionError
def get_user_profile(user_id):
try:
cached = redis_client.get(f"user:{user_id}")
if cached:
return json.loads(cached)
except ConnectionError:
# Redis 不可用,直接查 DB
pass
# 回源数据库
user = User.query.get(user_id)
# 注意:此时不写回 Redis(避免雪崩)
return user.to_dict()
关键 :缓存层应为可选依赖,而非硬依赖。
4.3 监控指标
- Redis 连接错误率 应上升
- 数据库 QPS 应短暂 spike
- API 延迟 应小幅增加(但不崩溃)
第五章:实验 3 ------ API 间歇性 500 错误
5.1 自定义 Flask 装饰器注入异常
# decorators/chaos.py
import random
from functools import wraps
from flask import abort
def inject_chaos(failure_rate=0.1):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
if random.random() < failure_rate:
# 模拟随机 500 错误
abort(500, "Chaos injected: Simulated server error")
return f(*args, **kwargs)
return wrapper
return decorator
5.2 应用到关键路由
@app.route('/api/orders')
@inject_chaos(failure_rate=0.2) # 20% 请求失败
@jwt_required()
def get_orders():
# ...
5.3 前端容错处理
Vue 组件需处理 500 错误:
<script setup>
const { data, error } = await useFetch('/api/orders')
// 显示友好错误
if (error.value?.status === 500) {
message.error('服务暂时不可用,请稍后重试')
// 可选:自动重试(带退避)
}
</script>
目标:用户体验不中断,仅功能短暂降级。
第六章:实验 4 ------ 前端资源加载失败
6.1 模拟 CDN 故障
使用 Nginx 返回 404:
# nginx.conf
location ~* \.(js|css)$ {
if ($arg_chaos = "true") {
return 404;
}
}
访问 https://app.com/main.js?chaos=true → 404
6.2 前端韧性设计
-
资源重试:使用 Service Worker 缓存关键 JS
-
优雅降级:核心功能不依赖最新 JS(如 SSR 备份)
-
错误边界 :Vue 的
onErrorCaptured捕获组件错误// main.ts
app.config.errorHandler = (err, instance, info) => {
Sentry.captureException(err)
// 显示全局错误提示
showGlobalError("部分功能不可用")
}
第七章:可观测性联动
7.1 关键指标监控
| 故障类型 | 应关注指标 |
|---|---|
| 数据库延迟 | DB query latency, HTTP 5xx rate |
| Redis 失效 | Cache miss rate, DB CPU |
| API 500 | Error rate by endpoint, Apdex score |
| 前端加载失败 | JS error count, Page load time |
7.2 Grafana 仪表盘
创建 Chaos Experiment Dashboard:
- 对比实验前后指标
- 标记实验开始/结束时间(通过 Annotation)
7.3 日志突变检测
使用 Loki + LogQL 检测错误日志激增:
count_over_time({job="flask"} |= "500" [5m]) > 10
第八章:自动化混沌测试(CI/CD 集成)
8.1 实验即代码(Chaos as Code)
定义实验 YAML:
# chaos/experiments/db-latency.yaml
name: "Database Latency Test"
steps:
- name: Inject 2s latency to PostgreSQL
type: toxiproxy
config:
proxy: postgres_delay
toxic:
type: latency
attributes:
latency: 2000
- name: Run E2E test suite
type: script
command: npm run test:e2e
- name: Clean up
type: toxiproxy
action: remove_toxic
8.2 GitHub Actions 集成
# .github/workflows/chaos.yml
name: Chaos Test
on: [push]
jobs:
chaos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start app with Toxiproxy
run: docker-compose -f docker-compose.yml -f docker-compose.chaos.yml up -d
- name: Run chaos experiment
run: python chaos-runner.py --experiment db-latency.yaml
- name: Assert system stability
run: |
# 检查错误率是否 < 5%
if [ $(get_error_rate) -gt 5 ]; then
echo "Chaos test failed!" && exit 1
fi
安全机制:
- 仅在非主分支运行
- 自动清理故障注入
第九章:生产环境混沌(谨慎!)
9.1 Chaos Mesh(Kubernetes)
部署 Chaos Mesh Operator:
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm install chaos-mesh chaos-mesh/chaos-mesh -n chaos-testing --create-namespace
9.2 Pod 网络延迟实验
# network-delay.yaml
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: delay-postgres-traffic
spec:
action: delay
mode: one # 只影响一个 Pod
selector:
namespaces: ["myapp"]
labelSelectors:
app: flask-api
delay:
latency: "2s"
target:
selector:
namespaces: ["database"]
labelSelectors:
app: postgres
9.3 金丝雀发布 + 混沌
- 对 1% 用户流量注入故障
- 监控错误率,若超标则自动回滚
第十章:文化与流程
10.1 混沌工程不是"破坏者"
- 提前通知:团队知晓实验计划
- 复盘会议:每次实验后分析根因
- 奖励改进:修复脆弱点的团队获得认可
10.2 从"救火"到"防火"
| 传统模式 | 混沌工程模式 |
|---|---|
| 故障发生 → 紧急修复 | 主动暴露 → 提前加固 |
| MTTR(平均修复时间) | MTTD(平均发现时间)↓ |
总结:韧性不是功能,而是属性
真正的高可用,经得起主动破坏。