文章

On-Call 体系与值班机制

On-Call 体系与值班机制

一句话:On-Call 不是”轮到你你就得接电话”,而是一套分轮值守 + 告警分级 + 升级策略 + Runbook 支撑的工程化体系——保证故障有人接、接得住、能升级。

On-Call 体系全景

graph TD
    ALERT[告警触发] --> ROUTE[告警路由<br/>按服务/严重程度]
    ROUTE --> P1[Primary On-Call<br/>一线值班]
    P1 -->|Ack| HANDLE[处理告警]
    P1 -->|未 Ack 超时| ESCALATE1[升级到 Secondary]
    ESCALATE1 -->|未 Ack| ESCALATE2[升级到 Manager]
    ESCALATE2 -->|未 Ack| ESCALATE3[全员 Call]
    HANDLE --> RUNBOOK[参考 Runbook]
    RUNBOOK --> RESOLVE[恢复]

值班轮换设计

轮换模式

模式周期优点缺点适用
Primary/Secondary1 周轮换有备份、压力可接受连续 1 周疲劳中小团队
三级轮换1 周轮换P→S→Manager 层层升级需要更多人大团队
Follow-the-Sun按时区无夜间值班需要跨国团队全球团队
24/7 单人3-5 天轮换简单疲劳严重不推荐

推荐轮换方案

推荐方案: Primary + Secondary 双人轮换

轮换周期: 1 周(周一 10:00 交接)
休息恢复: 轮换后至少 1 天不值班

人员要求:
  Primary:  能独立处理 SEV2,知道何时升级
  Secondary: 能处理 SEV1,作为 Primary 的备份

排班规则:
  - 连续值班不超过 1 周
  - 值班后至少 1 天恢复
  - 节假日提前排班 + 补偿
  - 每个 Primary 必须有 Secondary 备份

排班表示例

from datetime import datetime, timedelta
from dataclasses import dataclass

@dataclass
class OnCallShift:
    start: datetime
    end: datetime
    primary: str
    secondary: str
    manager: str

def generate_schedule(team: list[str], weeks: int, start_date: datetime) -> list:
    """生成 On-Call 排班表"""
    schedule = []
    n = len(team)
    
    for i in range(weeks):
        week_start = start_date + timedelta(weeks=i)
        week_end = week_start + timedelta(days=7)
        
        primary = team[i % n]
        secondary = team[(i + 1) % n]
        manager = team[(i + 2) % n]  # 经理也轮换
        
        schedule.append(OnCallShift(
            start=week_start,
            end=week_end,
            primary=primary,
            secondary=secondary,
            manager=manager,
        ))
    
    return schedule

# 示例
team = ["张三", "李四", "王五", "赵六"]
schedule = generate_schedule(team, 4, datetime(2026, 8, 3))
for s in schedule:
    print(f"{s.start.date()} ~ {s.end.date()}: P={s.primary} S={s.secondary} M={s.manager}")

告警分级与路由

告警分级

级别触发条件通知方式响应 SLA升级
PageSEV1/SEV2电话 + IM 推送5/15 min10 min 未 Ack → Secondary
TicketSEV3工单系统工作时间24h 未处理 → Primary
InfoSEV4日志/看板

Alertmanager 路由配置

route:
  receiver: default
  group_by: ['service', 'severity']
  group_wait: 10s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    # SEV1: 电话通知 Primary
    - match:
        severity: critical
      receiver: phone-primary
      group_wait: 0s
      repeat_interval: 30m
      routes:
        # 10 分钟未 Ack 升级到 Secondary
        - match:
            acknowledged: 'false'
          receiver: phone-secondary
          continue: true
    
    # SEV2: IM 通知 Primary
    - match:
        severity: warning
      receiver: im-primary
      group_wait: 30s
      repeat_interval: 2h

receivers:
  - name: phone-primary
    webhook_configs:
      - url: 'https://pagerduty.com/integration/xxx'
        send_resolved: true
  
  - name: phone-secondary
    webhook_configs:
      - url: 'https://pagerduty.com/integration/yyy'
        send_resolved: true
  
  - name: im-primary
    webhook_configs:
      - url: 'https://open.feishu.cn/open-apis/bot/v2/hook/xxx'
        send_resolved: true

Runbook 体系

Runbook 是什么

Runbook = 标准化操作手册——告诉 On-Call “这个告警来了该做什么”。

Runbook vs 文档:
  文档 = 知识(为什么这样做)
  Runbook = 操作(具体做什么)
  
Runbook 特点:
  - 按"告警名"索引(告警来了直接查对应 Runbook)
  - 步骤化(Step 1 → Step 2 → Step 3)
  - 含命令(可以直接复制执行)
  - 含决策点("如果 X 则 A,否则 B")
  - 含升级条件("到这里还不行就升级")

Runbook 模板

# Runbook: [告警名称]

## 告警信息
- 告警名: HighMemoryUsage
- 严重程度: warning
- 服务: api-gateway
- 触发条件: 内存使用率 > 85%

## 快速诊断
1. 检查内存趋势: `kubectl top pods -n production | grep api-gateway`
2. 检查是否 OOM: `kubectl get events -n production | grep OOMKilled`
3. 检查近期变更: `argocd app history api-gateway`

## 处理步骤

### 如果是新版本导致的内存泄漏
1. 回滚: `argocd app rollback api-gateway <previous-revision>`
2. 确认恢复: `curl http://api-gateway:8080/health`
3. 通知: 在故障群发消息 "已回滚 api-gateway 到 vX.X.X"

### 如果是流量增长导致
1. 扩容: `kubectl scale deployment api-gateway -n production --replicas=8`
2. 确认: `kubectl get pods -n production | grep api-gateway`

### 如果是 OOM
1. 临时增加内存: `kubectl patch deployment api-gateway -n production ...`
2. 排查内存泄漏(需开发介入)

## 升级条件
- 回滚后 5 分钟未恢复 → 升级到 Secondary
- 确认是代码 Bug → 拉入开发负责人
- 影响范围扩大 → 升级为 SEV1

On-Call 交接清单

## On-Call 交接清单

### 交接时需要同步的信息

- [ ] 本周处理的告警汇总(数量/类型/未解决)
- [ ] 正在跟进的 Action Items 进度
- [ ] 已知风险(即将到期的容量/计划变更)
- [ ] 特殊情况(假期/大促/灰度发布进行中)
- [ ] Runbook 需要更新的地方

### 交接方式
- 口头交接 + 文档同步
- 交接时间: 周一 10:00(避开早晚高峰)

On-Call 福利与健康

On-Call 健康原则:
  1. 保证休息: 值班后至少 1 天恢复
  2. 公平轮换: 不让同一个人长期值班
  3. 补偿机制: 值班补贴 + 调休
  4. 减少噪音: 告警降噪,只 Page 真正紧急的
  5. 工具支撑: Runbook 完善,减少"不知所措"
  6. 心理支持: SEV1 后给恢复时间,复盘不追责

常见问题 / 坑点

问题原因解决方案
半夜被叫醒处理 SEV3告警分级不当SEV3 不 Page,走工单
升级链太长Primary 不 Ack设短超时(5-10 min),快速升级
Runbook 过时告警改了但 Runbook 没更新告警和 Runbook 绑定,改告警必须改 Runbook
新人不敢值班怕处理不了影子值班(Shadow On-Call)+ 完善 Runbook
值班疲劳连续多周值班强制轮换休息 + 减少告警噪音

关联知识

状态

  • 值班轮换设计
  • 告警分级与路由
  • Runbook 体系
  • 交接清单
  • On-Call 健康