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 通信。
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 官方恢复文档
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