跳到主要内容

运维与监控

监控入口​

etcd 自带 Prometheus 格式的 /metrics,不用装 exporter:

# HTTP 场景
curl -s http://127.0.0.1:2379/metrics | grep '^etcd_'

# HTTPS 场景
curl -s --cacert /etc/etcd/certs/ca.crt \
--cert /etc/etcd/certs/client.crt \
--key /etc/etcd/certs/client.key \
https://127.0.0.1:2379/metrics | grep '^etcd_'

kubeadm 集群默认把 metrics 单独开在 127.0.0.1:2381(--listen-metrics-urls),不走 2379 的客户端 TLS,抓取时注意端口和协议。/metrics 里指标很多,真正需要盯的就那十几个,下面按「选主、RAFT、容量、磁盘、watch」五类拆开。

指标名前缀

etcd 的指标分两个前缀:etcd_ 是稳定指标,可以直接用于告警;etcd_debugging_ 是调试指标,字段和行为可能随版本变化,但 watch、压缩相关的关键数据目前只在这里。用调试指标做告警时,升级 etcd 后要回归验证一遍。

关键指标​

先给一张总表,后面逐类解释:

指标类型看什么
etcd_server_has_leaderGauge有没有 leader,0 就是集群不可用
etcd_server_leader_changes_seen_totalCounterleader 切换次数,频繁切换说明不稳定
etcd_server_proposals_pendingGauge待提交提案数,持续大于 0 说明写入受阻
etcd_server_proposals_failed_totalCounter提案失败总数,任何增长都值得查
etcd_server_proposals_committed_totalGauge已提交提案数,配合 applied 看积压
etcd_server_proposals_applied_totalGauge已应用提案数
etcd_server_slow_apply_totalCounterapply 慢的次数,通常和慢盘相关
etcd_server_slow_read_indexes_totalCounter线性读超时次数
etcd_disk_wal_fsync_duration_secondsHistogramWAL fsync 耗时,磁盘性能的核心指标
etcd_disk_backend_commit_duration_secondsHistogrambbolt 提交耗时
etcd_mvcc_db_total_size_in_bytesGaugedb 文件大小
etcd_mvcc_db_total_size_in_use_in_bytesGaugedb 实际使用量,和上者对比看碎片
etcd_server_quota_backend_bytesGauge后端配额大小
etcd_debugging_mvcc_db_compaction_lastGauge最近一次压缩到的 revision
etcd_debugging_mvcc_db_compaction_keys_totalCounter累计压缩掉的 key 数
etcd_network_peer_round_trip_time_secondsHistogram成员间网络 RTT
etcd_debugging_mvcc_watcher_totalGauge当前 watcher 数量
etcd_debugging_mvcc_slow_watcher_totalCounter慢 watcher 次数,watch 泄漏的信号
os_fd_used / os_fd_limitGauge文件描述符使用量和上限

leader 与选主​

etcd_server_has_leader 是最直接的可用性指标:集群里每个成员都应该为 1。出现 0 说明这个成员认为集群没有 leader,通常是网络分区、多数派丢失或者选主卡住。

etcd_server_leader_changes_seen_total 是 counter,看的是一段时间内的增量:

# 最近 1 小时每个成员的 leader 切换次数
increase(etcd_server_leader_changes_seen_total[1h])

# 最近 24 小时的总切换次数
sum(increase(etcd_server_leader_changes_seen_total[24h]))

一个稳定运行的集群,这个值应该长期为 0。一天切几次还能解释成网络抖动或磁盘卡顿;一小时切好几次,基本可以确定有成员在反复失联,接下来就是 apiserver 大量超时。

RAFT 延迟:propose、commit、apply​

一次写请求在 Raft 里分三段:

propose(leader 接受提案)
→ commit(多数派落盘并确认)
→ apply(应用到本地 MVCC 和 bbolt)

etcd 没有直接暴露「propose 到 commit 的耗时直方图」,要用几个指标组合判断:

  • propose 段:看 etcd_server_proposals_pending。它表示当前等待提交的提案数,正常情况下接近 0,持续大于 5 说明 leader 处理不过来,或者网络/磁盘拖慢了复制。
  • commit 段:看 etcd_disk_wal_fsync_duration_seconds(leader 本地落盘)和 etcd_network_peer_round_trip_time_seconds(复制往返)。写请求要等多数派,所以这两个指标的最慢值决定了写延迟。
  • apply 段:看 proposals_committed_total 和 proposals_applied_total 的差值,以及 etcd_server_slow_apply_total 的增量。差值持续拉大,说明提交上去了但本地状态机应用不过来,通常是 bbolt 写入慢或 CPU 饱和。

常用查询:

# 待提交提案数
etcd_server_proposals_pending

# 已提交但未应用的提案积压
etcd_server_proposals_committed_total - etcd_server_proposals_applied_total

# WAL fsync P99 延迟(秒,按成员看)
histogram_quantile(0.99, sum by (instance, le) (rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])))

# backend commit P99 延迟(秒,按成员看)
histogram_quantile(0.99, sum by (instance, le) (rate(etcd_disk_backend_commit_duration_seconds_bucket[5m])))
带 _total 后缀的 Gauge

etcd_server_proposals_committed_total 和 etcd_server_proposals_applied_total 名字里虽然有 _total,在 3.5 的实现里是 Gauge。直接做差或用 increase() 看增量,但不要被后缀误导成「重启归零的计数器」。

db 大小与 revision​

etcd_mvcc_db_total_size_in_bytes 是 bbolt 文件的实际大小,etcd_mvcc_db_total_size_in_use_in_bytes 是其中有效数据的大小。两者差距越大,说明碎片越多,该 defrag 了。

# db 大小(GiB)
etcd_mvcc_db_total_size_in_bytes / 1024 / 1024 / 1024

# 碎片率
(etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes) / etcd_mvcc_db_total_size_in_bytes

# 配额使用率
etcd_mvcc_db_total_size_in_bytes / etcd_server_quota_backend_bytes

revision 本身没有单独的指标,但可以从 etcd_mvcc_put_total 之类的写入计数和 db 增长速度间接判断。日常更实用的是直接查:

etcdctl endpoint status -w json | jq '.[0].Status | {dbSize, dbSizeInUse, revision: .header.revision}'

db 增长快一般有两个原因:写入量大(K8s 里大量 Pod 状态更新、Event 堆积)和历史版本没有及时压缩。先看自动压缩有没有生效,再看是不是业务在疯狂写。压缩是否跟上,用 etcd_debugging_mvcc_db_compaction_last 和当前 revision 对比:两者差距持续拉大,说明压缩周期太长或者压缩没跑起来。

磁盘与网络​

etcd 对磁盘的要求可以浓缩成一句话:fsync 的 P99 必须稳定在 10ms 以内。

  • etcd_disk_wal_fsync_duration_seconds 的 P99 超过 10ms,说明磁盘已经开始拖后腿;超过 25ms 基本可以确定写入会超时,leader 也可能因为心跳发不出去而抖动。
  • etcd_disk_backend_commit_duration_seconds 反映 bbolt 提交,P99 持续偏高同样指向磁盘。
  • etcd_network_peer_round_trip_time_seconds 的 P99 应该在毫秒级。跨机房部署时这个值会明显变大,选举和写延迟都会受影响。
  • etcd_server_heartbeat_send_failures_total 增长,说明 leader 连心跳都发不出去,优先查网络和磁盘。
# 成员间 RTT P99(秒,按成员看)
histogram_quantile(0.99, sum by (instance, le) (rate(etcd_network_peer_round_trip_time_seconds_bucket[5m])))

# 心跳发送失败速率
rate(etcd_server_heartbeat_send_failures_total[5m])

watch 与连接​

apiserver 和控制器对 etcd 的 watch 是长连接,正常情况下 watcher 数量跟集群对象数量和控制器数量相关,应该稳定在一个区间。持续上涨不回落,通常是客户端泄漏:

# 当前 watcher 数
etcd_debugging_mvcc_watcher_total

# 慢 watcher 增长
increase(etcd_debugging_mvcc_slow_watcher_total[10m])

连接数不在 etcd 指标里,直接在宿主机看:

# 2379 端口的 ESTABLISHED 连接数
ss -tn state established '( sport = :2379 )' | tail -n +2 | wc -l

# 按客户端 IP 聚合,找出连接最多的来源
ss -tn state established '( sport = :2379 )' | tail -n +2 \
| awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head

如果连接数暴涨且 watcher 数同步上涨,先查 apiserver 和 controller 的重启记录;如果只是连接多但 watcher 正常,可能是短连接客户端没复用连接,检查客户端 SDK 配置。

告警阈值​

阈值没有绝对标准,下面是一套经过生产验证的经验值,按自己集群的基线微调:

告警条件级别说明
无 leaderetcd_server_has_leader == 0 持续 1mcritical集群不可用,立刻处理
leader 频繁切换1h 内 leader_changes_seen_total 增量大于 0warning偶发一次可观察,持续出现要查盘和网络
提案失败increase(proposals_failed_total[5m]) > 0critical写入失败,通常伴随超时
提案积压proposals_pending > 5 持续 5mwarning写入链路拥塞
fsync 慢WAL fsync P99 大于 10ms 持续 10mwarning磁盘性能不足
fsync 严重慢WAL fsync P99 大于 25ms 持续 5mcritical大概率引发选主和超时
db 接近配额配额使用率大于 80%warning准备 compact + defrag 或扩容
db 超过 8Getcd_mvcc_db_total_size_in_bytes > 8 * 1024 * 1024 * 1024warning社区经验上限,恢复和迁移都会变慢
碎片率高碎片率大于 50% 持续 1hwarning安排 defrag
慢 watcher10m 内 slow_watcher_total 有增长warning查客户端消费能力
文件描述符os_fd_used / os_fd_limit > 0.8warning调大 LimitNOFILE 或查泄漏

关于 db 大小的两个数字要区分清楚:

  • 默认配额是 2 GiB(--quota-backend-bytes 的默认值)。也就是说 db 到 2G 就会触发 NOSPACE,不是 8G。
  • 8 GiB 是生产建议的上限,生产集群要显式设置 --quota-backend-bytes,kubeadm 默认并不会替你改这个参数。db 越大,启动恢复越慢、内存占用越高,超过 8G 就该考虑清理数据或拆分集群,而不是继续调大配额。
不要用调大配额来解决 db 增长

配额从 2G 调到 8G 只是把爆炸时间往后推。真正要做的是:开自动压缩、定期 defrag、清理不用的 K8s 对象(尤其是 Event、已完成的 Job、旧的 ReplicaSet)。db 持续增长说明有东西在漏。

Prometheus 接入​

etcd 的 /metrics 是标准 Prometheus 格式,直接在 prometheus.yml 里加一个 job 即可。概念和抓取机制见 Prometheus 核心概念与架构。

scrape_configs:
- job_name: etcd
scheme: https
metrics_path: /metrics
scrape_interval: 15s
scrape_timeout: 10s
static_configs:
- targets:
- 10.0.0.11:2379
- 10.0.0.12:2379
- 10.0.0.13:2379
labels:
env: prod
cluster: k8s-prod
tls_config:
ca_file: /etc/prometheus/certs/etcd-ca.crt
cert_file: /etc/prometheus/certs/etcd-client.crt
key_file: /etc/prometheus/certs/etcd-client.key

几个坑:

  • 客户端证书:开了 --client-cert-auth 就必须配 cert_file 和 key_file,只给 CA 会握手失败。
  • kubeadm 的 2381 端口:默认只监听 127.0.0.1,Prometheus 在集群外抓不到。要么让 Prometheus 以 hostNetwork 跑在控制面节点上,要么改 --listen-metrics-urls 绑到节点 IP(同时用网络策略限制来源),要么走 apiserver 代理。
  • 抓取超时:etcd 的 metrics 端点偶尔会慢,scrape_timeout 不要小于 10s,否则会出现假掉线。
  • 多成员对比:三个成员都要抓,只抓一个会在该成员故障时丢监控。

用 up 指标确认抓取状态,用 etcd_server_has_leader 做第一道可用性告警。

告警规则示例​

下面是一组可以直接落地的规则,按需调整阈值和 for:

groups:
- name: etcd
rules:
- alert: EtcdNoLeader
expr: etcd_server_has_leader == 0
for: 1m
labels:
severity: critical
annotations:
summary: "etcd 成员没有 leader"
description: "实例上的 etcd_server_has_leader 为 0,集群可能失去多数派。"

- alert: EtcdLeaderChanges
expr: increase(etcd_server_leader_changes_seen_total[1h]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "etcd leader 发生切换"
description: "最近 1 小时出现 leader 切换,检查磁盘 fsync 和成员网络。"

- alert: EtcdProposalFailures
expr: increase(etcd_server_proposals_failed_total[5m]) > 0
for: 5m
labels:
severity: critical
annotations:
summary: "etcd 提案失败"
description: "有写请求未能提交,检查成员健康和磁盘。"

- alert: EtcdWALFsyncSlow
expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) > 0.01
for: 10m
labels:
severity: warning
annotations:
summary: "etcd WAL fsync P99 超过 10ms"
description: "磁盘写入延迟偏高,可能是云盘 IOPS 不足或与其他 IO 密集型服务混部。"

- alert: EtcdDBQuotaHigh
expr: etcd_mvcc_db_total_size_in_bytes / etcd_server_quota_backend_bytes > 0.8
for: 10m
labels:
severity: warning
annotations:
summary: "etcd db 使用超过配额 80%"
description: "尽快执行 compact 和 defrag,或评估扩容;触发 NOSPACE 后集群会变为只读。"

- alert: EtcdSlowWatcher
expr: increase(etcd_debugging_mvcc_slow_watcher_total[10m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "etcd 出现慢 watcher"
description: "客户端消费 watch 事件过慢,检查 apiserver 和控制器状态。"
先跑通再加规则

新规则先设成 severity: info 或只做记录,观察一两周基线,再决定阈值和 for。拿别人的阈值直接上,很容易被误报淹没,最后整组告警被静音。

容量治理​

监控发现问题只是第一步,治理动作主要是三件事:压缩、碎片整理、控制配额。

自动压缩​

生产环境统一开自动压缩,别靠人记得住:

# etcd.conf.yml
auto-compaction-mode: periodic
auto-compaction-retention: 1h

周期模式保留最近 1 小时的历史 revision,足够覆盖 apiserver watch 重连和 etcd 自身恢复。写入量特别大的集群可以改成 revision 模式:

auto-compaction-mode: revision
auto-compaction-retention: 10000

碎片整理​

自动压缩不回收磁盘,defrag 要单独做。触发条件建议两条:碎片率超过 50%,或者 db 大小超过配额 60%。操作步骤:

# 1. 先压缩到当前 revision
REV=$(etcdctl endpoint status -w json | jq -r '.[0].Status.header.revision')
etcdctl compact $REV

# 2. 逐台 defrag,确认上一台健康后再动下一台
etcdctl --endpoints=https://10.0.0.11:2379 \
--cacert=/etc/etcd/certs/ca.crt \
--cert=/etc/etcd/certs/client.crt \
--key=/etc/etcd/certs/client.key \
defrag --command-timeout=600s

etcdctl endpoint health --cluster

一台一台来,别用 --cluster 图省事——虽然它会顺序执行,但中间没有停顿,集群压力更集中。leader 放到最后,defrag 前确认另外两个成员健康。

配额与数据治理​

  • 默认配额 2 GiB,生产按 8 GiB 设置,用 --quota-backend-bytes。不要设成无限大,db 过大时恢复时间会失控。
  • K8s 对象清理:Event 默认保留 1 小时,但量大时也会堆积;已完成 Job、旧 ReplicaSet、废弃的 CRD 实例都要定期清理。
  • 别存大对象:ConfigMap、Secret 单个别超过几百 KB,超过 1.5 MiB 的请求会直接被 etcd 拒绝。
  • 保留余量:db 到配额的 80% 就开始治理,留出 compact 和 defrag 的时间窗口。

日常巡检​

每天花几分钟过一遍,比出事再查日志高效得多:

# 集群健康和成员状态
etcdctl endpoint health --cluster
etcdctl endpoint status -w table
etcdctl member list -w table

# 有没有未处理的告警
etcdctl alarm list

# db 大小、碎片、revision
etcdctl endpoint status -w json | jq '.[0].Status | {dbSize, dbSizeInUse, revision: .header.revision}'

# 最近的慢日志(systemd 场景)
journalctl -u etcd --since "24 hours ago" | grep -iE 'slow|timeout|leader|space'

巡检表可以固定成四项:leader 有没有切换、db 有没有接近配额、fsync 有没有变慢、有没有 NOSPACE 告警。这四项正常,etcd 基本就不会出大问题。

小结​

  • etcd 自带 /metrics,重点盯选主、RAFT 积压、db 大小、fsync 延迟、watch 数量五类指标。
  • RAFT 延迟拆成 propose / commit / apply 三段看:proposals_pending 看入口,fsync 和网络 RTT 看提交,committed - applied 看应用积压。
  • 告警阈值:db 超过 8 GiB 或配额 80% 告警,fsync P99 超过 10ms 警惕、超过 25ms 严重,leader 1 小时内切换就要查。
  • 生产必须开 --auto-compaction-retention,defrag 逐台做、放低峰,碎片率超过 50% 就该安排。
  • Prometheus 抓 etcd 要配客户端证书;kubeadm 的 2381 端口默认只听本机,注意抓取路径。
  • 治理的核心不是调大配额,而是压缩历史、回收碎片、清理垃圾数据。

监控和治理到位,剩下的就是真出事时怎么救。下一篇按故障场景拆:多数派丢失、NOSPACE、慢盘、watch 泄漏,以及一份可以直接照着做的最佳实践清单。