ES transport证书p12到期监控实现(CronJob推送指标)

ES transport证书p12到期监控实现(CronJob推送指标)

记录时间:2026-09-07
环境:dyck 生产 K8s(RKE,ARM64 信创节点)/ Elasticsearch 7.17 三节点集群(StatefulSet dyck-elasticsearch,tools namespace)/ VictoriaMetrics 集群(部署在 kjds,入口域名 prod.vm.example.com)/ elasticsearch-exporter v1.11.0

一、背景

dyck 生产 ES 集群的节点间互信(transport 层,9300 端口)用的是 elasticsearch-certutil 自签的 p12 证书,以 Secret dyck-elastic-certificates 挂载进容器。我查了当前证书状态,2029-03-29 到期。

这个证书过期的影响比看起来严重:TLS 证书验证发生在握手阶段,存量连接过期后还活着,集群表面正常,但任何一次节点重启、网络闪断触发连接重建时握手直接失败,节点掉出集群回不来,分片堆积滚向 red。故障时刻不等于过期时刻,等爆出来排查会走弯路。

监控这条路有个前提问题:elasticsearch-exporter(我用的 v1.11.0)不采集 _ssl/certificates 这个 xpack API,标准采集方式拿不到证书剩余天数。好在 ES 原生就带这个接口,我手动调用确认输出里直接有 expiry 字段,数据源现成。

我的方案:CronJob 每天调一次这个 API,取最早过期的证书换算剩余天数,按 Prometheus 文本格式推给 VM,vmalert 按阈值评估告警。更彻底的做法是重签一份 10 年证书一次性解决,操作单我已经备好,但当前离过期还有两年半,先落监控兜底,重签排期再做。

镜像说明(重要):本方案用到的两个镜像都是我自己构建/转推进 Harbor 的,集群内不用 Docker Hub:

镜像 来源 Harbor 地址
elasticsearch-exporter 官方镜像 pull 后重打 tag reg-hub.example.com/library/prometheuscommunity/elasticsearch-exporter:v1.11.0
curl-jq 基于 alpine 自建(alpine 官方镜像不带 curl 和 jq,我加了一层 apk add reg-hub.example.com/library/curl-jq:latest

二、操作过程

2.1 构建并推送镜像

exporter 用官方镜像转推(注意 dyck 节点是 arm64,--platform 要对):

docker pull --platform linux/arm64 prometheuscommunity/elasticsearch-exporter:v1.11.0
docker tag prometheuscommunity/elasticsearch-exporter:v1.11.0 \
  reg-hub.example.com/library/prometheuscommunity/elasticsearch-exporter:v1.11.0
docker push reg-hub.example.com/library/prometheuscommunity/elasticsearch-exporter:v1.11.0

curl-jq 没有现成的官方组合镜像(官方 curlimages/curl 实测不带 jq),我自己写了两行 Dockerfile:

FROM alpine:3.20
RUN apk add --no-cache curl jq
docker build -t reg-hub.example.com/library/curl-jq:latest .
docker push reg-hub.example.com/library/curl-jq:latest

注意:构建机的架构要和集群节点一致(我直接在 arm64 机器上 build),x86 机器跨架构 build 需要 buildx + QEMU。

2.2 部署 CronJob

完整清单在仓库 prometheus/kube-prometheus-stack/prod-xc-kube-prometheus-stack/other/elasticsearch-exporter/es-cert-expiry-check-cronjob.yaml,核心是 ConfigMap 脚本 + CronJob 两段。

脚本逻辑:带认证调 _ssl/certificates,jq 取最早过期的证书转 epoch,减当前时间得剩余天数,拼成文本格式的指标 POST 给 vminsert 的导入端点:

#!/bin/sh
set -eu
ES_PW="$(cat /run/secrets/es-pw/password)"
JSON="$(curl -sf --max-time 30 -u "elastic:${ES_PW}" \
  "http://dyck-elasticsearch.tools.svc.cluster.local:9200/_ssl/certificates")"
MIN_EPOCH="$(echo "$JSON" | jq -r '[.[].expiry | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601] | min')"
[ -n "${MIN_EPOCH}" ] || { echo "ERROR: no expiry parsed from API response"; exit 1; }
NOW="$(date +%s)"
DAYS=$(( (MIN_EPOCH - NOW) / 86400 ))
METRIC="es_transport_cert_expiry_days{systemName=\"dyck\",cluster=\"dyck-prod-xc\",job=\"dyck-es-cert-expiry-check\"} ${DAYS} $(date +%s)000"
curl -sf --max-time 30 -u "${VM_USER}:${VM_PASS}" \
  -X POST "http://prod.vm.example.com/insert/0/prometheus/api/v1/import/prometheus" \
  --data-binary "${METRIC}"

CronJob 关键参数:

参数 说明
schedule 0 6 * * * 每日 06:00 低峰执行,告警粒度 ±1 天
镜像 library/curl-jq:latest 自建镜像(见 2.1)
hostAliases 10.202.17.91 → prod.vm.example.com 集群内 DNS 解析不了这个域名,必须写 hosts
env 凭据 vm-remote-write-basic-auth VM 推送认证,与 Prometheus remoteWrite 同源
volume 挂载 dyck-elasticsearch-password ES 密码经文件读入,脚本零明文

推送地址我用的是 prod.vm.example.com/insert/0/prometheus/api/v1/import/prometheus,这个前置条件有两个,缺一不可:

  1. hostAliases:集群内 DNS 解析不了公司内部域名,我在 CronJob 的 Pod spec 里写了与 dyck Prometheus 同款的 hosts 条目(10.202.17.91 是 kjds 的 ingress VIP);
  2. tools namespace 的 Secret 副本:K8s 的 secretKeyRef 不能跨 namespace 引用,monitoring 里的 vm-remote-write-basic-auth 不会自动共享,我在 tools 建了一份同值的。

2.3 配置 VMRule 告警

规则追加在仓库 victoriametrics/xc-kjds/alerting/rules/elasticsearch/vmrule-elasticsearch.yaml,三条:

- alert: ESTransport证书即将过期
  expr: es_transport_cert_expiry_days < 30
  for: 1h
  labels:
    severity: warning

- alert: ESTransport证书过期临近红线
  expr: es_transport_cert_expiry_days < 7
  for: 1h
  labels:
    severity: critical

- alert: ES证书检查数据中断
  expr: (time() - timestamp(es_transport_cert_expiry_days{job="dyck-es-cert-expiry-check"})) > 172800
  for: 1h
  labels:
    severity: warning

第三条是给我自己这套检查任务上的保险:指标 48 小时没更新(ES 认证轮换没同步、CronJob 被删等),告警本身先报出来,监控不静默变盲。

三、验证

手动触发一次(不用等凌晨的 schedule):

kubectl apply -f es-cert-expiry-check-cronjob.yaml
kubectl create job --from=cronjob/dyck-es-cert-expiry-check dyck-es-cert-test -n tools
kubectl logs -n tools job/dyck-es-cert-test

预期输出两行:

cert earliest expiry days remaining: 939
pushed to VM ok

vmui 查询确认序列与值:

es_transport_cert_expiry_days{job="dyck-es-cert-expiry-check"}   # ≈ 939

之后每天 06:00 刷新一次,跌破 30 天微信收到 warning,7 天 critical。

四、注意事项 / 遇到的问题

这几个问题都是我实际撞到的,按踩到的顺序记:

  1. exporter v1.11.0 不消费 ES_URI/ES_PASSWORD 环境变量。我最初用 env 方式注入地址和密码,Pod 起来了、targets 也是 UP,但 /metrics 里只有 go 和 process 自身指标,一个 elasticsearch_* 都没有,启动日志干净得只有两行 Listening。最后确认进程拿着默认值 localhost:9200 在空转。解决:认证地址改写在启动参数里 --es.uri=http://elastic:xxx@...(k3s 那套的老写法)。代价是密码明文出现在 Pod spec,换密码时 command 和 Secret 两处要同步改。
  2. v1.11.0 镜像的 USER 是字母形态 nobody,和清单里的 runAsNonRoot: true 冲突(kubelet 要求数字 UID 才能验证非 root),Pod 起不来。解决:容器级显式 runAsUser: 65534
  3. 官方 curlimages/curl 镜像不带 jq(我原本想当然认为带了),Job 直接 jq: not found。解决:换我自建的 curl-jq 镜像。

五、参考资料

  • ES 证书 API:https://www.elastic.co/guide/en/elasticsearch/reference/7.17/security-api-ssl.html
  • elasticsearch_exporter:https://github.com/prometheus-community/elasticsearch_exporter
  • VM 文本导入端点:https://docs.victoriametrics.com/#how-to-import-data-in-prometheus-exposition-format
  • 仓库部署清单:prometheus/kube-prometheus-stack/prod-xc-kube-prometheus-stack/other/elasticsearch-exporter/
  • 告警规则:victoriametrics/xc-kjds/alerting/rules/elasticsearch/vmrule-elasticsearch.yaml
暂无评论

发送评论 编辑评论


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