🚨 第15课:告警与SLO

📌 课程阶段:安全与监控(5/5)|预计时间:60分钟|难度:⭐⭐⭐⭐☆

一、告警设计哲学

好的告警系统应该:只告真正需要人介入的问题。告警太多→疲劳→忽略→遗漏真正故障。Google SRE的黄金法则:每条告警都应该可执行。

┌──────────── 告警设计原则 ────────────┐
│                                        │
│  ❌ 坏告警:                           │
│  • CPU > 80%                           │
│    → 所以呢?需要我做什么?             │
│  • 内存使用率高                        │
│    → 可能是正常的,无法直接行动         │
│                                        │
│  ✅ 好告警:                           │
│  • API P99延迟 > 500ms 持续5分钟       │
│    → 用户受到影响,需要排查             │
│  • 错误率 > 1% 持续3分钟               │
│    → 服务异常,需要修复                 │
│  • SLO预算消耗速度异常                 │
│    → 需要关注可靠性                    │
│                                        │
│  核心原则:                            │
│  🔹 基于症状而非原因                   │
│  🔹 基于用户影响而非系统指标           │
│  🔹 每条告警都必须有明确的行动指南      │
│  🔹 使用SLO驱动告警                   │
└────────────────────────────────────────┘

二、Prometheus告警规则

# alerting-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: k8s-alerts
  namespace: monitoring
spec:
  groups:
  - name: k8s-alerts
    rules:
    # Pod不可用告警
    - alert: PodCrashLooping
      expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 5 > 0
      for: 15m
      labels:
        severity: warning
      annotations:
        summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"
        runbook: "https://runbook.example.com/PodCrashLooping"
    
    # Deployment副本数不足
    - alert: DeploymentReplicasMismatch
      expr: |
        kube_deployment_spec_replicas
          != kube_deployment_status_available_replicas
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} has insufficient replicas"
    
    # 节点NotReady
    - alert: NodeNotReady
      expr: kube_node_status_condition{condition="Ready",status="true"} == 0
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Node {{ $labels.node }} is not ready"
    
    # PVC即将写满
    - alert: PVCAlmostFull
      expr: |
        kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.85
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} is {{ $value | humanizePercentage }} full"
    
    # API错误率
    - alert: HighAPIErrorRate
      expr: |
        sum(rate(http_requests_total{status=~"5.."}[5m]))
        / sum(rate(http_requests_total[5m])) > 0.01
      for: 3m
      labels:
        severity: critical
      annotations:
        summary: "API error rate is {{ $value | humanizePercentage }}"

# ✅ 验证通过
kubectl apply -f alerting-rules.yaml
kubectl get prometheusrule -n monitoring
# NAME          AGE
# k8s-alerts    5s

三、Alertmanager配置

# alertmanager-config.yaml
apiVersion: v1
kind: Secret
metadata:
  name: alertmanager-monitoring-kube-prometheus-alertmanager
  namespace: monitoring
type: Opaque
stringData:
  alertmanager.yaml: |
    # 全局配置
    global:
      resolve_timeout: 5m
    
    # 路由树
    route:
      group_by: ['namespace', 'alertname']
      group_wait: 30s           # 同组告警等待时间
      group_interval: 5m        # 同组新告警间隔
      repeat_interval: 4h       # 重复告警间隔
      receiver: 'default'
      routes:
      - match:
          severity: critical
        receiver: 'critical'
        repeat_interval: 1h
      - match:
          severity: warning
        receiver: 'warning'
        repeat_interval: 6h
    
    # 抑制规则:critical告警抑制同组warning
    inhibit_rules:
    - source_match:
        severity: 'critical'
      target_match:
        severity: 'warning'
      equal: ['namespace', 'alertname']
    
    # 接收器
    receivers:
    - name: 'default'
      webhook_configs:
      - url: 'http://alertmanager-webhook:8080/alerts'
    
    - name: 'critical'
      # 企业微信/钉钉
      webhook_configs:
      - url: 'http://webhook-adapter:8080/critical'
        send_resolved: true
      # 邮件
      email_configs:
      - to: 'oncall@example.com'
        from: 'alertmanager@example.com'
        smarthost: 'smtp.example.com:587'
        auth_username: 'alertmanager@example.com'
        auth_password: '<password>'
    
    - name: 'warning'
      slack_configs:
      - api_url: 'https://hooks.slack.com/services/xxx'
        channel: '#alerts-warning'
        title: '{{ .GroupLabels.alertname }}'
        text: "{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"

# ✅ 验证通过 - 查看告警
kubectl port-forward svc/monitoring-kube-prometheus-alertmanager 9093:9093 -n monitoring
# 浏览器打开 http://localhost:9093

四、SLO(服务等级目标)

┌─────── SLO核心概念 ───────┐
│                             │
│  SLI(指标)               │
│  → 衡量服务的量化指标       │
│  例:可用性、延迟、错误率   │
│                             │
│  SLO(目标)               │
│  → SLI的目标值              │
│  例:99.9%可用性            │
│                             │
│  SLA(协议)               │
│  → 对客户的正式承诺         │
│  例:低于99.9%则赔偿       │
│                             │
│  错误预算 = 1 - SLO        │
│  99.9% SLO → 0.1%错误预算  │
│  = 每月43.2分钟不可用      │
└─────────────────────────────┘

4.1 基于SLO的告警

# slo-alerts.yaml - 错误预算消耗告警
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: slo-alerts
  namespace: monitoring
spec:
  groups:
  - name: slo-alerts
    rules:
    # 计算SLO可用性(30天窗口)
    - record: slo:availability:ratio
      expr: |
        (
          sum(rate(http_requests_total{status!~"5.."}[30d]))
          /
          sum(rate(http_requests_total[30d]))
        )
    
    # 错误预算消耗速率
    - record: slo:error_budget:burn_rate
      expr: |
        (
          1 - slo:availability:ratio
        )
        /
        (1 - 0.999)                # 99.9% SLO
    
    # 错误预算快速消耗告警(1小时消耗2.5%预算 → 约6天内耗尽)
    - alert: SLOErrorBudgetBurningFast
      expr: slo:error_budget:burn_rate > 14.4
      for: 1h
      labels:
        severity: critical
      annotations:
        summary: "SLO error budget burning fast for {{ $labels.service }}"
        description: "Error budget burn rate is {{ $value | printf \"%.1f\" }}x, budget will exhaust in ~{{ 720 | divf $value | printf \"%.0f\" }} hours"
    
    # 错误预算缓慢消耗告警(3天消耗2.5%预算 → 约30天内耗尽)
    - alert: SLOErrorBudgetBurningSlow
      expr: slo:error_budget:burn_rate > 1
      for: 3d
      labels:
        severity: warning
      annotations:
        summary: "SLO error budget burning for {{ $labels.service }}"
    
    # 可用性低于SLO
    - alert: SLOViolation
      expr: slo:availability:ratio < 0.999
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Availability {{ $value | humanizePercentage }} is below 99.9% SLO"

4.2 错误预算看板

# Grafana错误预算Dashboard关键查询

# 当前可用性(30天)
slo:availability:ratio * 100

# 剩余错误预算(百分比)
(1 - (1 - slo:availability:ratio) / (1 - 0.999)) * 100

# 错误预算消耗趋势
1 - slo:availability:ratio

# 每日请求量与错误量
sum(rate(http_requests_total[1d])) by (service)
sum(rate(http_requests_total{status=~"5.."}[1d])) by (service)

五、On-Call最佳实践

# On-Call轮值配置
# 1. 告警分级
#    P0 - 立即响应(5分钟内)
#    P1 - 30分钟内响应
#    P2 - 工作时间处理
#    P3 - 下一工作日处理

# 2. 告警降噪
# - 合理设置for持续时间
# - 使用group_by合并相关告警
# - 抑制规则:高级别抑制低级别
# - 静默:计划维护期间静默告警

# 3. Runbook
# 每条告警必须有对应Runbook:
# - 告警含义
# - 影响范围
# - 排查步骤
# - 修复方案
# - 升级路径

六、故障排查实战

6.1 告警风暴

# 现象:同时收到大量告警
# 解决:
# 1. 紧急静默
amtool silence add --author="admin" --comment="investigating" \
  alertname=PodCrashLooping namespace=default

# 2. 优化group_by
route:
  group_by: ['namespace', 'alertname', 'cluster']

# 3. 增大for持续时间
# for: 5m → for: 15m

# 4. 检查是否是级联故障
# 核心服务故障→依赖服务全部异常→大量告警

6.2 告警未触发

# 检查告警规则是否生效
kubectl get prometheusrule -A

# 在Prometheus UI查看Alerts
# Status: Inactive → 条件不满足
# Status: Pending → 满足条件但for时间未到
# Status: Firing → 正在告警

# 检查Alertmanager路由
amtool config routes --config-file=alertmanager.yaml

七、练习

  1. 创建PrometheusRule,配置Pod/Node/Deployment关键告警
  2. 配置Alertmanager路由:critical→邮件,warning→Slack
  3. 实现基于SLO的告警:错误预算消耗告警
  4. 为每条告警编写Runbook
  5. 模拟告警风暴,使用amtool静默和排查

🏆 第15课成就解锁

下一课预告:第16课深入Helm包管理——K8s应用打包与分发。

📌 补充知识

15-告警与slo补充要点:K8s生产实践扩展

🔹 资源配额(ResourceQuota):限制命名空间总资源
  apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: compute-quota
    namespace: production
  spec:
    hard:
      requests.cpu: "20"
      requests.memory: 40Gi
      limits.cpu: "40"
      limits.memory: 80Gi
      pods: "50"
      services: "10"

🔹 LimitRange:设置默认资源限制
  apiVersion: v1
  kind: LimitRange
  metadata:
    name: default-limits
  spec:
    limits:
    - type: Container
      default:
        cpu: "200m"
        memory: 256Mi
      defaultRequest:
        cpu: "100m"
        memory: 128Mi
      max:
        cpu: "2"
        memory: 4Gi

🔹 Pod优先级与抢占
  apiVersion: scheduling.k8s.io/v1
  kind: PriorityClass
  metadata:
    name: high-priority
  value: 1000000
  globalDefault: false
  ---
  spec:
    preemptionPolicy: PreemptLowerPriority

🔹 优雅处理Pod中断
  • PDB保证最小可用副本
  • preStop钩子处理连接排空
  • terminationGracePeriodSeconds充足
  • 应用必须处理SIGTERM信号

🔹 生产环境Checklist
  ✅ 设置resources requests/limits
  ✅ 配置liveness/readiness探针
  ✅ 使用PDB保护关键服务
  ✅ 实现优雅关闭(SIGTERM)
  ✅ 配置HPA自动伸缩
  ✅ 使用NetworkPolicy隔离
  ✅ 开启RBAC最小权限
  ✅ 日志结构化输出
  ✅ 指标暴露/metrics端点
  ✅ 配置PVC数据备份