可观测性即代码:仪表盘、告警规则与采集配置的 GitOps

系统讲解可观测性即代码(Observability as Code)的工程实践:仪表盘即代码与 Grafana as Code、告警规则即代码与单元测试、采集配置即代码、GitOps 工作流与 CI 校验、评审与版本回滚,以及常见避坑与最佳实践清单。

在 Grafana 界面上点几下就能加个面板、调个阈值,这种「所见即所得」的便利,正是可观测性配置失控的根源:没人知道某个告警阈值为什么是 5 分钟、谁在什么时候改的、改之前长什么样。可观测性即代码(Observability as Code)把仪表盘、告警规则、采集配置全部纳入 Git 管理,用 PR 评审、CI 校验与自动同步替代「点鼠标」。本文从三类配置的代码化讲到 GitOps 工作流与单元测试。

关键概念:可观测性即代码=把仪表盘、告警规则、采集配置当作源代码,用版本控制、评审、测试与自动化流水线来管理。核心收益不是「省事」,而是可评审、可回溯、可测试、可复现。



1. 为什么要可观测性即代码

1.1 手工配置的四宗罪

1. 不可追溯:阈值被改了,没人知道是谁、为什么改
2. 不可复现:新建集群要手工重配一遍,容易漏
3. 不可评审:一个错误的正则告警规则直接上生产
4. 不可测试:规则写错只有等它该响的时候才发现

1.2 三类可代码化对象

仪表盘(Dashboards):JSON 模型,可存文件、可导入
告警规则(Alert Rules):PrometheusRule CRD / Grafana 规则文件
采集配置(Collectors):OTel Collector config / Prometheus scrape config

1.3 收益量化

维度手工配置即代码
变更追溯无Git 历史完整
评审无PR 评审 + 责任人
测试无单元测试 + lint
复现手工一条命令
回滚手动改回git revert
环境一致性易漂移同一份代码多环境渲染
结论:配置规模一旦超过几十个面板、几十条规则,手工维护必然失控

2. 仪表盘即代码与 Grafana as Code

2.1 仪表盘的本质

Grafana 仪表盘 = 一个 JSON 文档
  包含:panels、queries、variables、layout、datasource 引用

导出方式:
  UI → Dashboard settings → JSON Model → 复制
  或 API:GET /api/dashboards/uid/{uid}

2.2 用 Grizzly 管理

Grizzly 是 Grafana 官方的 as-code 工具:
  grr pull   https://grafana.example.com   # 拉取现有仪表盘为文件
  grr push   dashboards/                   # 推送本地文件到 Grafana
  grr export                               # 导出为可版本化格式
# dashboards/api-latency.json 的引用配置示例
apiVersion: grizzly.grafana.com/v1alpha1
kind: Dashboard
metadata:
  name: api-latency
spec:
  title: API Latency Overview
  uid: api-latency
  schemaVersion: 39

2.3 用 Terraform 管理

resource "grafana_dashboard" "api_latency" {
  config_json = file("${path.module}/dashboards/api-latency.json")
  folder      = grafana_folder.observability.id
}

2.4 参数化与多环境

问题:仪表盘里写死了 datasource uid 与集群名
方案:用变量占位,渲染时注入
  - Jsonnet / Grafonnet:用代码生成 JSON,复用面板模板
  - Terraform 变量:同一份 HCL 渲染 dev/staging/prod
  - Grafana 内置变量:$datasource、$cluster、$namespace

3. 告警规则即代码与单元测试

3.1 PrometheusRule CRD

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: api-slo-rules
  namespace: observability
spec:
  groups:
  - name: api-slo
    interval: 30s
    rules:
    - alert: HighErrorRate
      expr: |
        sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
          / sum(rate(http_requests_total[5m])) by (service) > 0.05
      for: 10m
      labels:
        severity: critical
      annotations:
        summary: "服务 {{ $labels.service }} 错误率超过 5%"
        runbook_url: "https://runbook.example.com/high-error-rate"

3.2 用 promtool 做单元测试

# tests/api-slo-test.yml
rule_files:
- api-slo-rules.yml
evaluation_interval: 1m
tests:
- interval: 1m
  input_series:
  - series: 'http_requests_total{service="checkout",status="500"}'
    values: '0+10x20'
  - series: 'http_requests_total{service="checkout",status="200"}'
    values: '0+90x20'
  alert_rule_test:
  - eval_time: 12m
    alertname: HighErrorRate
    exp_alerts:
    - exp_labels:
        severity: critical
        service: checkout
运行:promtool test rules tests/api-slo-test.yml
价值:在 CI 里验证"这条规则在什么数据下会响、什么时候不响"
     避免"规则上线后才发现永远不触发"或"疯狂误报"

3.3 规则质量检查清单

□ 每条告警都有 for(避免瞬时抖动)与 severity 标签
□ 都有 summary 与 runbook_url
□ 表达式不含高基数 label 聚合(防 OOM)
□ 阈值有数据依据(SLO / 历史 P99),不是拍脑袋
□ 有对应的单元测试用例

4. 采集配置即代码

4.1 OpenTelemetry Collector 配置

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 75
  batch:
    timeout: 5s
    send_batch_size: 8192
  resource:
    attributes:
    - key: deployment.environment
      value: prod
      action: upsert
exporters:
  otlphttp:
    endpoint: http://tempo.observability:4318
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch, resource]
      exporters: [otlphttp]

4.2 Prometheus 采集配置

scrape_configs:
- job_name: kubernetes-service-endpoints
  kubernetes_sd_configs:
  - role: endpoints
  relabel_configs:
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
    action: keep
    regex: "true"
  - source_labels: [__meta_kubernetes_pod_label_app]
    target_label: service

4.3 配置分发的两种模式

模式一:配置内嵌 CRD(Prometheus Operator)
  改 PrometheusRule / ServiceMonitor → Operator 自动重载,天然 GitOps
模式二:配置文件 + ConfigMap 挂载
  ConfigMap 更新 → 挂载卷刷新 → 需 reload(SIGHUP 或 /-/reload)
  注意:reload 失败会静默不生效,必须监控

4.4 采集配置的验证

验证手段:
  otelcol validate --config=config.yaml       # OTel Collector 配置校验
  promtool check config prometheus.yml        # Prometheus 配置校验
  promtool check rules rules/*.yml            # 规则语法校验
全部可放进 CI,PR 阶段就拦住语法错误

5. GitOps 工作流与 CI 校验

5.1 标准流水线

开发者提交 PR
  → CI:lint + 单元测试 + 渲染校验
  → 评审:SRE / 值班同学 review
  → 合并到 main
  → GitOps 控制器(ArgoCD / Flux)检测差异并自动同步
  → 冒烟验证(规则是否加载、面板是否可见)

5.2 CI 校验内容

# .github/workflows/observability-ci.yml 关键步骤
steps:
- name: Validate Prometheus rules
  run: promtool check rules rules/*.yml
- name: Run rule unit tests
  run: promtool test rules tests/*.yml
- name: Validate OTel Collector config
  run: otelcol validate --config=collector/config.yaml
- name: Lint dashboards JSON
  run: ./scripts/lint-dashboards.sh
- name: Check dashboard datasource refs
  run: ./scripts/check-datasource-refs.sh

5.3 ArgoCD 同步策略

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: observability-config
spec:
  source:
    repoURL: https://git.example.com/sre/observability
    path: overlays/prod
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
selfHeal: true → 有人手工改了规则,会被自动改回
prune: true    → 删掉的文件对应资源也会被清理
注意:prune 有风险,务必先在非生产验证

6. 评审、版本与回滚实践

6.1 评审要点

告警规则评审清单:
  1. 这条告警对应哪个 SLO / 用户影响?
  2. 阈值依据是什么(历史数据 / 压测)?
  3. 有没有 for 与 severity?误报时怎么静默?有没有 runbook?
  4. 单元测试覆盖了吗?

仪表盘评审清单:
  1. 变量是否参数化(不写死集群/实例)?
  2. 查询是否高基数(可能拖垮 Prometheus)?

6.2 版本与回滚

分支策略:main 为唯一真相,变更走 PR
回滚:git revert <commit> → GitOps 自动同步回滚
关键:所有变更都有 Git 记录,回滚是"重放"而非"重做"

6.3 配置与环境的映射

目录结构示例:
  base/                    公共规则与面板
  overlays/dev/            开发环境差异(阈值放宽)
  overlays/prod/           生产(阈值严格)
Kustomize / Helm 渲染 base + overlay → 目标环境配置
收益:规则逻辑只写一份,环境差异用补丁表达

ℹ️ 核心:可观测性即代码的价值不在于「用文件代替界面」,而在于把配置纳入软件工程流程——评审、测试、版本、回滚。配置从此有了「责任人」和「历史」。


7. 常见避坑

坑现象对策
只导 JSON 不参数化多环境复制粘贴易漂移用变量与 overlay 渲染
规则无单元测试上线才发现不触发或狂响promtool test rules 进 CI
仪表盘写死数据源换环境后面板全空用 $datasource 变量
高基数查询上大盘Prometheus 被拖垮聚合后再画图,限制 label
配置无 lint语法错误直接进生产CI 加 check config
手工改生产配置与 Git 不一致,下次同步被覆盖selfHeal + 禁止手工改
prune 未验证误删生产告警规则先在非生产验证 prune
reload 静默失败配置改了没生效监控 reload 成功指标
无回滚预案出错只能手工修git revert + ArgoCD 回退
告警无 runbook值班不知怎么处理规则强制带 runbook_url

8. 最佳实践清单

□ 仪表盘、告警规则、采集配置全部纳入 Git 单一仓库
□ 用 base + overlay 表达环境差异,规则逻辑只写一份
□ 每条告警规则配单元测试,CI 中 promtool test rules
□ CI 集成 promtool check、otelcol validate、JSON lint
□ 仪表盘用变量参数化数据源与集群,不写死 uid
□ 用 ArgoCD/Flux 自动同步,开启 selfHeal 防手工漂移
□ 告警规则强制 for、severity、summary、runbook_url
□ 配置变更走 PR 评审,SRE 参与 review
□ 监控 reload/sync 成功率,防止静默失效
□ 建立 git revert 一键回滚流程并演练

一句话原则

可观测性即代码 = 配置进 Git + PR 评审 + CI 测试 +
GitOps 自动同步,让每条规则都有出处与回滚路径。

小结

可观测性即代码的本质,是把仪表盘、告警规则、采集配置从「界面上的临时操作」变成「仓库里的受控资产」。落地分三步:代码化——用 Grafana JSON、PrometheusRule CRD、OTel Collector 配置把三类对象落到文件;工程化——用 promtool test rules 做告警单元测试、用 lint 与 validate 做语法校验、用 PR 评审把关阈值依据;自动化——用 ArgoCD/Flux 做 GitOps 同步,开启 selfHeal 防止手工漂移。当「为什么这个阈值是 5 分钟」能在 Git 历史里找到答案时,可观测性配置才真正成为可靠的工程资产。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「infra」更多文章

  1. 多集群可观测性联邦与聚合:联邦查询、数据分片与全局视图
  2. 消息队列可观测性:Kafka 与 RabbitMQ 的滞后、积压与端到端延迟
  3. 数据库与查询层可观测性:慢查询、连接池与执行计划