7.5. 灾备与高可用设计让你的 Skills 服务永不掉线

你需要什么

  • 一个可用的 Kubernetes 集群(1.27+),至少 3 个跨可用区节点
  • kubectlhelm 与对应集群的认证配置
  • 云厂商 CLI 工具(如 Azure CLI、AWS CLI)用于管理有状态资源
  • 你的 Skills 服务已容器化并推送至镜像仓库
  • 预计时间:约 2 小时(含演练)

最终成果

你将得到一套具备生产级韧性的 Skills 服务平台:

  • 无状态 Skills 自动水平扩展,单副本故障对用户无感知
  • 有状态 Skills 在跨可用区故障时能完成分钟级恢复,且数据不丢
  • 定义了明确的 SLI/SLO,并通过演练验证了恢复流程

这么做的目的是让商业化 Skills 平台不止“可用”,更能扛住真实世界的中断,守住客户信任与服务水平协议。


步骤一:无状态 Skills 的多副本部署——“流量分担 + 自动诊疗”

只要是纯粹的函数调用、无本地持久化的 Skills,都应设计为无状态组件。
借助 Kubernetes 原生的 Deployment + Service + Ingress,我们可以在 30 分钟内建立多副本、负载均衡与自动健康检查。

1.1 编写零配置 Deployment

创建一个 YAML 清单 skill-worker.yaml,关键配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: skill-worker
  labels:
    app: skill-worker
spec:
  replicas: 3                # 至少 3 副本,分布到不同 AZ
  selector:
    matchLabels:
      app: skill-worker
  template:
    metadata:
      labels:
        app: skill-worker
    spec:
      affinity:
        podAntiAffinity:     # 避免所有副本落在同一节点
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app: skill-worker
                topologyKey: kubernetes.io/hostname
      containers:
        - name: skill
          image: your-registry/skill-executor:v1.2.0
          env:
            - name: STATEFUL_BACKEND_URL  # 有状态技能依赖的外部存储地址
              value: "https://your-cosmosdb-endpoint"
          ports:
            - containerPort: 8080
          livenessProbe:     # 进程存活检查,连续失败重启
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:    # 服务就绪检查,未就绪不加入 Service 端点
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5

预期结果:部署后可见 3 个 Pod 运行,且 READY 列全部为 1/1。若某 Pod 探活失败,会自动重启,且在就绪探针失败期间会被暂时移出负载均衡。

kubectl apply -f skill-worker.yaml
kubectl get pods -l app=skill-worker -o wide

1.2 暴露服务并使用负载均衡

apiVersion: v1
kind: Service
metadata:
  name: skill-worker-svc
spec:
  type: ClusterIP
  selector:
    app: skill-worker
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  sessionAffinity: None      # 无状态服务禁止会话亲和

配合 Ingress(如 NGINX Ingress Controller)将外部流量引入:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: skills-ingress
spec:
  rules:
    - host: skills.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: skill-worker-svc
                port:
                  number: 80

踩坑笔记:

  • 一定不要让无状态 Skills 在容器内保存 Session 或本地文件。如果技能运行时生成临时文件,请使用空目录挂载(emptyDir)并明确其生命周期。
  • 确认 livenessProbe/healthz 只检查进程是否阻塞,不要链接外部依赖(如数据库),否则数据库抖动会引发连锁重启。

1.3 验证水平扩展

模拟压测的同时动态调整副本数:

# 使用 Vegeta 或 hey 产生请求
echo "GET https://skills.example.com/v1/classify" | vegeta attack -duration=30s -rate=100 | tee results.bin | vegeta report

# 动态扩容至 6 副本
kubectl scale deployment skill-worker --replicas=6

观察请求延迟与错误率应无明显上升,甚至吞吐线性增长。


步骤二:有状态 Skills 的备份与恢复——“状态快照 + 跨 AZ 故障转移”

Skills 中的记忆、对话历史、长期缓存等属于有状态部分,需要依赖托管的数据服务(如 Cosmos DB、Redis、PostgreSQL)。根据调研素材强调的“共享责任模型”,数据平面的高可用必须由客户自行设计。这里以 Azure Cosmos DB 为例,演示跨区域冗余与备份恢复。

2.1 启用多区域写入与自动故障转移

# 为现有 Cosmos DB 账户启用多区域写入
az cosmosdb update \
  --name skill-state-db \
  --resource-group skills-prod-rg \
  --enable-multiple-write-locations true

# 添加至少一个跨区域的写入区域(例如从 East US 扩展到 West Europe)
az cosmosdb failover-priority-change \
  --name skill-state-db \
  --resource-group skills-prod-rg \
  --failover-policies "East US=0" "West Europe=1"

# 开启服务端自动故障转移
az cosmosdb update \
  --name skill-state-db \
  --resource-group skills-prod-rg \
  --enable-automatic-failover true

预期结果:在门户中查看 Cosmos DB 的“全球复制”页面,已有两个区域都为读写状态。任何区域发生中断,客户端 SDK 的重试策略会自动切换到可用区域(需配置 SDK 多区域选项)。

2.2 配置定期备份与快照导出

Cosmos DB 默认提供连续备份,但我们仍建议每周全量快照,并存储到异地对象存储:

# 创建定期备份策略(Azure 提供的定期备份模式)
az cosmosdb update \
  --name skill-state-db \
  --resource-group skills-prod-rg \
  --backup-policy-type Periodic \
  --backup-interval 240 \          # 备份间隔(分钟)
  --backup-retention 8             # 保留时长(小时)

# 手动触发一次快照导出至跨区域 Storage Account
az cosmosdb restore \
  --source-database-account skill-state-db \
  --target-database-account skill-state-db-backup \
  --restore-timestamp "2026-06-02T12:00:00+00:00" \
  --resource-group skills-dr-rg

另外,编写一个 CronJob 来自动化导出:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cosmos-backup-job
spec:
  schedule: "0 2 * * 0"   # 每周日凌晨2点
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: mcr.microsoft.com/azure-cli:latest
              command:
                - /bin/bash
                - -c
                - |
                  az cosmosdb restore \
                    --source-database-account skill-state-db \
                    --target-database-account skill-state-db-backup-$(date +%s) \
                    --restore-timestamp $(date -u +"%Y-%m-%dT%H:%M:%SZ") \
                    --resource-group skills-dr-rg
          restartPolicy: OnFailure

踩坑经验

  • 不可仅依赖自动备份:多区域写入虽能应付整个区域挂掉,但逻辑错误、误删数据仍需按时间点快照恢复。
  • 确认快照恢复后的数据库连接字符串与生产隔离,避免因 DNS 冲突写回生产。演练时将备份库向一个临时 Skills 实例注入验证。

2.3 验证跨 AZ 故障转移

模拟区域故障(谨慎在生产操作,建议在演练环境):停掉主区域节点的网络接口或手动对 Cosmos DB 执行计划内区域故障转移。

# 手动触发故障转移(将 West Europe 提升为主写入)
az cosmosdb failover-priority-change \
  --name skill-state-db \
  --resource-group skills-prod-rg \
  --failover-policies "West Europe=0" "East US=1"

观察 Skills 服务日志,连接会自动切换,记录故障转移时间(RPO ~0,RTO <1 分钟)。测试 SQL 查询是否正常。


步骤三:灾难演练与 SLI/SLO 设定——“量化可靠性,定期练兵”

有高可用架构不等于真的可靠,必须通过数据和演练验证。本章基于站点可靠性工程实践,定义服务水平指标并实施障碍注入。

3.1 定义合适的 SLI 与 SLO

SLI(服务水平指标) 测量方式 SLO 目标(示例)
请求成功率 非 5xx 响应数 / 总请求数 ≥ 99.95%(30 天窗口)
P99 延迟(无状态 Skills) 请求处理时间分布 P99 ≤ 800 ms
故障恢复时间 (RTO) 从故障注入到服务恢复可用性的时间 ≤ 5 分钟
数据恢复点目标 (RPO) 故障时刻到最后一次数据快照的时间差 ≤ 1 小时

将 SLO 嵌入监控与告警(Prometheus AlertManager 规则)。示例规则:

groups:
  - name: skills-slo
    rules:
      - alert: HighErrorRate
        expr: (sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))) > 0.0005
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Skills 错误率超过 SLO 阈值 0.05%"

3.2 执行灾难演练

我们采用 Chaos Mesh 模拟两种故障:Pod 随机死亡网络延迟 / 分区

# 1. Pod 杀戮实验,验证多副本自动恢复
kubectl apply -f - <<EOF
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: skill-kill-random
spec:
  action: pod-kill
  mode: one
  selector:
    namespaces:
      - skills-prod
    labelSelectors:
      app: skill-worker
  scheduler:
    cron: "@every 30m"
EOF

监控恢复速度:Grafana 面板应显示 Pod 数量短暂下降后快速回升至设定副本数,且请求成功率无明显降低。

# 2. 网络延迟注入,验证超时与重试
kubectl apply -f - <<EOF
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: skill-net-delay
spec:
  action: delay
  mode: all
  selector:
    namespaces:
      - skills-prod
    labelSelectors:
      app: skill-worker
  delay:
    latency: "500ms"
    jitter: "100ms"
  duration: "2m"
EOF

观察 P99 延迟是否超出 SLO,若超出则检查客户端超时配置和熔断策略。

注意:演练前务必通知上下游团队,启用回滚机制(如 Chaos 实验的 duration 有限,自动终止)。每次演练后形成报告,附上 SLI 偏离情况和改进项。

3.3 验证完整恢复流程实战

规划一个“全栈演练”:

  1. 停止主区域的 Cosmos DB 写入(模拟中断)。
  2. 自动故障转移生效。
  3. 将 Skills 的 Deployment 镜像升级至新版本(验证滚动更新期间可用性)。
  4. 从备份恢复部分数据,并与当前状态联合验证。

跟踪关键时间戳,计算实际 RTO 和 RPO,与 SLO 对比。一次成功演练花费约 2 小时(包含准备和观察)。


回顾

我们完成了一套面向 Skills 服务的灾备与高可用方案:

  • 通过 Kubernetes 多副本 + 健康检查与负载均衡,让无状态 Skills 从容应对单点故障;
  • 借助 Cosmos DB 多区域写入、定期快照与 CronJob 备份,实现了有状态数据的跨 AZ 故障转移与时间点恢复;
  • 定义了核心 SLI(成功率、延迟、RTO)并设定了具体 SLO,通过 Chaos Mesh 注错进行了实验验证。

整个过程从基础部署到全链路演练,大约需要 2 小时操作时间(不含阅读与规划)。

行动清单

  1. 标记出你现有 Skills 中 需要持久化的状态,将其迁移至多区域托管的数据库。
  2. 为每个无状态 Skill 编写 Kubernetes Deployment,并加入 liveness/readiness 探针。
  3. 设定你的第一个 SLO(如“99.9% 可用性”),并配置相应告警。
  4. 在非生产环境执行一次自动故障转移演练,记录 RTO 和 RPO。
  5. 阅读下一章——“代码审查 Skill 系统让团队效率翻倍”,思考如何将高可用设计融入该 Skill 的实现,例如将审查规则与结果持久化,并验证其恢复能力。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 查看所有版本


暂无话题~