Prometheus 切换到 VictoriaMetrics 统一告警的部署记录

Prometheus 切换到 VictoriaMetrics 统一告警的部署记录

记录时间:2026-08-27
环境:RKE 信创 ARM64 双生产集群(clusterA / clusterB)+ 公司测试集群 / victoria-metrics-cluster 0.42.0(v1.143.0-cluster)/ victoria-metrics-operator 0.67.2(v0.74.0)/ kube-prometheus-stack 58.7.2 / PrometheusAlert v4.9.2

一、事情的由头

起因是一条 Swap 告警风暴:clusterA 一台主机 Swap 使用率 critical 告警 10 分钟一循环,规则是瞬时值直接比 80%,恢复侧没滞后,指标贴着阈值穿插,就变成 firing、resolved、再 firing。修好之后我想,两集群数据反正都写进 VM 了,告警为什么不也统一到 VM 评估,一处发。

目标 起始 完成
数据汇聚 两集群 Prometheus 已写 VM 不变
统一告警引擎 各集群 PrometheusRule 各自评估 vmalert + VMRule 一处评估
告警分发 clusterA 的 Alertmanager + PAC 复用现有 PAC,不加跨集群依赖

1.1 参考文章对照

后来翻到一篇”百万级指标切 VM 集群版”(WAKE UP技术),几个要点跟我的选择有差异有重合:

文章做法 我的选择 差异原因
vmagent 替换 Prometheus 采集 Prometheus 继续采集 remoteWrite 进 VM 我停在九阶段第 4 步稳态,采集、短期存储、旧告警兜底都留 Prometheus
replicationFactor 2 1,无副本 存储是集中式 SSD NFS,两份副本写同一阵列买不到独立故障域
集群版 2+2+2 同款 扩容原理一致:vminsert 按标签哈希分片、vmselect 广播聚合
k8s-stack chart 全家桶 裸 operator chart 加 CR 逐步搭 不动现有 Prometheus 体系;遇到的是另一边的坑:prometheus-converter 自动转换
NodePort Service Ingress 域名 + AM hostAliases 跨集群解析 我要跨集群访问 dyck PAC,NodePort 解决不了域名路由
Grafana 填 /select/0/prometheus/ 同款 一致

二、方案我前后翻了三版

2.1 第一版:victoria-metrics-alert 裸 chart,被否

起手用官方 victoria-metrics-alert chart,改一条规则要 helm upgrade 整个 release,站不住。改成独立 ConfigMap 挂载加 -configCheckInterval 热加载,又发现所有规则堆一个 yaml 会膨胀,而 ConfigMap 卷只能点名挂载,加新业务域文件还得改 values。绕 ConfigMap 模拟 CRD,总归有缺口。

2.2 第二版:全 operator

把现有 VM 全改造成 operator 行不行?VMAlert、VMAlertmanager、VMRule 全部 CR 化。VMRule 的 groups 语法跟 PrometheusRule 完全兼容,迁移几乎改个 kind,按业务域一文件、apply 秒级热加载,正是想要的体验。

2.3 第三版(终态):存储层不动,只上告警链

阶段二收编(helm 版 VM 集群改 VMCluster CR)动手前夜我掂量了一句:”helm 版跑了这么久不是更好吗,为什么一定要收编?”算完账认账:零功能收益的形态统一,代价是没演练过的 PVC 重绑加停机窗口。终态:告警链走 operator,存储层留 helm。 中间还试过裸 chart 装 vmalert 配 VMRule,推演后否了:operator 的 VMRule 动作链是 watch 规则、渲染配置、挂到它自己建的 vmalert,野生 vmalert 不在流程里,要 VMRule 的爽就必须让 operator 建 vmalert,没有中间态。

2.4 采集层也按兵不动:为什么不用 VMAgent 和 VMServiceScrape

VM operator 给抓取层也准备了一套等价 CRD:VMServiceScrape 对标 ServiceMonitor、VMPodScrape 对标 PodMonitor,还有 VMNodeScrape、VMProbe、VMStaticScrape,由 VMAgent 消费。VMServiceScrape 必须配 VMAgent 才有意义,Prometheus 不认,VMAgent 也不看 ServiceMonitor。operator 默认的 prometheus-converter 会自动转换 ServiceMonitor→VMServiceScrape、PrometheusRule→VMRule,测试环境我见过它把 kube-prometheus-stack 规则全量转换、日志刷屏,生产配了 disable_prometheus_converter: true

采集层不换 VMAgent,三个理由:采集层没坏(换等于重写全部采集配置)、零功能收益(跟收编同理)、Prometheus 本地短期存储加 WAL 兜底(remoteWrite 断链自动补写,VM 挂了不全瞎)。哪天真要统一(省内存或全面替换),converter 或改 kind 就能迁,成本不高。没坏的东西别动它。

三、测试环境从零部署

生产是双集群,动手前先在测试集群完整跑一遍。目录按环境分组:xc-test/ 下分 operator/cluster/alerting/

3.1 组件清单

组件 形态 关键配置
victoria-metrics-operator helm chart 0.67.2 disable_prometheus_converter: true
VMCluster CR 2+2+2,retention 180d
VMAuth CR + Secret 统一入口,/select 与 /insert 分发,basic auth
VMAlert CR datasource 指 vmselect,selectAllByDefault: true
VMAlertmanager CR + Secret configSecret 模式,webhook 指测试 PAC
VMRule CR 每业务域一个文件

3.2 部署命令

helm upgrade --install victoria-metrics-operator victoria-metrics-operator-0.67.2.tgz -n monitoring -f operator/values.yaml
kubectl apply -f cluster/vmcluster.yaml
kubectl apply -f cluster/vmauth.yaml
kubectl apply -f alerting/vmalertmanager.yaml
kubectl apply -f alerting/vmalert.yaml
kubectl apply -f alerting/vmrule-node-exporter.yaml

3.3 测试环境遇到的六个问题

  1. prometheus-converter 批量转换。日志大片 failed to reconcile VMRule,是 converter 把 kube-prometheus-stack 的规则全量转成 VMRule、ServiceMonitor 转成 VMServiceScrape,配 selectAllByDefault 迟早告警风暴。处理:disable_prometheus_converter: true,删掉已转的 48 个对象。
  2. 命名是前缀式。svc 名 vmauth-vmauthvmselect-victoria-metrics-cluster,跟 helm 后缀式(victoria-metrics-cluster-vmselect)相反,我按 helm 习惯写地址,vmauth 报 backend unavailable。operator 系资源名一律记 组件名-<CR名>
  3. VMAuth 的 Secret key 必须是 config.yaml。我按 AM 的习惯写了 auth.yaml,Pod 卡 ContainerCreating 报 key 不存在。两个 CR 的 configSecret key 约定不一样,各记各的。
  4. Alertmanager 不认 loggerTimezone。VM 组件专属 flag 配给 prom/alertmanager,启动即退出 unknown long flag,AM 时区用 TZ 环境变量。
  5. sts 的 volumeClaimTemplates 不可变。换 storageClassName apply 显示 configured 就是不生效,换存储类只能删 CR 加 PVC 重建。
  6. remoteWrite 变更走热加载。加完 remoteWrite Pod 没滚动,我一度以为没生效,实际 config-reloader 直接 reload 进程,Pod AGE 不变是正常的。

验收:vmrule 放永真规则(expr: vector(1)),1 到 2 分钟测试企业微信群收到通知,vmalert → AM → PAC → 微信全通。

四、生产阶段一投产

阶段一只上告警链,datasource 直接指现有 helm 版 vmselect,VM 集群本体不碰,零风险;阶段二收编后来挂起(见 2.3)。

4.1 通知出口,来回最多的一块

第一版想 clusterB 本地装新 PAC。再查 dyck 的 PAC 配置发现它写 ES(alert_to_es=1),我初版关成 0,这不是平移是降级。又提启用 dyck ES 的 NodePort 让 clusterB 直连写,又否了:为一个记录功能暴露 ES 不值。最后定:PAC 用 dyck 的,AM 跨集群 webhook 指过去。

4.2 hostAliases 查证,我错了三次

复用 dyck PAC 要 clusterB 的 AM 解析 dyck ingress 域名,用 hostAliases 试试。我一开始不信,查了三处:chart 的 crd.yaml 没有、集群 CRD grep 没有、主 types 文件没有,三处都没有就下了结论不支持。翻官网文档一看明明白白支持,再翻源码,字段藏在嵌入的共享结构 CommonAppsParams 里,CRD 对它是 schemaless 处理不显式列出。schema 查不到不等于不支持。测试集群 patch 一下,hostAliases 立刻出现在生成的 StatefulSet 里,当场打脸。这条记死了:CR 字段要查嵌入结构,静态查证永远留余量,实测才是金标准。

4.3 生产专属的两个集群级问题

  1. ResourceQuota 强制所有容器带 limits。operator 注入的 config-init 和 config-reloader 边车没配资源,vmalert Pod FailedCreate(must specify limits.cpu for: config-init,config-reloader)。解法是 CR 的 configReloaderResources 字段,同样藏在共享结构里。
  2. 边车镜像默认走 Docker Hub。config-reloader 镜像名没 registry 前缀,信创环境拉不下来。解法是 operator chart 的 global.image.registry。注意这个 chart 的 global 对边车生效,跟 victoria-metrics-alert chart 的 global 不生效是两回事,各自验证。

4.4 两个配置字段错误

  • AM webhook 的 TLS 层级tls_config 直接挂 webhook 条目下,operator 拒绝创建 AM,报 field tls_config not found in type webhook.plain。正确是 http_config.tls_config,六处全改。
  • vmalert remoteWrite URL 双路径。日志全是 400,路径是 /insert/0/prometheus/api/v1/write/api/v1/write,vmalert 自己会拼 /api/v1/write,我 URL 又写了一遍。删掉重写那截,400 消失。这个错直接导致 ALERTS 序列写不进 VM,Grafana 查不到新链告警历史。

4.5 端到端验收

生产 vmalert 加永真测试规则后,企业微信收到”告警链路连通性测试”通知,时间戳 +08:00 正确。整条路径:vmalert(clusterB,读 VM 汇聚数据)→ VMAlertmanager(hostAliases 跨集群解析加 https)→ dyck PAC → 企业微信。

五、规则迁移分批落地

5.1 顺序与铁律

四个批次:redis 13 条练手,kafka 18 条,node-exporter 33 条,k8s 拣选 37 条收尾。每批守同一条铁律:先 dyck 侧 PrometheusRule 下线再本侧 VMRule 启用,同一告警绝不允许两条链同时发。

业务三批转换手法一致:仓库 PrometheusRule 全文转 VMRule,groups 语法原样,labels 模板引用行(systemName: "{{ $labels.systemName }}")删掉(告警标签 = 序列全部标签,无需再声明),按业务域一目录一文件放 alerting/rules/<域>/,apply 即生效。

5.2 k8s 自带规则只拣选不搬全量

自带 128 告警加 85 记录,全量搬不现实。clusterB 的 19 个自带 PrometheusRule 无通知出口原样留;dyck 侧按”实际触发加运维价值”拣选 37 条:Pod 反复崩溃重启、Pod 长时间未就绪、HPA 副本到上限、Deployment 副本不一致、配额接近上限与已超限、PVC 空间严重不足、节点 NotReady 与不可达、Kubelet/APIServer/CM/Scheduler 不可用、etcd 全组 15、Alertmanager 自监控 8。告警名文案全中文化,拆六个文件(apps/resources/storage/control-plane/etcd/alertmanager)。

提取手法跟业务批不同:helm template 渲染 chart 后脚本按告警名提取变体,校验每条 expr 与渲染产物逐字符一致。中间有个反复处理的点:脚本默认序列化把多行 expr 写成带转义符的引号串("(\n 这种),改成块标量写法,多行字符串直接按块输出,干净了。

两个适配点:AM 自监控组的 job 选择器从 dyck 老 AM 的 pc-kube-prometheus-stack-alertmanager 换成 vmalertmanager-alertmanager(终态活着的 AM 只有它);etcd 组待命(依赖 job=~".*etcd.*" 抓取,当前没配 etcd 证书,规则不误报,配了自动生效)。

5.3 两个数据前提先补齐

  1. kubelet 的 volume 指标。KubePersistentVolumeFillingUp 匹配 metrics_path="/metrics",原 cadvisor 抓取是 /metrics/cadvisor,路径对不上规则永远没数据。additionalScrapeConfigs 里给 cadvisor 加 metrics_path 标签补成 /metrics,再加一个抓 kubelet 主端点的 job(rancher-kubelet-metrics)专取 kubelet_volume_stats_*。两集群配置对齐后 PVC 规则才有数据。
  2. VMAlertmanager 指标抓取。AM 自监控 8 条规则的数据源是 AM 自身指标,先有 ServiceMonitor 把 vmalertmanager-alertmanager 抓进 clusterB Prometheus。文件按”一服务一目录”归到 stack 目录 other/vmalertmanager/,跟 cadvisor、kafka 平级。

5.4 dyck 侧收尾:全量下线加 AM 退役

业务三批逐个删 PrometheusRule(kubectl delete prometheusrule redis-rules kafka-rules node-exporter-rules)。k8s 批原计划 defaultRules.disabled 按名禁用,后来一步到位:两集群 values 都改 defaultRules.create: false,整块规则不渲染。验证时 kubectl get prometheusrule -n monitoring 返回 No resources found,比禁用更彻底,monitoring 下所有 PrometheusRule 对象全量回收,dyck 本地规则评估清零。

dyck 的 Alertmanager 同时退役:alertmanager.enabled: false,升级后 sts、deploy、ingress 清场,只剩一条 AM 的 PVC(nflog 和 silence 数据,10Gi nfs-dyck),无人消费,留还是删先记账。

终态达成:告警唯一源 = clusterB vmalert(101 条),经 VMAlertmanager 发 dyck PAC 到企业微信kubectl get vmrule 全部 operational,微信收到中文版测试告警。

六、告警怎么看,收尾怎么收

6.1 告警查看入口

vmalert UI 的 Alerts 页一度让我以为是版本 bug:API(/api/v1/alerts)有 firing 数据,页面点 Alerts 是空的,F12 里 Network 和 Console 也干净。折腾半天最后换浏览器试出真相:Chrome 一切正常,Firefox 就是空白,是该版本 UI 跟 Firefox 兼容性差。日常看告警换 Chrome,另外两个入口:Grafana 大屏(ALERTS{alertstate="firing"} 六面板,按严重级别和业务域分布)、API 直查。

触发时间两处来源:ALERTS_FOR_STATE 样本值就是进入状态的 Unix 时间戳,time() - ALERTS_FOR_STATE 直接得持续时长;Grafana 里乘 1000 配 Datetime 显示。

6.2 收尾动作

  1. 删除测试规则组。
  2. run.sh 定稿终态运维手册;规则迁移章节标完成态,新规则走”复制现有 VMRule 改 name/groups 再 apply”。
  3. 目录整理:cluster/ 放现行 helm 集群配置,阶段二收编 CR 改名 cluster.后续/ 挂起,废弃裸 chart 目录删除。
  4. 文档同步:README 更新终态(clusterA 采集加 PAC 所在地、迁移进度表全完成、AM 退役),memory 归档。
  5. 待办挂账:退役 AM 的 PVC 残留处置(10Gi nfs-dyck)。

七、遇到的问题速查表

现象 根因 解法
operator 启动大量转换日志 prometheus-converter 默认开启 disable_prometheus_converter: true
vmauth 转发 backend unavailable svc 名写成 helm 后缀式 全部改前缀式 组件-<CR名>
vmauth Pod 卡 ContainerCreating Secret key 错 key 必须是 config.yaml
AM 启动即退出 配了 VM 专属的 loggerTimezone 删掉,TZ env 足够
换存储类不生效 sts volumeClaimTemplates 不可变 删 CR 加 PVC 重建
Prometheus 加 remoteWrite 后 Pod 没滚 配置变更走热加载 正常现象,直接验证数据
vmalert Pod FailedCreate ResourceQuota 要求边车带 limits configReloaderResources
边车镜像拉不下来 默认 Docker Hub 无前缀 operator chart 配 global.image.registry
AM 创建被拒 unmarshal error tls_config 层级错 http_config.tls_config
remoteWrite 报 400 双路径 URL 写全了 api/v1/write 写到 /insert/0/prometheus 为止
Grafana 查不到新链告警 上一个问题导致 ALERTS 没入库 同上修复后即恢复
vmalert UI Alerts 页空(火狐) 该版本 UI 与 Firefox 兼容性差 换 Chrome,或 AM UI / Grafana 大屏
AM 自监控规则无数据 ServiceMonitor 未 apply 先 apply other/vmalertmanager/ 下的 ServiceMonitor
脚本生成的 expr 带转义符 序列化默认写引号串 块标量写法
迁移规则后疑似双发 忘记先下线 dyck 侧旧规则 铁律:先下线再启用

八、注意事项

  • 迁移规则防双发是最高优先级纪律。同一告警两条链同时发,值班群被打扰两遍,还会掩盖”到底哪条链在报”。
  • vector(1) 测试规则是字面量,不查数据源。只能验收”评估到通知”这一段,datasource 连通性和 remoteWrite 入库要单独验证(看 Rules 页 lastError、查 VM 里 ALERTS 序列)。
  • 同一个 global 字段,换 chart 先实测。victoria-metrics-alert 的 global 不生效要组件级显式配,victoria-metrics-operator 的 global 对边车生效。
  • 测试与生产的存储层是两种管理形态(测试 operator、生产 helm),存储层变更不能两边照搬,告警链同构可以先行。

九、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇