4. 压测沙盘环境搭建:使用 docker-compose 搭建 3 节点 etcd 集群、配置参数详解

在压测沙盘中,一个稳定且可复现的 etcd 集群是故障注入与排查的基础。本章将使用 docker-compose 在单机环境下快速拉起一个 3 节点 etcd 集群,并深入解析影响集群稳定性的核心配置参数:心跳间隔、选举超时与快照策略。

4.1 环境准备与目录结构

在开始之前,确保宿主机已安装 Docker 和 Docker Compose。建议使用以下目录结构来组织沙盘文件:

etcd-sandbox/
├── docker-compose.yml
├── etcd1/
│   └── data/
├── etcd2/
│   └── data/
└── etcd3/
    └── data/

4.2 docker-compose.yml 完整配置

以下是一个可直接用于压测沙盘的 3 节点 etcd 集群配置。每个节点使用独立的容器、数据卷和端口映射,并显式配置了关键参数。

version: '3.8'

services:
  etcd1:
    image: quay.io/coreos/etcd:v3.5.9
    container_name: etcd-sandbox-1
    hostname: etcd1
    ports:
      - "23791:2379"
      - "23801:2380"
    volumes:
      - ./etcd1/data:/etcd-data
    environment:
      - ETCD_NAME=etcd1
      - ETCD_DATA_DIR=/etcd-data
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd1:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd1:2379
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_=etcd-sandbox-
      - ETCD_INITIAL_CLUSTER_STATE=new
      # 核心参数
      - ETCD_HEARTBEAT_INTERVAL=100
      - ETCD_ELECTION_TIMEOUT=1000
      - ETCD_SNAPSHOT_COUNT=10000
    networks:
      - etcd-net

  etcd2:
    image: quay.io/coreos/etcd:v3.5.9
    container_name: etcd-sandbox-2
    hostname: etcd2
    ports:
      - "23792:2379"
      - "23802:2380"
    volumes:
      - ./etcd2/data:/etcd-data
    environment:
      - ETCD_NAME=etcd2
      - ETCD_DATA_DIR=/etcd-data
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd2:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd2:2379
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_=etcd-sandbox-
      - ETCD_INITIAL_CLUSTER_STATE=new
      - ETCD_HEARTBEAT_INTERVAL=100
      - ETCD_ELECTION_TIMEOUT=1000
      - ETCD_SNAPSHOT_COUNT=10000
    networks:
      - etcd-net

  etcd3:
    image: quay.io/coreos/etcd:v3.5.9
    container_name: etcd-sandbox-3
    hostname: etcd3
    ports:
      - "23793:2379"
      - "23803:2380"
    volumes:
      - ./etcd3/data:/etcd-data
    environment:
      - ETCD_NAME=etcd3
      - ETCD_DATA_DIR=/etcd-data
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd3:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd3:2379
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_=etcd-sandbox-
      - ETCD_INITIAL_CLUSTER_STATE=new
      - ETCD_HEARTBEAT_INTERVAL=100
      - ETCD_ELECTION_TIMEOUT=1000
      - ETCD_SNAPSHOT_COUNT=10000
    networks:
      - etcd-net

networks:
  etcd-net:
    driver: bridge
启动命令:在 etcd-sandbox/ 目录下执行 docker-compose up -d,等待约 5 秒后,使用 docker-compose ps 确认三个节点均为 Up 状态。使用 docker exec etcd-sandbox-1 etcdctl endpoint health --cluster 验证集群健康。

4.3 核心配置参数详解

etcd 的稳定性高度依赖 Raft 协议的时间参数与存储策略。以下三个参数是压测沙盘中必须理解并可能调整的关键项。

4.3.1 心跳间隔(Heartbeat Interval)

参数名:--heartbeat-interval(环境变量 ETCD_HEARTBEAT_INTERVAL)
默认值:100 毫秒
作用:Leader 向 Follower 发送心跳消息的时间间隔。心跳用于维持 Leader 权威并防止 Follower 发起不必要的选举。

  • 过小(如 50ms):网络开销增大,CPU 占用上升,但 Leader 故障检测更快。
  • 过大(如 500ms):网络负载降低,但 Follower 可能因未及时收到心跳而误判 Leader 失效,触发频繁选举。
  • 压测建议:在沙盘中保持 100ms 默认值,模拟 Leader 网络分区时可临时调大至 300ms 以观察选举超时行为。

4.3.2 选举超时(Election Timeout)

参数名:--election-timeout(环境变量 ETCD_ELECTION_TIMEOUT)
默认值:1000 毫秒
作用:Follower 等待 Leader 心跳的最大时间。若超时未收到心跳,Follower 转换为 Candidate 并发起选举。

  • 选举超时 = 心跳间隔 × 随机因子(默认 1~2 倍)。例如心跳 100ms,选举超时 1000ms,则实际超时在 1000ms~2000ms 之间随机。
  • 过小(如 400ms):集群对 Leader 故障敏感,但可能因瞬时网络抖动导致频繁选举,降低可用性。
  • 过大(如 3000ms):Leader 故障后集群恢复时间变长,服务中断窗口增大。
  • 压测建议:沙盘中保持 1000ms。在模拟 Leader 节点宕机时,可观察约 1~2 秒的不可用窗口,符合预期。

4.3.3 快照策略(Snapshot Count)

参数名:--snapshot-count(环境变量 ETCD_SNAPSHOT_COUNT)
默认值:100000 条
作用:当 etcd 数据存储中累积的变更记录(Entry)达到此数量时,触发一次快照(Snapshot)。快照将当前状态持久化到磁盘,并截断 WAL 日志以释放空间。

  • 过小(如 1000):快照频繁,磁盘 I/O 和 CPU 开销增加,但节点重启时恢复速度更快。
  • 过大(如 1000000):快照间隔长,WAL 日志膨胀,节点重启时需回放大量日志,启动变慢。
  • 压测建议:沙盘中设置为 10000,便于在压测写入时快速触发快照,观察快照对写入延迟的影响。

4.4 参数对比与推荐值

参数 默认值 沙盘推荐值 压测调整范围 影响维度
心跳间隔 100ms 100ms 50ms ~ 500ms 网络开销、Leader 检测速度
选举超时 1000ms 1000ms 400ms ~ 3000ms 故障恢复时间、选举稳定性
快照计数 100000 10000 1000 ~ 1000000 磁盘 I/O、节点重启速度

4.5 集群启动后的验证步骤

  1. 检查成员列表:docker exec etcd-sandbox-1 etcdctl member list,应输出 3 个节点。
  2. 检查集群健康:docker exec etcd-sandbox-1 etcdctl endpoint health --cluster,所有节点应为 healthy。
  3. 验证 Leader:docker exec etcd-sandbox-1 etcdctl endpoint status --cluster -w table,查看 IS LEADER 列。
  4. 写入测试:docker exec etcd-sandbox-1 etcdctl put /test/key1 "hello-sandbox",然后 get 确认。
沙盘提示:在后续章节中,我们将通过修改 docker-compose.yml 中的 ETCD_HEARTBEAT_INTERVAL 和 ETCD_ELECTION_TIMEOUT 来模拟“心跳延迟”和“选举超时”故障场景。建议在压测前先使用默认参数运行 5 分钟,观察基线行为。

4.6 3 节点 etcd 集群架构图

下图展示了沙盘中 3 节点 etcd 集群的组件关系与通信路径。Leader 负责处理客户端写入并同步日志到 Follower,Follower 转发读请求或提供线性一致读。

3 节点 etcd 集群架构(沙盘环境) etcd1 Leader 2379 / 2380 etcd2 Follower 23792 / 23802 etcd3 Follower 23793 / 23803 心跳 100ms 心跳 100ms 选举超时机制 Follower 在 1000ms~2000ms 内未收到心跳 → 发起选举 → 新 Leader 产生 客户端(etcdctl / 压测工具) 写入请求 读请求(线性一致)

4.7 快照与 WAL 日志关系图

快照策略直接影响 etcd 的存储行为。下图展示了 Entry 累积、快照触发与 WAL 截断的流程。

快照触发与 WAL 截断流程 WAL 日志:Entry 1 → Entry 2 → ... → Entry 10000(达到 snapshot-count) 触发快照(Snapshot) 持久化当前状态到 .snap 文件 释放磁盘空间 截断已快照的 WAL 日志 保留最新 Entry 用于恢复 snapshot-count=10000:每 10000 条 Entry 触发一次快照
注意:在压测场景中,如果写入速率极高(如每秒 5000+ 条),snapshot-count 设置为 10000 可能导致快照频繁触发(每 2 秒一次),从而引起磁盘 I/O 抖动。此时可适当调大至 50000 以降低快照频率,但需权衡节点重启恢复时间。

4.8 常见问题与避坑

  • 端口冲突:宿主机上 2379/2380 端口可能被其他 etcd 或服务占用。使用 23791~23793 和 23801~23803 避免冲突。
  • 数据持久化:挂载本地目录时,确保 ./etcd1/data 等目录存在且具有写权限,否则容器启动失败。
  • 网络模式:使用自定义 bridge 网络 etcd-net,确保容器间通过 hostname 互通。不要使用 network_mode: host,否则端口映射失效。
  • 初始集群状态:首次启动使用 new;若重建集群,需清空数据目录或改为 existing。