在 Grafana 界面上点几下就能加个面板、调个阈值,这种「所见即所得」的便利,正是可观测性配置失控的根源:没人知道某个告警阈值为什么是 5 分钟、谁在什么时候改的、改之前长什么样。可观测性即代码(Observability as Code)把仪表盘、告警规则、采集配置全部纳入 Git 管理,用 PR 评审、CI 校验与自动同步替代「点鼠标」。本文从三类配置的代码化讲到 GitOps 工作流与单元测试。
关键概念:可观测性即代码=把仪表盘、告警规则、采集配置当作源代码,用版本控制、评审、测试与自动化流水线来管理。核心收益不是「省事」,而是可评审、可回溯、可测试、可复现。
- 1. 为什么要可观测性即代码
- 2. 仪表盘即代码与 Grafana as Code
- 3. 告警规则即代码与单元测试
- 4. 采集配置即代码
- 5. GitOps 工作流与 CI 校验
- 6. 评审、版本与回滚实践
- 7. 常见避坑
- 8. 最佳实践清单
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 历史里找到答案时,可观测性配置才真正成为可靠的工程资产。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。