KKubepath
存储运维工程师 路线4 / 15 · 先看懂容器与节点退出路线
实验预计 35 分钟

etcd 磁盘验收:p99 fsync 10ms 这条硬线

集群卡顿、频繁选主、apiserver 超时,追到底常常是一块不合格的盘。这条线部署前就能用 fio 测出来。

学完这节你能做到

  • 用 fio 复现 etcd 的 WAL 写入模式,并读懂 sync percentiles 输出
  • 说清哪些存储介质绝对不能给 etcd 用,以及为什么
  • 部署后用 PromQL 持续盯住 fsync、backend commit 与选主次数

这一节只解决一个问题:手里这批盘,能不能给 etcd 用

它值得单独占一节,因为这是 K8s 部署前唯一能独立测出来、且测不过就必须换硬件的指标。集群建起来之后再发现盘不行,代价是停机迁移 etcd 数据目录。

为什么 etcd 这么挑盘

etcd 用 Raft 协议保证一致性,而 Raft 有一条不能商量的规则:每次写提案,必须 fsync 落盘之后才能确认。不是写到 page cache 就算,是真的要落到介质上。

于是一次 kubectl apply 的写入延迟里,包含了一次磁盘 fsync 的完整耗时。盘慢一点,整条链路都跟着慢:

盘 fsync 慢
  → etcd 写提案确认慢
    → apiserver 写请求超时
      → 心跳超不上 → 触发选主 → 选主期间集群不可写
        → kubelet 上报状态失败 → 节点开始随机 NotReady

这就是"集群整体发黏"最常见的根因。表现五花八门,根子在一块盘上。

硬件要求:先排除掉不能用的

项目要求
磁盘类型NVMe SSD 优先,SATA SSD 可接受
磁盘占用独占一块盘,不与系统盘、日志盘共享
明确禁止机械盘、NAS、SAN、iSCSI、Ceph RBD、NFS

小集群最低 50 顺序写 IOPS,大集群推荐 500 以上。

×千万不要把 etcd 放在网络存储上

这是搭实验环境时最容易犯的错:集群里已经有一套 Ceph 或者 GPFS,很自然就想"数据目录都放共享存储,节点坏了好迁移"。

但网络存储的 fsync 要多跨一次网络往返 —— 延迟从几十微秒变成几毫秒甚至几十毫秒,而且抖动大得多。更糟的是形成了循环依赖:存储集群出问题会拖垮 K8s 控制面,而存储的 CSI 又跑在这个 K8s 上。

etcd 的数据目录必须是本地盘,而且最好独占。它一共就那么几个 GB,不值得为它省一块盘。

部署前必测:fio 模拟 WAL 写入

关键是让 fio 的行为像 etcd 写 WAL:小块、顺序、每写一次就 fdatasync。

fio --rw=write --ioengine=sync --fdatasync=1 \
    --directory=/var/lib/etcd \
    --size=22m --bs=2300 \
    --name=etcd-test

参数逐个解释 —— 这几个值都不是随便填的:

参数为什么是这个值
--fdatasync=1每写一次就同步一次,这是模拟 etcd 行为的核心,去掉就毫无意义
--bs=2300匹配 etcd WAL 条目的典型大小
--ioengine=syncetcd 的 WAL 写入是同步的,不用异步引擎
--directory必须指向 etcd 实际要用的那个路径,不同盘结果差很远
--size=22m约产生 10⁴ 个样本,让 p99 的统计有意义

fio 版本需要 ≥ 3.5,旧版不报 fdatasync 的百分位。

不确定 WAL 条目大小时,可以在已有集群上实测:

strace -p $(pgrep etcd) -e write 2>&1 | head -50

结果判定:只看一个数

输出里找 sync percentiles (usec) 这一段:

sync percentiles (usec):
| 99.00th =[ 2376]    ← 2.4ms,合格
| 99.50th =[ 9634]    ← 9.6ms,勉强
| 99.90th =[15795]    ← p99.9 不看

判定标准:99.00th < 10000 usec(10ms)即通过。

!测出 9ms 不叫「刚好通过」

fio 是隔离测试 —— 机器上只有它一个在写。真实运行的 etcd 还有 boltdb 刷盘、快照、压缩这些额外 I/O 一起抢盘。

所以 fio 测出接近 10ms,意味着上线后余量为零,稍有并发就会突破。测出来在 3ms 以内才叫有余量。

部署后:三个必须盯住的指标

fio 只能验收硬件。上线后要持续监控的是运行态:

指标目标说明
etcd_disk_wal_fsync_duration_seconds p99< 10ms和部署前同一个口径
etcd_disk_backend_commit_duration_seconds p99< 25msboltdb 刷盘,无法提前测
etcd_server_leader_changes_seen_total0在涨就是磁盘或网络有问题
histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m]))
histogram_quantile(0.99, rate(etcd_disk_backend_commit_duration_seconds_bucket[5m]))
increase(etcd_server_leader_changes_seen_total[15m])

官方告警阈值放得比验收线宽得多 —— warning 是 fsync p99 > 500ms 持续 10 分钟,critical 是 > 1s。别拿告警阈值当验收标准:等它响的时候,集群已经很难用了。自己的告警建议设在 50–100ms。

上机演练

下面这一关模拟部署前的验收现场:两台候选机器,选出能用的那台。

root@mn-10-128-0-1
目标 0/4
  1. 1.先确认盘的类型,排除掉机械盘
  2. 2.确认 etcd 数据目录落在哪块盘上
  3. 3.跑 fio 拿到 fsync 的 p99
  4. 4.对比另一块候选盘,看清差距有多大
模拟终端:新到两批机器,要给 etcd 选盘。
输入 goals 看目标,hint 要提示。
[root@mn-10-128-0-1 ~]#
help 查看用法 · goals 看目标 · hint 要提示 · ↑↓ 翻历史

两个结果的差距是数量级的:NVMe 的 p99 是 807 usec(0.8ms),余量充足;机械盘的 p99 是 72192 usec(72ms),超标七倍以上。装上去集群会一直选主,而现象会伪装成"网络不稳定"。

检查点单选

fio 测出 sync 的 99.00th = 8500 usec。以下判断哪个最合理?

验收清单

装机之前照着走一遍:

  1. lsblk -d -o NAME,SIZE,ROTA 确认不是机械盘(ROTA=0)
  2. 确认 /var/lib/etcd 独占一块盘,不和系统盘或日志盘混用
  3. 确认不是任何形式的网络存储
  4. fio 跑出 99.00th < 3000 usec 才算有余量,< 10000 usec 是底线
  5. 上线后配好三个监控指标,告警设在 50–100ms,而不是官方的 500ms

延伸资料