4. 压测沙盘环境搭建:Docker Compose 部署 etcd 集群(3节点)

本章将使用 Docker Compose 在单台物理机或虚拟机上快速搭建一个 3 节点的 etcd 集群,并配置网络隔离与数据持久化。该环境将作为后续压测沙盘的基础设施,模拟生产环境中的高可用 etcd 部署。

4.1 环境准备与目录结构

在开始之前,确保宿主机已安装 Docker Engine(20.10+)和 Docker Compose(v2 或 v1.29+)。建议使用 Linux 或 macOS 系统,Windows 需启用 WSL2。

创建项目根目录 etcd-bench-sandbox,并建立以下子目录结构:

etcd-bench-sandbox/
├── docker-compose.yml          # 主编排文件
├── .env                        # 环境变量(节点 IP、版本等)
├── etcd1/
│   └── data/                   # 节点1数据持久化目录
├── etcd2/
│   └── data/                   # 节点2数据持久化目录
├── etcd3/
│   └── data/                   # 节点3数据持久化目录
└── scripts/
    └── health-check.sh         # 集群健康检查脚本
设计原则:每个 etcd 节点拥有独立的数据目录,避免容器重启后数据丢失。生产环境中建议将 data/ 挂载到高性能 SSD 磁盘路径。

4.2 Docker Compose 编排文件

以下 docker-compose.yml 定义了 3 个 etcd 服务,使用 bitnami/etcd 镜像(基于官方 etcd 构建,配置更友好)。每个节点通过环境变量指定集群成员、监听地址和初始集群状态。

version: '3.8'

services:
  etcd1:
    image: bitnami/etcd:3.5.15
    container_name: etcd-bench-1
    hostname: etcd1
    networks:
      etcd-net:
        ipv4_address: 172.20.0.11
    ports:
      - "23791:2379"   # 客户端端口映射到宿主机
      - "23801:2380"   # 对等通信端口映射
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_NAME=etcd1
      - ETCD_INITIAL_CLUSTER=etcd1=http://172.20.0.11:2380,etcd2=http://172.20.0.12:2380,etcd3=http://172.20.0.13:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
      - ETCD_INITIAL_CLUSTER_=etcd-bench-
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://172.20.0.11:2379
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://172.20.0.11:2380
      - ETCD_DATA_DIR=/data
    volumes:
      - ./etcd1/data:/data
    restart: unless-stopped

  etcd2:
    image: bitnami/etcd:3.5.15
    container_name: etcd-bench-2
    hostname: etcd2
    networks:
      etcd-net:
        ipv4_address: 172.20.0.12
    ports:
      - "23792:2379"
      - "23802:2380"
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_NAME=etcd2
      - ETCD_INITIAL_CLUSTER=etcd1=http://172.20.0.11:2380,etcd2=http://172.20.0.12:2380,etcd3=http://172.20.0.13:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
      - ETCD_INITIAL_CLUSTER_=etcd-bench-
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://172.20.0.12:2379
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://172.20.0.12:2380
      - ETCD_DATA_DIR=/data
    volumes:
      - ./etcd2/data:/data
    restart: unless-stopped

  etcd3:
    image: bitnami/etcd:3.5.15
    container_name: etcd-bench-3
    hostname: etcd3
    networks:
      etcd-net:
        ipv4_address: 172.20.0.13
    ports:
      - "23793:2379"
      - "23803:2380"
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_NAME=etcd3
      - ETCD_INITIAL_CLUSTER=etcd1=http://172.20.0.11:2380,etcd2=http://172.20.0.12:2380,etcd3=http://172.20.0.13:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
      - ETCD_INITIAL_CLUSTER_=etcd-bench-
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://172.20.0.13:2379
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://172.20.0.13:2380
      - ETCD_DATA_DIR=/data
    volumes:
      - ./etcd3/data:/data
    restart: unless-stopped

networks:
  etcd-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24
关键参数说明:
  • ETCD_INITIAL_CLUSTER:列出所有集群节点的对等通信地址,格式为 节点名=协议://IP:2380。
  • ETCD_INITIAL_CLUSTER_STATE=new:首次启动时设为 new;若集群已存在需恢复,则改为 existing。
  • ETCD_INITIAL_CLUSTER_:集群唯一标识,防止不同集群的节点误加入。
  • ALLOW_NONE_AUTHENTICATION=yes:开发/测试环境允许无认证访问;生产环境必须配置 TLS 和认证。

4.3 网络拓扑与端口映射

集群使用自定义桥接网络 etcd-net,子网 172.20.0.0/24。每个节点分配固定 IP,并通过宿主机端口映射暴露服务:

节点 容器 IP 宿主机端口(客户端) 宿主机端口(对等通信)
etcd1 172.20.0.11 23791 → 2379 23801 → 2380
etcd2 172.20.0.12 23792 → 2379 23802 → 2380
etcd3 172.20.0.13 23793 → 2379 23803 → 2380

宿主机上的压测工具(如 benchmark、etcdctl)可通过 127.0.0.1:23791 等端口访问集群。容器内部节点间通过固定 IP 和 2380 端口进行 Raft 通信。

3节点 etcd 集群网络拓扑 宿主机 (Host) Docker 桥接网络: etcd-net (172.20.0.0/24) etcd1 172.20.0.11 2379 / 2380 etcd2 172.20.0.12 2379 / 2380 etcd3 172.20.0.13 2379 / 2380 宿主机端口映射:23791→2379 / 23801→2380 等 压测工具通过 127.0.0.1:23791~23793 访问集群

4.4 启动集群与验证

执行以下命令启动集群:

# 进入项目目录
cd etcd-bench-sandbox

# 启动所有服务(后台运行)
docker compose up -d

# 查看容器状态
docker compose ps

# 查看实时日志(可选)
docker compose logs -f

启动后,使用 etcdctl 验证集群健康状态。可在宿主机上安装 etcdctl 或通过容器内执行:

# 通过容器内 etcdctl 检查成员列表
docker exec etcd-bench-1 etcdctl member list

# 检查集群健康状态(v3 API)
docker exec etcd-bench-1 etcdctl endpoint health --cluster

# 写入测试键值
docker exec etcd-bench-1 etcdctl put /test/key "hello-bench"

# 读取测试键值
docker exec etcd-bench-1 etcdctl get /test/key

预期输出示例:

# member list 输出
8213f9d7f9c0a1e2, started, etcd1, http://172.20.0.11:2380, http://172.20.0.11:2379, false
a4b6c8d0e2f4a6b8, started, etcd2, http://172.20.0.12:2380, http://172.20.0.12:2379, false
c5d7e9f1a3b5c7d9, started, etcd3, http://172.20.0.13:2380, http://172.20.0.13:2379, false

# endpoint health 输出
172.20.0.11:2379 is healthy: successfully committed proposal: took = 2.345ms
172.20.0.12:2379 is healthy: successfully committed proposal: took = 3.012ms
172.20.0.13:2379 is healthy: successfully committed proposal: took = 2.891ms
常见问题排查:
  • 端口冲突:如果宿主机 23791~23793 或 23801~23803 被占用,修改 docker-compose.yml 中的映射端口。
  • 集群无法形成:检查 ETCD_INITIAL_CLUSTER 中的 IP 是否与网络配置一致;确保所有节点使用相同的 ETCD_INITIAL_CLUSTER_。
  • 数据目录权限:确保宿主机 ./etcd*/data 目录对容器用户可写,必要时执行 chmod 777 或使用 Docker 卷。

4.5 数据持久化与备份策略

每个节点的数据目录通过 volumes 挂载到宿主机,确保容器重启或升级后数据不丢失。生产环境中建议:

  • 定期快照备份:使用 etcdctl snapshot save 将集群状态导出到安全存储。
  • 数据目录分离:将 data/ 挂载到独立的高 IOPS 磁盘(如 AWS EBS gp3、本地 NVMe)。
  • WAL 与数据分离:通过 ETCD_WAL_DIR 将 WAL 日志写入不同磁盘,减少写入竞争。
# 手动创建快照示例(在宿主机执行)
docker exec etcd-bench-1 etcdctl snapshot save /data/snapshot-$(date +%Y%m%d).db

# 从快照恢复(需停止集群并重新初始化)
# 详见 etcd 官方恢复文档
etcd 数据持久化与备份流程 etcd 容器 /data 目录 (member/ 子目录) 写入 宿主机持久化目录 ./etcd1/data/ (Docker volume 挂载) 快照 备份存储 S3 / NFS / 本地 snapshot-*.db 恢复流程:停止集群 → 清理数据 → 加载快照 → 重启 建议:每天定时执行 etcdctl snapshot save,保留最近 7 天快照 生产环境启用 --auto-compaction-retention=1 控制历史压缩

4.6 集群配置参数速查表

以下表格汇总了 etcd 集群启动的核心环境变量及其作用,便于后续调优:

环境变量 作用 示例值
ETCD_NAME 节点唯一名称,用于集群标识 etcd1
ETCD_INITIAL_CLUSTER 初始集群成员列表(节点名=对等URL) etcd1=http://172.20.0.11:2380,...
ETCD_INITIAL_CLUSTER_STATE 集群状态:new(新建)或 existing(加入已有) new
ETCD_INITIAL_CLUSTER_ 集群令牌,防止跨集群误加入 etcd-bench-
ETCD_LISTEN_PEER_URLS 节点监听对等通信的地址 http://0.0.0.0:2380
ETCD_LISTEN_CLIENT_URLS 节点监听客户端请求的地址 http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS 节点向客户端通告的访问地址 http://172.20.0.11:2379
ETCD_DATA_DIR 数据持久化目录 /data
ETCD_QUOTA_BACKEND_BYTES 后端存储配额(默认 2GB,压测时可调大) 8589934592 (8GB)
压测调优提示:在进行高吞吐压测前,建议将 ETCD_QUOTA_BACKEND_BYTES 提升至 8GB 或更高,避免因存储配额不足导致写入失败。同时可设置 ETCD_SNAPSHOT_COUNT=100000 减少频繁快照对性能的影响。

4.7 健康检查脚本

创建一个简单的健康检查脚本 scripts/health-check.sh,用于快速验证集群状态:

#!/bin/bash
# etcd 集群健康检查脚本
# 用法: ./scripts/health-check.sh

ENDPOINTS="127.0.0.1:23791 127.0.0.1:23792 127.0.0.1:23793"

echo "=== etcd 集群健康检查 ==="
echo ""

# 检查成员列表
echo ">> 成员列表:"
docker exec etcd-bench-1 etcdctl member list

echo ""
# 检查端点健康
echo ">> 端点健康状态:"
for ep in $ENDPOINTS; do
  docker exec etcd-bench-1 etcdctl endpoint health --endpoints=$ep
done

echo ""
# 检查集群状态
echo ">> 集群状态:"
docker exec etcd-bench-1 etcdctl endpoint status --cluster -w table

echo ""
echo "=== 检查完成 ==="

赋予执行权限并运行:

chmod +x scripts/health-check.sh
./scripts/health-check.sh