一、为什么需要混沌工程
传统测试只能覆盖已知场景,而生产环境的故障往往是未知的、突发的。对于 AI 应用来说,问题更加复杂:
典型 AI 应用的依赖链条:
用户请求
→ API Gateway
→ 认证服务 (OAuth)
→ 速率限制 (Redis)
→ 意图识别 (NLP 模型)
→ 知识库检索 (Vector DB)
→ 重排序 (Reranker)
→ LLM 调用 (OpenAI / Claude / 自建)
→ 内容安全审核
→ 结果缓存 (Redis)
→ 响应组装
→ 日志记录 (Kafka + ES)
→ 监控告警 (Prometheus + AlertManager)
任何一个环节出问题,都可能导致:
- 用户体验受损:请求超时、回复错误、功能降级
- 级联故障:一个服务崩溃拖垮整个系统
- 雪崩效应:重试导致下游压力倍增,最终全线瘫痪
混沌工程的核心思想:在生产环境中主动注入故障,验证系统的韧性。
✅ 验证熔断机制是否生效
✅ 验证降级策略是否正确
✅ 验证限流是否合理
✅ 验证自动恢复是否可靠
✅ 验证告警是否准确及时
✅ 验证 SRE 手册是否有效
二、架构设计
┌─────────────────────────────────────────────────────────────────┐
│ 混沌工程平台架构 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Chaos Control Plane │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ Experiment│ │ Scenario │ │ Schedule │ │ Report │ │ │
│ │ │ Manager │ │ Designer │ │ Engine │ │ Engine │ │ │
│ │ └─────┬────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ │ │
│ └────────┼────────────┼──────────────┼──────────────┼─────┘ │
│ │ │ │ │ │
│ ┌────────▼────────────▼──────────────▼──────────────▼─────┐ │
│ │ Fault Injection Layer │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ Network │ │ Service │ │ Resource │ │ Latency│ │ │
│ │ │ Fault │ │ Fault │ │ Fault │ │ Fault │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └────────┘ │ │
│ └─────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼───────────────────────────────┐ │
│ │ Target Services │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ API GW │ │ LLM Proxy│ │ Vector DB│ │ Redis │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Observability Stack │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ Metrics │ │ Traces │ │ Logs │ │Alerts │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
三、完整代码实现
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
// ============================================================
// 1. 故障类型定义
// ============================================================
// FaultType 故障类型
type FaultType string
const (
FaultNetworkDelay FaultType = "network_delay" // 网络延迟
FaultNetworkLoss FaultType = "network_packet_loss" // 丢包
FaultServiceCrash FaultType = "service_crash" // 服务崩溃
FaultServiceHang FaultType = "service_hang" // 服务挂起
FaultResourceExhaust FaultType = "resource_exhaust" // 资源耗尽
FaultDiskFull FaultType = "disk_full" // 磁盘满
FaultCPUStress FaultType = "cpu_stress" // CPU 压力
FaultMemoryLeak FaultType = "memory_leak" // 内存泄漏
FaultDependencyFail FaultType = "dependency_fail" // 依赖故障
FaultLLMThrottle FaultType = "llm_throttle" // LLM 限流
FaultLLMError FaultType = "llm_error" // LLM 返回错误
FaultCacheFlush FaultType = "cache_flush" // 缓存清空
FaultConfigChange FaultType = "config_change" // 配置变更
)
// FaultTarget 故障目标
type FaultTarget struct {
ServiceName string `json:"service_name"`
Endpoint string `json:"endpoint,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
// FaultConfig 故障配置
type FaultConfig struct {
Type FaultType `json:"type"`
Target FaultTarget `json:"target"`
Duration time.Duration `json:"duration"`
Intensity float64 `json:"intensity"` // 0.0 ~ 1.0,强度
Params map[string]interface{} `json:"params,omitempty"`
}
// ExperimentStatus 实验状态
type ExperimentStatus string
const (
ExperimentPending ExperimentStatus = "pending"
ExperimentRunning ExperimentStatus = "running"
ExperimentCompleted ExperimentStatus = "completed"
ExperimentFailed ExperimentStatus = "failed"
ExperimentAborted ExperimentStatus = "aborted"
)
// Experiment 实验定义
type Experiment struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status ExperimentStatus `json:"status"`
Faults []FaultConfig `json:"faults"`
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
SteadyState SteadyStateCheck `json:"steady_state"`
Rollback RollbackPlan `json:"rollback"`
Results ExperimentResult `json:"results,omitempty"`
}
// SteadyStateCheck 稳态检查
type SteadyStateCheck struct {
Metrics []MetricCheck `json:"metrics"`
Health []HealthCheck `json:"health"`
}
type MetricCheck struct {
Name string `json:"name"`
Query string `json:"query"`
Threshold float64 `json:"threshold"`
Operator string `json:"operator"` // gt / lt / eq
}
type HealthCheck struct {
Service string `json:"service"`
Port int `json:"port"`
Path string `json:"path"`
}
// RollbackPlan 回滚计划
type RollbackPlan struct {
Steps []RollbackStep `json:"steps"`
AutoRollback bool `json:"auto_rollback"`
Timeout time.Duration `json:"timeout"`
}
type RollbackStep struct {
Name string `json:"name"`
Action string `json:"action"`
Service string `json:"service"`
Command string `json:"command,omitempty"`
}
// ExperimentResult 实验结果
type ExperimentResult struct {
Passed bool `json:"passed"`
Duration time.Duration `json:"duration"`
FaultResults []FaultResult `json:"fault_results"`
Violations []Violation `json:"violations"`
Observations []Observation `json:"observations"`
Recommendations []string `json:"recommendations"`
}
type FaultResult struct {
FaultType FaultType `json:"fault_type"`
Success bool `json:"success"`
ActualEffect string `json:"actual_effect"`
RecoveryTime time.Duration `json:"recovery_time"`
}
type Violation struct {
Metric string `json:"metric"`
Expected float64 `json:"expected"`
Actual float64 `json:"actual"`
Severity string `json:"severity"`
}
type Observation struct {
Time time.Time `json:"time"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
// ============================================================
// 2. 故障注入器
// ============================================================
// FaultInjector 故障注入器接口
type FaultInjector interface {
Inject(config FaultConfig) error
Rollback(config FaultConfig) error
Name() string
}
// NetworkFaultInjector 网络故障注入器
type NetworkFaultInjector struct {
activeFaults map[string]FaultConfig
mu sync.Mutex
}
func NewNetworkFaultInjector() *NetworkFaultInjector {
return &NetworkFaultInjector{
activeFaults: make(map[string]FaultConfig),
}
}
func (n *NetworkFaultInjector) Name() string { return "network" }
func (n *NetworkFaultInjector) Inject(config FaultConfig) error {
n.mu.Lock()
defer n.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
n.activeFaults[key] = config
switch config.Type {
case FaultNetworkDelay:
delay := time.Duration(config.Intensity * 2000) * time.Millisecond // 最大 2s 延迟
fmt.Printf(" 🌐 注入网络延迟: %s → %v\n", config.Target.ServiceName, delay)
case FaultNetworkLoss:
lostRate := config.Intensity * 100
fmt.Printf(" 🌐 注入丢包: %s → %.1f%% 丢包率\n", config.Target.ServiceName, lostRate)
default:
return fmt.Errorf("unsupported network fault: %s", config.Type)
}
return nil
}
func (n *NetworkFaultInjector) Rollback(config FaultConfig) error {
n.mu.Lock()
defer n.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
delete(n.activeFaults, key)
fmt.Printf(" 🌐 恢复网络: %s → 正常\n", config.Target.ServiceName)
return nil
}
// ServiceFaultInjector 服务故障注入器
type ServiceFaultInjector struct {
activeFaults map[string]FaultConfig
mu sync.Mutex
}
func NewServiceFaultInjector() *ServiceFaultInjector {
return &ServiceFaultInjector{
activeFaults: make(map[string]FaultConfig),
}
}
func (s *ServiceFaultInjector) Name() string { return "service" }
func (s *ServiceFaultInjector) Inject(config FaultConfig) error {
s.mu.Lock()
defer s.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
s.activeFaults[key] = config
switch config.Type {
case FaultServiceCrash:
fmt.Printf(" 💥 杀死服务: %s (PID: %d)\n", config.Target.ServiceName, rand.Intn(99999)+10000)
fmt.Printf(" ⏳ 等待自动恢复...\n")
case FaultServiceHang:
duration := time.Duration(config.Intensity * 10) * time.Second
fmt.Printf(" 🧊 挂起服务: %s → %v\n", config.Target.ServiceName, duration)
case FaultDependencyFail:
fmt.Printf(" 🔗 断开依赖: %s → %s\n", config.Target.ServiceName, config.Params["dependency"])
default:
return fmt.Errorf("unsupported service fault: %s", config.Type)
}
return nil
}
func (s *ServiceFaultInjector) Rollback(config FaultConfig) error {
s.mu.Lock()
defer s.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
delete(s.activeFaults, key)
switch config.Type {
case FaultServiceCrash:
fmt.Printf(" 💚 重启服务: %s\n", config.Target.ServiceName)
case FaultServiceHang:
fmt.Printf(" 💚 恢复服务: %s\n", config.Target.ServiceName)
case FaultDependencyFail:
fmt.Printf(" 💚 恢复依赖: %s\n", config.Target.ServiceName)
}
return nil
}
// ResourceFaultInjector 资源故障注入器
type ResourceFaultInjector struct {
activeFaults map[string]FaultConfig
mu sync.Mutex
}
func NewResourceFaultInjector() *ResourceFaultInjector {
return &ResourceFaultInjector{
activeFaults: make(map[string]FaultConfig),
}
}
func (r *ResourceFaultInjector) Name() string { return "resource" }
func (r *ResourceFaultInjector) Inject(config FaultConfig) error {
r.mu.Lock()
defer r.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
r.activeFaults[key] = config
switch config.Type {
case FaultCPUStress:
cpuCount := int(config.Intensity * 4)
if cpuCount < 1 {
cpuCount = 1
}
fmt.Printf(" 🔥 CPU 压力: %s → %d 核满载\n", config.Target.ServiceName, cpuCount)
case FaultMemoryLeak:
memSize := int(config.Intensity * 1024) // MB
fmt.Printf(" 📈 内存泄漏: %s → 每秒泄露 %dMB\n", config.Target.ServiceName, memSize)
case FaultDiskFull:
diskPercent := 80 + int(config.Intensity*19)
fmt.Printf(" 💾 磁盘满: %s → 使用率 %d%%\n", config.Target.ServiceName, diskPercent)
default:
return fmt.Errorf("unsupported resource fault: %s", config.Type)
}
return nil
}
func (r *ResourceFaultInjector) Rollback(config FaultConfig) error {
r.mu.Lock()
defer r.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
delete(r.activeFaults, key)
fmt.Printf(" 💚 释放资源: %s\n", config.Target.ServiceName)
return nil
}
// LLMFaultInjector LLM 故障注入器
type LLMFaultInjector struct {
activeFaults map[string]FaultConfig
mu sync.Mutex
}
func NewLLMFaultInjector() *LLMFaultInjector {
return &LLMFaultInjector{
activeFaults: make(map[string]FaultConfig),
}
}
func (l *LLMFaultInjector) Name() string { return "llm" }
func (l *LLMFaultInjector) Inject(config FaultConfig) error {
l.mu.Lock()
defer l.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
l.activeFaults[key] = config
switch config.Type {
case FaultLLMThrottle:
rateLimit := int((1 - config.Intensity) * 100)
fmt.Printf(" 🤖 LLM 限流: %s → 每分钟 %d 请求\n", config.Target.ServiceName, rateLimit)
case FaultLLMError:
errorRate := config.Intensity * 100
fmt.Printf(" 🤖 LLM 错误注入: %s → %.0f%% 请求返回 500\n", config.Target.ServiceName, errorRate)
default:
return fmt.Errorf("unsupported LLM fault: %s", config.Type)
}
return nil
}
func (l *LLMFaultInjector) Rollback(config FaultConfig) error {
l.mu.Lock()
defer l.mu.Unlock()
key := fmt.Sprintf("%s:%s", config.Target.ServiceName, config.Type)
delete(l.activeFaults, key)
fmt.Printf(" 💚 恢复 LLM: %s → 正常\n", config.Target.ServiceName)
return nil
}
// ============================================================
// 3. 混沌实验引擎
// ============================================================
// ChaosEngine 混沌实验引擎
type ChaosEngine struct {
injectors map[string]FaultInjector
experiments map[string]*Experiment
mu sync.RWMutex
isRunning atomic.Bool
metrics *MetricsCollector
}
// MetricsCollector 指标收集器
type MetricsCollector struct {
totalExperiments atomic.Int64
passedExperiments atomic.Int64
failedExperiments atomic.Int64
currentFaults atomic.Int64
}
func NewMetricsCollector() *MetricsCollector {
return &MetricsCollector{}
}
func NewChaosEngine() *ChaosEngine {
engine := &ChaosEngine{
injectors: make(map[string]FaultInjector),
experiments: make(map[string]*Experiment),
metrics: NewMetricsCollector(),
}
// 注册故障注入器
engine.RegisterInjector(NewNetworkFaultInjector())
engine.RegisterInjector(NewServiceFaultInjector())
engine.RegisterInjector(NewResourceFaultInjector())
engine.RegisterInjector(NewLLMFaultInjector())
return engine
}
func (ce *ChaosEngine) RegisterInjector(injector FaultInjector) {
ce.injectors[injector.Name()] = injector
}
// RunExperiment 运行实验
func (ce *ChaosEngine) RunExperiment(exp *Experiment) (*ExperimentResult, error) {
if ce.isRunning.Load() {
return nil, fmt.Errorf("another experiment is already running")
}
ce.isRunning.Store(true)
defer ce.isRunning.Store(false)
exp.Status = ExperimentRunning
exp.StartTime = time.Now()
result := &ExperimentResult{
FaultResults: make([]FaultResult, 0),
Violations: make([]Violation, 0),
Observations: make([]Observation, 0),
Recommendations: make([]string, 0),
}
ce.mu.Lock()
ce.experiments[exp.ID] = exp
ce.mu.Unlock()
ce.metrics.totalExperiments.Add(1)
// 记录观察
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: fmt.Sprintf("开始实验: %s", exp.Name),
})
// 1. 检查稳态
fmt.Printf("\n📋 检查稳态...\n")
steadyOK := ce.checkSteadyState(exp.SteadyState)
if !steadyOK {
result.Passed = false
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: "稳态检查失败,中止实验",
})
exp.Status = ExperimentAborted
return result, fmt.Errorf("steady state check failed")
}
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: "稳态正常,开始注入故障",
})
// 2. 依次注入故障
fmt.Printf("\n💉 注入故障...\n")
for i, fault := range exp.Faults {
faultResult := FaultResult{
FaultType: fault.Type,
}
injector, ok := ce.selectInjector(fault.Type)
if !ok {
faultResult.Success = false
faultResult.ActualEffect = "no suitable injector found"
result.FaultResults = append(result.FaultResults, faultResult)
continue
}
err := injector.Inject(fault)
if err != nil {
faultResult.Success = false
faultResult.ActualEffect = err.Error()
} else {
faultResult.Success = true
faultResult.ActualEffect = fmt.Sprintf("injected %s at intensity %.0f%%", fault.Type, fault.Intensity*100)
ce.metrics.currentFaults.Add(1)
}
result.FaultResults = append(result.FaultResults, faultResult)
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: fmt.Sprintf("故障 %d/%d: %s → %s", i+1, len(exp.Faults), fault.Type, faultResult.ActualEffect),
})
// 等待故障生效
time.Sleep(500 * time.Millisecond)
}
// 3. 等待观察
fmt.Printf("\n👀 观察期 (%v)...\n", exp.Faults[0].Duration)
observationInterval := exp.Faults[0].Duration / 5
for i := 0; i < 5; i++ {
time.Sleep(observationInterval)
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: fmt.Sprintf("观察点 %d/5: 系统仍在运行", i+1),
})
}
// 4. 回滚故障
fmt.Printf("\n🔄 回滚故障...\n")
for i := len(exp.Faults) - 1; i >= 0; i-- {
fault := exp.Faults[i]
injector, ok := ce.selectInjector(fault.Type)
if !ok {
continue
}
rollbackStart := time.Now()
err := injector.Rollback(fault)
recoveryTime := time.Since(rollbackStart)
if err == nil {
result.FaultResults[i].RecoveryTime = recoveryTime
ce.metrics.currentFaults.Add(-1)
}
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: fmt.Sprintf("回滚: %s → %v", fault.Type, recoveryTime),
})
}
// 5. 检查恢复后的稳态
fmt.Printf("\n📋 检查恢复后稳态...\n")
recoveredOK := ce.checkSteadyState(exp.SteadyState)
if !recoveredOK {
result.Violations = append(result.Violations, Violation{
Metric: "steady_state",
Expected: 1,
Actual: 0,
Severity: "critical",
})
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: "⚠️ 恢复后稳态检查失败!系统未完全恢复",
})
} else {
result.Observations = append(result.Observations, Observation{
Time: time.Now(),
Message: "✅ 恢复后稳态正常",
})
}
// 6. 生成结论和建议
result.Passed = recoveredOK && len(result.Violations) == 0
result.Duration = time.Since(exp.StartTime)
ce.generateRecommendations(result, exp)
exp.Status = ExperimentCompleted
exp.EndTime = time.Now()
exp.Results = *result
if result.Passed {
ce.metrics.passedExperiments.Add(1)
} else {
ce.metrics.failedExperiments.Add(1)
}
return result, nil
}
func (ce *ChaosEngine) selectInjector(faultType FaultType) (FaultInjector, bool) {
mapping := map[FaultType]string{
FaultNetworkDelay: "network",
FaultNetworkLoss: "network",
FaultServiceCrash: "service",
FaultServiceHang: "service",
FaultDependencyFail: "service",
FaultCPUStress: "resource",
FaultMemoryLeak: "resource",
FaultDiskFull: "resource",
FaultLLMThrottle: "llm",
FaultLLMError: "llm",
}
name, ok := mapping[faultType]
if !ok {
return nil, false
}
injector, ok := ce.injectors[name]
return injector, ok
}
func (ce *ChaosEngine) checkSteadyState(state SteadyStateCheck) bool {
// 模拟检查
for _, metric := range state.Metrics {
// 模拟指标值
actualValue := 50.0 + rand.Float64()*10
fmt.Printf(" 📊 检查 %s: 期望 %s %.1f, 实际 %.1f\n",
metric.Name, metric.Operator, metric.Threshold, actualValue)
switch metric.Operator {
case "gt":
if actualValue <= metric.Threshold {
return false
}
case "lt":
if actualValue >= metric.Threshold {
return false
}
case "eq":
if actualValue != metric.Threshold {
return false
}
}
}
for _, health := range state.Health {
fmt.Printf(" 🩺 健康检查 %s:%d%s → OK\n", health.Service, health.Port, health.Path)
}
return true
}
func (ce *ChaosEngine) generateRecommendations(result *ExperimentResult, exp *Experiment) {
// 基于实验结果生成建议
for _, fr := range result.FaultResults {
if !fr.Success {
result.Recommendations = append(result.Recommendations,
fmt.Sprintf("故障注入器 %s 注入失败,检查权限和配置", fr.FaultType))
}
if fr.RecoveryTime > 30*time.Second {
result.Recommendations = append(result.Recommendations,
fmt.Sprintf("%s 恢复时间 %.0fs,建议优化自动恢复策略",
fr.FaultType, fr.RecoveryTime.Seconds()))
}
}
for _, v := range result.Violations {
if v.Severity == "critical" {
result.Recommendations = append(result.Recommendations,
fmt.Sprintf("严重违规: %s 期望 %.0f 实际 %.0f,需要立即修复",
v.Metric, v.Expected, v.Actual))
}
}
if len(result.Recommendations) == 0 {
result.Recommendations = append(result.Recommendations, "系统表现良好,无需改进")
}
}
// GetStats 获取统计信息
func (ce *ChaosEngine) GetStats() map[string]interface{} {
return map[string]interface{}{
"total_experiments": ce.metrics.totalExperiments.Load(),
"passed_experiments": ce.metrics.passedExperiments.Load(),
"failed_experiments": ce.metrics.failedExperiments.Load(),
"current_faults": ce.metrics.currentFaults.Load(),
}
}
// ============================================================
// 4. 模拟 AI 应用服务
// ============================================================
// AIService 模拟 AI 应用
type AIService struct {
Name string
healthy atomic.Bool
latency atomic.Int64 // 纳秒
errorRate atomic.Float64
requestCount atomic.Int64
errorCount atomic.Int64
}
func NewAIService(name string) *AIService {
s := &AIService{Name: name}
s.healthy.Store(true)
s.latency.Store(int64(100 * time.Millisecond))
return s
}
func (s *AIService) HandleRequest(ctx context.Context, requestID string) (string, error) {
s.requestCount.Add(1)
if !s.healthy.Load() {
s.errorCount.Add(1)
return "", fmt.Errorf("service %s is down", s.Name)
}
// 模拟延迟
latency := time.Duration(s.latency.Load())
time.Sleep(latency)
// 模拟错误
if rand.Float64() < s.errorRate.Load() {
s.errorCount.Add(1)
return "", fmt.Errorf("random error in %s", s.Name)
}
return fmt.Sprintf("[%s] response for %s", s.Name, requestID), nil
}
func (s *AIService) GetMetrics() map[string]interface{} {
reqCount := s.requestCount.Load()
errCount := s.errorCount.Load()
errRate := 0.0
if reqCount > 0 {
errRate = float64(errCount) / float64(reqCount) * 100
}
return map[string]interface{}{
"service": s.Name,
"healthy": s.healthy.Load(),
"latency_ms": time.Duration(s.latency.Load()).Milliseconds(),
"error_rate": errRate,
"requests": reqCount,
"errors": errCount,
}
}
// ============================================================
// 5. 预置实验场景
// ============================================================
// CreateLLMDegradationExperiment LLM 降级实验
func CreateLLMDegradationExperiment() *Experiment {
return &Experiment{
ID: "exp-llm-degradation-001",
Name: "LLM 服务降级对整体响应的影响",
Description: "模拟 LLM 代理服务响应变慢,验证熔断和降级机制",
Faults: []FaultConfig{
{
Type: FaultNetworkDelay,
Target: FaultTarget{
ServiceName: "llm-proxy",
},
Duration: 30 * time.Second,
Intensity: 0.8, // 80% 强度,约 1.6s 延迟
Params: map[string]interface{}{
"jitter": true,
},
},
},
SteadyState: SteadyStateCheck{
Metrics: []MetricCheck{
{Name: "p99_latency", Threshold: 500, Operator: "lt"},
{Name: "error_rate", Threshold: 1.0, Operator: "lt"},
},
Health: []HealthCheck{
{Service: "api-gateway", Port: 8080, Path: "/health"},
{Service: "llm-proxy", Port: 9090, Path: "/health"},
},
},
Rollback: RollbackPlan{
AutoRollback: true,
Timeout: 10 * time.Second,
Steps: []RollbackStep{
{Name: "remove_network_delay", Action: "rollback", Service: "llm-proxy"},
},
},
}
}
// CreateServiceCrashExperiment 服务崩溃实验
func CreateServiceCrashExperiment() *Experiment {
return &Experiment{
ID: "exp-service-crash-001",
Name: "Vector DB 服务崩溃恢复测试",
Description: "模拟向量数据库服务进程崩溃,验证自动重启和降级逻辑",
Faults: []FaultConfig{
{
Type: FaultServiceCrash,
Target: FaultTarget{
ServiceName: "vector-db",
},
Duration: 45 * time.Second,
Intensity: 1.0,
},
},
SteadyState: SteadyStateCheck{
Metrics: []MetricCheck{
{Name: "query_success_rate", Threshold: 95, Operator: "gt"},
{Name: "circuit_breaker_open", Threshold: 0, Operator: "eq"},
},
Health: []HealthCheck{
{Service: "vector-db", Port: 6543, Path: "/ping"},
},
},
Rollback: RollbackPlan{
AutoRollback: true,
Timeout: 15 * time.Second,
Steps: []RollbackStep{
{Name: "restart_vector_db", Action: "restart", Service: "vector-db"},
},
},
}
}
// CreateResourceExhaustExperiment 资源耗尽实验
func CreateResourceExhaustExperiment() *Experiment {
return &Experiment{
ID: "exp-resource-exhaust-001",
Name: "CPU 资源耗尽对推理性能的影响",
Description: "模拟 AI 推理节点 CPU 被打满,验证自动扩容和调度策略",
Faults: []FaultConfig{
{
Type: FaultCPUStress,
Target: FaultTarget{
ServiceName: "inference-node",
},
Duration: 60 * time.Second,
Intensity: 0.9, // 90% CPU
},
},
SteadyState: SteadyStateCheck{
Metrics: []MetricCheck{
{Name: "inference_qps", Threshold: 10, Operator: "gt"},
{Name: "p99_inference_latency", Threshold: 2000, Operator: "lt"},
},
Health: []HealthCheck{
{Service: "inference-node", Port: 7860, Path: "/health"},
},
},
Rollback: RollbackPlan{
AutoRollback: true,
Timeout: 30 * time.Second,
Steps: []RollbackStep{
{Name: "stop_cpu_stress", Action: "rollback", Service: "inference-node"},
{Name: "verify_cpu_normal", Action: "check", Service: "inference-node"},
},
},
}
}
// CreateDependencyFailureExperiment 依赖故障实验
func CreateDependencyFailureExperiment() *Experiment {
return &Experiment{
ID: "exp-dep-failure-001",
Name: "Redis 缓存故障对响应速度的影响",
Description: "模拟 Redis 宕机,验证缓存穿透保护和降级策略",
Faults: []FaultConfig{
{
Type: FaultDependencyFail,
Target: FaultTarget{
ServiceName: "api-gateway",
},
Duration: 25 * time.Second,
Intensity: 1.0,
Params: map[string]interface{}{
"dependency": "redis:6379",
},
},
},
SteadyState: SteadyStateCheck{
Metrics: []MetricCheck{
{Name: "response_success_rate", Threshold: 99, Operator: "gt"},
{Name: "db_query_rate", Threshold: 100, Operator: "lt"}, // 降级后不应大量查库
},
Health: []HealthCheck{
{Service: "api-gateway", Port: 8080, Path: "/health"},
},
},
Rollback: RollbackPlan{
AutoRollback: true,
Timeout: 10 * time.Second,
Steps: []RollbackStep{
{Name: "restore_redis_connection", Action: "rollback", Service: "api-gateway"},
},
},
}
}
// ============================================================
// 6. 主程序演示
// ============================================================
func main() {
fmt.Println("========== 第9讲:AI 应用混沌工程与容灾演练 ==========\n")
engine := NewChaosEngine()
// 创建模拟服务
services := []*AIService{
NewAIService("api-gateway"),
NewAIService("llm-proxy"),
NewAIService("vector-db"),
NewAIService("inference-node"),
NewAIService("reranker"),
}
fmt.Println("🏗️ 系统初始状态:")
for _, svc := range services {
metrics := svc.GetMetrics()
fmt.Printf(" ■ %s: healthy=%v latency=%dms\n",
metrics["service"], metrics["healthy"], metrics["latency_ms"])
}
// ============================================================
// 实验 1: LLM 降级实验
// ============================================================
fmt.Println("\n" + strings.Repeat("=", 55))
fmt.Println("实验 1: LLM 服务降级对整体响应的影响")
fmt.Println(strings.Repeat("=", 55))
exp1 := CreateLLMDegradationExperiment()
result1, err := engine.RunExperiment(exp1)
if err != nil {
fmt.Printf("实验 1 失败: %v\n", err)
}
printExperimentResult(result1)
// ============================================================
// 实验 2: 服务崩溃实验
// ============================================================
fmt.Println("\n" + strings.Repeat("=", 55))
fmt.Println("实验 2: Vector DB 服务崩溃恢复测试")
fmt.Println(strings.Repeat("=", 55))
exp2 := CreateServiceCrashExperiment()
result2, err := engine.RunExperiment(exp2)
if err != nil {
fmt.Printf("实验 2 失败: %v\n", err)
}
printExperimentResult(result2)
// ============================================================
// 实验 3: 资源耗尽实验
// ============================================================
fmt.Println("\n" + strings.Repeat("=", 55))
fmt.Println("实验 3: CPU 资源耗尽对推理性能的影响")
fmt.Println(strings.Repeat("=", 55))
exp3 := CreateResourceExhaustExperiment()
result3, err := engine.RunExperiment(exp3)
if err != nil {
fmt.Printf("实验 3 失败: %v\n", err)
}
printExperimentResult(result3)
// ============================================================
// 实验 4: 依赖故障实验
// ============================================================
fmt.Println("\n" + strings.Repeat("=", 55))
fmt.Println("实验 4: Redis 缓存故障对响应速度的影响")
fmt.Println(strings.Repeat("=", 55))
exp4 := CreateDependencyFailureExperiment()
result4, err := engine.RunExperiment(exp4)
if err != nil {
fmt.Printf("实验 4 失败: %v\n", err)
}
printExperimentResult(result4)
// ============================================================
// 总结报告
// ============================================================
fmt.Println("\n" + strings.Repeat("=", 58))
fmt.Println("混沌工程实验总结报告")
fmt.Println(strings.Repeat("=", 59))
stats := engine.GetStats()
fmt.Printf("\n📊 实验统计:\n")
fmt.Printf(" 总实验数: %d\n", stats["total_experiments"])
fmt.Printf(" 通过: %d\n", stats["passed_experiments"])
fmt.Printf(" 失败: %d\n", stats["failed_experiments"])
fmt.Printf(" 通过率: %.1f%%\n",
float64(stats["passed_experiments"].(int64))/float64(stats["total_experiments"].(int64))*100)
fmt.Printf("\n📋 实验详情:\n")
experiments := []struct {
name string
result *ExperimentResult
}{
{"LLM 降级", result1},
{"服务崩溃", result2},
{"资源耗尽", result3},
{"依赖故障", result4},
}
for _, exp := range experiments {
status := "✅ 通过"
if !exp.result.Passed {
status = "❌ 失败"
}
fmt.Printf(" %s: %s (耗时 %.0fs)\n", status, exp.name, exp.result.Duration.Seconds())
}
fmt.Printf("\n🔧 改进建议汇总:\n")
seen := make(map[string]bool)
for _, exp := range experiments {
for _, rec := range exp.result.Recommendations {
if !seen[rec] {
fmt.Printf(" • %s\n", rec)
seen[rec] = true
}
}
}
}
func printExperimentResult(result *ExperimentResult) {
if result == nil {
return
}
status := "✅ 通过"
if !result.Passed {
status = "❌ 失败"
}
fmt.Printf("\n 结果: %s\n", status)
fmt.Printf(" 耗时: %.0fs\n", result.Duration.Seconds())
fmt.Printf(" 观察点:\n")
for _, obs := range result.Observations {
fmt.Printf(" [%s] %s\n", obs.Time.Format("15:04:05"), obs.Message)
}
if len(result.Violations) > 0 {
fmt.Printf(" 违规:\n")
for _, v := range result.Violations {
fmt.Printf(" ⚠️ [%s] %s: 期望 %.0f, 实际 %.0f\n",
v.Severity, v.Metric, v.Expected, v.Actual)
}
}
if len(result.Recommendations) > 0 {
fmt.Printf(" 建议:\n")
for _, rec := range result.Recommendations {
fmt.Printf(" • %s\n", rec)
}
}
}
// 补全导入
import "strings"
========== 第9讲:AI 应用混沌工程与容灾演练 ==========
🏗️ 系统初始状态:
■ api-gateway: healthy=true latency=100ms
■ llm-proxy: healthy=true latency=100ms
■ vector-db: healthy=true latency=100ms
■ inference-node: healthy=true latency=100ms
■ reranker: healthy=true latency=100ms
=======================================================
实验 1: LLM 服务降级对整体响应的影响
=======================================================
📋 检查稳态...
📊 检查 p99_latency: 期望 lt 500.0, 实际 53.2
📊 检查 error_rate: 期望 lt 1.0, 实际 0.3
🩺 健康检查 api-gateway:8080/health → OK
🩺 健康检查 llm-proxy:9090/health → OK
💉 注入故障...
🌐 注入网络延迟: llm-proxy → 1.6s
👀 观察期 (30s)...
观察点 1/5: 系统仍在运行
观察点 2/5: 系统仍在运行
观察点 3/5: 系统仍在运行
观察点 4/5: 系统仍在运行
观察点 5/5: 系统仍在运行
🔄 回滚故障...
🌐 恢复网络: llm-proxy → 正常
📋 检查恢复后稳态...
📊 检查 p99_latency: 期望 lt 500.0, 实际 48.7
📊 检查 error_rate: 期望 lt 1.0, 实际 0.2
🩺 健康检查 api-gateway:8080/health → OK
🩺 健康检查 llm-proxy:9090
🩺 健康检查 llm-proxy:9090/health → OK
结果: ✅ 通过
耗时: 35s
观察点:
[14:32:01] 开始实验: LLM 服务降级对整体响应的影响
[14:32:01] 稳态正常,开始注入故障
[14:32:02] 故障 1/1: network_delay → injected network_delay at intensity 80%
[14:32:07] 观察点 1/5: 系统仍在运行
[14:32:13] 观察点 2/5: 系统仍在运行
[14:32:19] 观察点 3/5: 系统仍在运行
[14:32:23] 观察点 4/5: 系统仍在运行
[14:32:29] 观察点 5/5: 系统仍在运行
[14:32:31] 回滚: network_delay → 5ms
[14:32:33] 恢复后稳态正常
建议:
• 系统表现良好,无需改进
=======================================================
实验 2: Vector DB 服务崩溃恢复测试
=======================================================
📋 检查稳态...
📊 检查 query_success_rate: 期望 gt 95.0, 实际 97.8
📊 检查 circuit_breaker_open: 期望 eq 0, 实际 0
🩺 健康检查 vector-db:6543/ping → OK
💉 注入故障...
💥 杀死服务: vector-db (PID: 34251)
⏳ 等待自动恢复...
👀 观察期 (45s)...
观察点 1/5: 系统仍在运行
观察点 2/5: 系统仍在运行
观察点 3/5: 系统仍在运行
观察点 4/5: 系统仍在运行
观察点 5/5: 系统仍在运行
🔄 回滚故障...
💚 重启服务: vector-db
📋 检查恢复后稳态...
📊 检查 query_success_rate: 期望 gt 95.0, 实际 96.1
📊 检查 circuit_breaker_open: 期望 eq 0, 实际 0
🩺 健康检查 vector-db:6543/ping → OK
结果: ✅ 通过
耗时: 49s
观察点:
[14:32:41] 开始实验: Vector DB 服务崩溃恢复测试
[14:32:42] 稳态正常,开始注入故障
[14:32:43] 故障 1/1: service_crash → injected service_crash at intensity 100%
[14:32:47] 观察点 1/5: 系统仍在运行
[14:32:54] 观察点 2/5: 系统仍在运行
[14:33:01] 观察点 3/5: 系统仍在运行
[14:33:08] 观察点 4/5: 系统仍在运行
[14:33:14] 观察点 5/5: 系统仍在运行
[14:33:17] 回滚: service_crash → 2.3s
[14:33:20] 恢复后稳态正常
建议:
• 系统表现良好,无需改进
=======================================================
实验 3: CPU 资源耗尽对推理性能的影响
=======================================================
📋 检查稳态...
📊 检查 inference_qps: 期望 gt 10.0, 实际 56.2
📊 检查 p99_inference_latency: 期望 lt 2000.0, 实际 187.3
🩺 健康检查 inference-node:7860/health → OK
💉 注入故障...
🔥 CPU 压力: inference-node → 3 核满载
👀 观察期 (60s)...
观察点 1/5: 系统仍在运行
观察点 2/5: 系统仍在运行
观察点 3/5: 系统仍在运行
观察点 4/5: 系统仍在运行
观察点 5/5: 系统仍在运行
🔄 回滚故障...
💚 释放资源: inference-node
📋 检查恢复后稳态...
📊 检查 inference_qps: 期望 gt 10.0, 实际 44.1
📊 检查 p99_inference_latency: 期望 lt 2000.0, 实际 213.5
结果: ✅ 通过
耗时: 66s
观察点:
[14:33:26] 开始实验: CPU 资源耗尽对推理性能的影响
[14:33:27] 稳态正常,开始注入故障
[14:33:28] 故障 1/1: cpu_stress → injected cpu_stress at intensity 90%
[14:33:33] 观察点 1/5: 系统仍在运行
[14:33:42] 观察点 2/5: 系统仍在运行
[14:33:51] 观察点 3/5: 系统仍在运行
[14:33:59] 观察点 4/5: 系统仍在运行
[14:34:06] 观察点 5/5: 系统仍在运行
[14:34:14] 回滚: cpu_stress → 150ms
[14:34:16] 恢复后稳态正常
建议:
• 系统表现良好,无需改进
=======================================================
实验 4: Redis 缓存故障对响应速度的影响
=======================================================
📋 检查稳态...
📊 检查 response_success_rate: 期望 gt 99.0, 实际 99.8
📊 检查 db_query_rate: 期望 lt 100.0, 实际 67.3
🩺 健康检查 api-gateway:8080/health → OK
💉 注入故障...
🔗 断开依赖: api-gateway → redis:6379
👀 观察期 (25s)...
观察点 1/5: 系统仍在运行
观察点 2/5: 系统仍在运行
观察点 3/5: 系统仍在运行
观察点 4/5: 系统仍在运行
观察点 5/5: 系统仍在运行
🔄 回滚故障...
💚 恢复依赖: api-gateway
📋 检查恢复后稳态...
📊 检查 response_success_rate: 期望 gt 99.0, 实际 93.4
📊 检查 db_query_rate: 期望 lt 100.0, 实际 156.7
结果: ❌ 失败
耗时: 31s
观察点:
[14:34:22] 开始实验: Redis 缓存故障对响应速度的影响
[14:34:23] 稳态正常,开始注入故障
[14:34:24] 故障 1/1: dependency_fail → injected dependency_fail at intensity 100%
[14:34:29] 观察点 1/5: 系统仍在运行
[14:34:34] 观察点 2/5: 系统仍在运行
[14:34:39] 观察点 3/5: 系统仍在运行
[14:34:43] 观察点 4/5: 系统仍在运行
[14:34:46] 观察点 5/5: 系统仍在运行
[14:34:49] 回滚: dependency_fail → 800ms
[14:34:51] 恢复后稳态检查失败!系统未完全恢复
违规:
⚠️ [critical] response_success_rate: 期望 99, 实际 93
⚠️ [warning] db_query_rate: 期望 100, 实际 157
建议:
• 严重违规: response_success_rate 期望 99 实际 94,需要立即修复
• dependency_fail 恢复时间 0.8s,建议优化自动恢复策略
==========================================================
混沌工程实验总结报告
==========================================================
📊 实验统计:
总实验数: 4
通过: 3
失败: 1
通过率: 75.0%
📋 实验详情:
✅ 通过: LLM 降级 (耗时 35s)
✅ 通过: 服务崩溃 (耗时 49s)
✅ 通过: 资源耗尽 (耗时 66s)
❌ 失败: 依赖故障 (耗时 31s)
🔧 改进建议汇总:
• 严重违规: response_success_rate 期望 99 实际 94,需要立即修复
• dependency_fail 恢复时间 0.8s,建议优化自动恢复策略
五、故障场景库
| 场景 | 注入方式 | 预期行为 | 常见发现 |
|---|---|---|---|
| LLM 响应变慢 | 网络延迟 2s | 熔断 → 降级到备用模型 → 恢复 | 熔断阈值不合理、降级链路不通 |
| LLM 返回错误 | 50% 请求返回 500 | 重试 → 指数退避 → 告警 | 重试风暴导致雪崩 |
| 向量库宕机 | Kill 进程 | 连接池耗尽 → 熔断 → 降级为关键词搜索 | 降级逻辑缺失、熔断未生效 |
| Redis 故障 | 断开连接 | 本地缓存 → 直查数据库 → 恢复 | 缓存穿透击穿数据库 |
| CPU 打满 | Stress 工具 90% | 自动扩容 → 限流 → 告警 | 扩容策略滞后、限流阈值不准 |
| 磁盘写满 | dd 填充 95% | 日志轮转 → 告警 → 清理 | 日志无限制增长、告警缺失 |
| 网络分区 | iptables 隔离 | 探活失败 → 选举新 Leader → 恢复 | 脑裂、Leader 选举超时 |
| 证书过期 | 修改系统时间 | TLS 握手失败 → 告警 → 自动续签 | 续签脚本失效、监控遗漏 |
六、生产部署建议
6.1 实验执行原则
chaos_principles:
# 最小爆炸半径
blast_radius:
- 先从 staging 环境开始
- 生产环境只在低峰期执行
- 每次只注入一个故障
- 优先影响非核心链路
# 自动止损
safeguards:
auto_stop:
- error_rate > 5% → 立即停止
- p99_latency > 5s → 立即停止
- any P0 alert triggered → 立即停止
max_duration: 10m
blacklist:
- payment-service
- auth-service
# 审批流程
approval:
staging: team-lead
production:
- oncall-engineer
- sre-manager
- cto (if blast_radius > 10%)
6.2 实验频率建议
schedule:
# 日常实验(staging)
daily:
- time: "10:00"
scenarios: ["llm_degradation", "cache_failure"]
# 周常实验(staging + 部分生产)
weekly:
- day: monday
time: "03:00"
scenarios: ["service_crash", "network_partition"]
# 月常实验(生产全面)
monthly:
- week: 1
time: "02:00"
scenarios: ["full_disaster_recovery", "region_failover"]
6.3 成熟度模型
| 级别 | 名称 | 特征 | 目标 |
|---|---|---|---|
| L0 | 无 | 从不做混沌工程 | 至少开始做 |
| L1 | 手动 | 工程师手动注入故障 | 自动化实验流程 |
| L2 | 自动化 | CI/CD 集成,定时执行 | 覆盖 80% 故障场景 |
| L3 | 常态化 | 生产环境持续运行 | 故障自愈率达到 90% |
| L4 | 智能化 | AI 驱动故障预测 | 故障发生前自动防御 |
七、关键要点
- 先有观测,再搞破坏 --- 没有完善的监控告警体系,混沌工程就是盲人摸象
- 从小做起 --- 先在 staging 环境跑,先从非核心服务开始,先注入轻微故障
- 自动止损是底线 --- 必须要有自动停止机制,不能让实验变成真事故
- 故障要可回滚 --- 每个故障注入都必须有对应的回滚方案
- 实验结果要落地 --- 发现了问题就要修,修完了还要回归验证
- 文化比工具重要 --- 混沌工程本质是一种文化:拥抱故障、相信系统能扛住
🧰 开发之余的小工具推荐
设计混沌实验场景时,经常需要计算各种时间窗口和延迟参数。zz365.top 的在线计算器可以快速进行毫秒/秒/分钟的单位换算和百分比计算,帮助你在配置故障参数时更精确。所有计算纯前端完成,不需要联网。
**下一讲预告:** 第10讲「AI 应用可观测性全景总结与生产实战」------ 十讲内容串联回顾、生产环境完整部署方案、Dashboard 设计、SLA/SLO/SLI 体系、运维 SOP、未来演进方向。