bigywater.tech
Kubernetes · 97

etcd 的 compaction 与 defrag:空间怎么回收

etcd 为什么越用越大?删了数据文件却不变小?compact 和 defrag 各做什么?这篇往 etcd 里写入 100KB 的 key,覆盖三遍,再依次 compact、defrag,把 dbSize 和 dbSizeInUse 的变化逐步量出来。

79.5 → 20.0 MiB
defrag 之后 dbSize 的变化;compact 之前 dbSize 保持 79.5 MiB 不变
77.7 → 20.0 MiB
compact 之后 dbSizeInUse 的变化
19.1 MiB
200 个 100KB 的 key 的有效数据量,覆盖写 3 遍后文件却是 79.2 MiB

写入:revision 与文件一起长

实测 · 本机执行输出
  写 200 个 100KB 的 key(17 秒):dbSize=21.0 MiB(+20.3 MiB);dbSizeInUse=20.3 MiB;revision 144664 → 144893(+229:200 次写,其余是集群自己的写入)

基线(清空测试 key、compact、defrag 之后)dbSize=0.8 MiB。往 etcd 写 200 个 100KB 的 key(17 秒):dbSize 涨到 21.0 MiB(+20.3 MiB),revision 从 144664 到 144893(+229,其中 200 次是我们的写,其余是集群自己的写入)。kube-apiserver 记下的最近压缩位置 compact_rev_key 是 142537,落后于当前 revision 一小段。

MVCC:写一次留一个版本

实测 · 本机执行输出
  覆盖写 3 遍后:dbSize=79.2 MiB(+58.2 MiB);dbSizeInUse=77.7 MiB;revision 144893 → 145573(+680,600 次写加集群自己的写入)——实际有效数据只有 200 个 key 约 19.1 MiB,其余都是旧版本
  用 --rev=144694(第一遍写入时)读 /k91/0000:还能读到,长度 100000 字节(旧版本还在)

把同一批 key 覆盖写 3 遍:dbSize 79.2 MiB,dbSizeInUse 77.7 MiB,revision 到 145573,而有效数据其实只有约 19.1 MiB——其余都是旧版本。用 --rev 读第一遍写入时的 /k91/0000 还能读到 100000 字节:旧版本都在,这是 watch 与一致性读的基础,也是空间的来源。Lease、Event、Node status 这类频繁更新的对象最容易吃空间。

compact:丢旧版本,不缩文件

实测 · 本机执行输出
  etcdctl compact 145573:compacted revision 145573
  再读 --rev=144694:required revision has been compacted
  compact 之后:dbSize=79.5 MiB(几乎没变);dbSizeInUse=20.0 MiB(降了 57.7 MiB)——文件里出现了大量可复用的空闲页,但文件本身不缩

etcdctl compact 145573 之后,再读旧 revision 得到 required revision has been compacted。dbSizeInUse 从 77.7 降到 20.0 MiB,但 dbSize 仍是 79.5 MiB——旧版本占的页被标记为空闲、可以复用,可是 bbolt 文件本身不会缩小。监控磁盘空间时要看 dbSizeInUse 与 dbSize 的差。kube-apiserver 默认每 5 分钟做一次压缩,etcd 自己没有开自动压缩。

defrag:真正归还

实测 · 本机执行输出
  etcdctl defrag(0.3 秒):Finished defragmenting etcd member[https://127.0.0.1:2379]. took 228.738125ms;dbSize=79.5 MiB → 20.0 MiB;dbSizeInUse=20.0 MiB(现在两者接近)
  把测试 key 删掉后再 compact + defrag:dbSize=0.8 MiB(回到基线 0.8 MiB 附近);revision=145580
  etcd 的启动参数里 quota-backend-bytes / auto-compaction:都没有设置,用默认值(db 配额默认 2 GiB;etcd 自己不自动压缩,由 kube-apiserver 每 5 分钟压缩)

etcdctl defrag(0.3 秒;大库会久得多,期间该成员的读写会被阻塞)之后 dbSize 从 79.5 缩到 20.0 MiB,和 dbSizeInUse 一致。把测试 key 删掉再 compact + defrag,dbSize 回到 0.8 MiB。etcd 启动参数里没设 quota-backend-bytes(默认配额 2 GiB),超过配额会进入告警、只读状态,所以磁盘空间要靠定期 compact 与 defrag 来管理。

分步演示

etcd 空间回收
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0)的单成员 etcd;测试 key 写在 /k91/ 前缀下,不是 Kubernetes 的 key,实验结束后已删除并 compact + defrag;值是 100000 个随机字母数字字符,用 kubectl exec -i 的标准输入传给 etcdctl put;etcdctl endpoint status -w json 读 dbSize / dbSizeInUse / revision;每种情形各一次
    • 没有验证:多成员 etcd 的逐个 defrag 与 leader 影响;quota 超限后的 NOSPACE 告警与 alarm disarm;etcd 的 --auto-compaction-mode / retention;kube-apiserver 压缩的 --etcd-compaction-interval 调整;大库(GB 级)defrag 的耗时;快照备份与 compaction 的关系;watch 过期(见 k7)与压缩的关系
    京ICP备2026067502号-1京公网安备11010502063487号