Skip to content
最佳实践与陷阱

最佳实践与陷阱

etcd 全手册陷阱汇总、最佳实践清单,以及生产环境上线前检查清单。


陷阱汇总表

集群与高可用

陷阱 严重度 说明
🚨 2 节点集群 🔴 高 无法容忍任何节点故障, 一个节点挂掉即无法形成 quorum
🚨 节点数为偶数 🔴 高 偶数节点无法提高容错(如 4 节点仍只能容忍 1 个故障),浪费资源
🚨 NOSPACE 被忽略 🔴 高 触发 NOSPACE 告警后写入被拒绝,服务不可用
🚨 新节点直接加为 Voter 🟡 中 日志严重落后的节点加入集群会阻塞 quorum 提交
🚨 跨地域 RTT 过大 🟡 中 Raft 日志复制受限于网络延迟,Leader 必须在多数节点确认后才能返回写入成功
🚨 时钟不同步 🟡 中 etcd 内部使用逻辑时钟(revision),但租赁(lease)TTL 依赖于本地时钟;时钟偏差可能导致提前或延迟过期

存储与性能

陷阱 严重度 说明
🚨 不配置自动压缩 🔴 高 默认不会自动压缩,revision 持续增长直到磁盘耗尽
🚨 大 Value 写入 🟡 中 单个 Value 超过 1MB 时 Raft 提案和复制明显变慢
🚨 Key 数量极大 🟡 中 百万级 Key 时需要适当提高 --quota-backend-bytes
🚨 频繁碎片整理 🟡 中 defrag 会锁住数据库,操作期间服务中断
🚨 磁盘 IO 性能差 🟡 中 HDD 或共享磁盘严重影响 etcd 写延迟,推荐专用 SSD

客户端

陷阱 严重度 说明
🚨 Endpoints 仅配一个节点 🔴 高 该节点挂掉整个客户端不可用
🚨 忘记 Close Client 🟡 中 gRPC 连接不回收,goroutine 随连接数增长
🚨 AutoSyncInterval = 0 🟡 中 集群成员变化后客户端不感知,持续的请求可能失败
🚨 Context 无超时 🟡 中 网络故障时请求可能永久阻塞
🚨 每个 goroutine 创建 Client 🟡 中 Client 并发安全,一个全局实例即可

Watch

陷阱 严重度 说明
🚨 Compaction 后 Watch 断连 🔴 高 从已压缩 revision 恢复 watch 会收到 ErrCompacted
🚨 忘记 WithPrefix 🟡 中 Watch(ctx, "/foo") 只监听精确匹配的 /foo
🚨 Watch channel 不消费 🟡 中 服务端为每个 watcher 维护推送队列,消费慢会占用 etcd 内存
🚨 不检查 resp.Err() 🟡 中 Watch 可能因为网络问题返回错误,不及时处理导致事件丢失

Lease

陷阱 严重度 说明
🚨 KeepAlive 用过期 ctx 🔴 高 ctx 超时或取消 → channel 关闭 → Lease 到期 → Key 被删
🚨 不监控 KeepAlive channel 🟡 中 channel 无声关闭后应用以为注册有效,实际 Key 已过期
🚨 TTL 设置不当 🟡 中 太短:网络抖动导致不必要的 Key 删除;太长:故障检测慢
🚨 忘记 Close Session 🟡 中 Session 持有 Lease,不 Close 持续续约直到连接断开

并发

陷阱 严重度 说明
🚨 Mutex Lock 无 ctx 超时 🟡 中 前一持锁者若永不释放,Lock 永久阻塞
🚨 多 Compare 全 AND 逻辑 🟡 中 所有 Compare 必须满足才执行 Then,不能实现"任一满足"
🚨 STM 不支持 Delete 🟡 中 STM 只有 Get/Put,无法事务性删除 Key
🚨 Election Session 断开 🟡 中 Session 断开自动 Resign,但没有心跳监控时 Leader 可能不自知

安全

陷阱 严重度 说明
🚨 etcd 暴露在公网 🔴 高 攻击者可读写任意数据,读取所有敏感配置
🚨 root 密码忘记 🔴 高 root 密码无法找回,需重建集群
🚨 auto-tls 上生产 🟡 中 自签名证书不提供身份验证,仅用于测试
🚨 Auth 启用后已有连接失败 🟡 中 启用 Auth 前建立的连接后续请求全部被拒
🚨 权限粒度理解错误 🟡 中 精确匹配 /foo 不等于前缀匹配 /foo/

最佳实践清单

部署与运维

  • 💡 生产环境至少 3 个节点,推荐 5 个节点
  • 💡 使用 SSD 专用磁盘 存储 data-dir
  • 💡 必配自动压缩:--auto-compaction-mode=periodic --auto-compaction-retention=1
  • 💡 监控 5 个核心指标:DB 大小、Leader 是否存在、fsync 延迟、Raft 提案延迟、快照频率
  • 💡 定期创建快照并异地备份(建议 cron 每天 1 次)
  • 💡 新节点加入使用 Learner 模式,追上日志后再 Promote
  • 💡 跨可用区部署时适当调大 election-timeout

客户端

  • 💡 一个应用一个全局 Client,并发安全共享
  • 💡 Endpoints 列出所有集群节点
  • 💡 生产环境设置 AutoSyncInterval(建议 30s ~ 5min)
  • 💡 所有请求必须带 context 超时
  • 💡 程序退出前 defer cli.Close()

数据设计

  • 💡 Key 使用层级命名/<domain>/<subsystem>/<entity>/<id>
  • 💡 单个 Value 建议 ≤ 1MB,大对象存外部存储,etcd 只存引用 URL
  • 💡 临时数据绑定 Lease,避免手动清理
  • 💡 一个服务实例一个 Lease,而非一个应用一个 Lease

Watch

  • 💡 标准模式:先 Get → 记住 revision → 再 Watch(revision+1)
  • 💡 必须处理 ErrCompacted(全量重新同步)
  • 💡 必须检查 resp.Err()resp.Canceled
  • 💡 建议开启 WithProgressNotify() 检测连接活性
  • 💡 单个客户端 watcher 数建议 < 1000,超过时按 range 合并

并发

  • 💡 简单 CAS 用手动 Txn,复杂逻辑用 STM
  • 💡 分布式锁必须绑定 Lease,防止死锁
  • 💡 Lock 操作必须带 context 超时
  • 💡 监控 Mutex/Election 的 Session 状态

安全

  • 💡 生产环境开启 mTLS + RBAC
  • 💡 遵循最小权限原则分配角色
  • 💡 证书设置合理的过期时间并建立轮换机制
  • 💡 etcd 端口仅绑定内网 IP

生产环境检查清单

使用以下清单在上线前逐项确认:

部署检查

检查项 说明
☐ 集群节点数 ≥ 3 的奇数个节点
☐ 存储介质 data-dir 在专用 SSD 上
☐ 自动压缩 --auto-compaction-mode=periodic --auto-compaction-retention=1
☐ 存储上限 --quota-backend-bytes 合理设置(如 8GB)
☐ Raft 参数 election-timeout ≥ 5 × heartbeat-interval
☐ 文件描述符 LimitNOFILE ≥ 65536(systemd)
☐ 网络端口 2379/2380 仅绑定内网 IP,防火墙限制访问来源

客户端检查

检查项 说明
☐ Endpoints 列出所有集群节点
☐ DialTimeout 有合理的超时(如 5s
☐ AutoSyncInterval 已设置(如 30s
☐ 全局 Client 单例 未创建多余 Client
☐ 带超时 ctx 所有操作都有 context 超时
☐ 优雅关闭 程序退出时调用 cli.Close()

数据设计检查

检查项 说明
☐ Key 命名规范 统一使用层级命名
☐ Value 大小 没有超大 Value(> 1MB)
☐ Lease 使用 临时数据绑定 Lease
☐ Key 数量预估 评估是否在百万级以内

Watch 检查

检查项 说明
☐ compact 处理 有 ErrCompacted 的全量同步逻辑
☐ Err 处理 有 resp.Err() 检查
☐ revision 恢复 使用 Get + revision 的增量监听模式
☐ watcher 数量 未超过建议上限

安全检查

检查项 说明
☐ TLS 加密 已启用
☐ mTLS 认证 已启用(非 auto-tls)
☐ RBAC 认证 已启用,root 密码已设置
☐ 最小权限 各用户仅有所需 Key 范围的必要权限
☐ 证书过期 证书有轮换计划

监控与运维检查

检查项 说明
☐ DB 大小告警 quota-backend-bytes 80% 时告警
☐ Leader 监控 集群始终有 Leader
☐ 快照备份 定期快照 + 异地存储
☐ 恢复演练 至少执行过一次快照恢复演练
☐ etcd 版本 所有节点版本一致
☐ 日志级别 生产用 infowarn

快速排障

现象 可能原因 排查命令
写入被拒绝 NOSPACE 告警 etcdctl alarm list
读不到最新数据 使用了 WithSerializable() 去掉该 option 或改用默认的 Linearizable
Watch 连接断 revision 被 compact 全量 Get 后重新 Watch
客户端连不上 端点不可达 / TLS 配置错误 etcdctl endpoint health
频繁 Leader 切换 election-timeout 太短 / 网络不稳定 检查日志中的 election 相关消息
写操作延迟高 磁盘 IO 瓶颈 / 跨地域复制 / 网络延迟 etcdctl endpoint status 查看 DB size 和 Raft index

参考资源