4. 搭建 etcd 压测沙盘环境:Docker Compose 与 Kind 双模式部署
本章将指导你使用两种主流本地容器化方案——Docker Compose 与 Kind (Kubernetes in Docker),分别搭建多节点 etcd 集群,并配置完整的 Prometheus + Grafana 监控栈。前者适合快速原型验证与单机压测,后者更贴近生产级 K8s 跨集群调度场景。两种环境均会输出可复用的配置文件与启动脚本。
4.1 环境概览与选型对比
在开始搭建前,先明确两种方案的适用场景与核心差异:
| 对比维度 | Docker Compose 方案 | Kind 方案 |
|---|---|---|
| 部署复杂度 | 低,单文件编排 | 中,需先创建 Kind 集群 |
| 网络模型 | Docker bridge 网络 | K8s Pod 网络 (CNI) |
| etcd 成员发现 | 静态配置 IP:Port | Headless Service + DNS SRV |
| 压测场景适配 | 单机多进程压测 | 跨节点/跨集群调度延迟 |
| 监控栈集成 | 手动配置 targets | Prometheus Operator CRD |
| 资源占用 | 约 2GB 内存 | 约 4GB 内存 (含 K8s 组件) |
选型建议: 若你主要关注 etcd 本身的读写性能与 Raft 行为,优先使用 Docker Compose;若需要模拟 K8s 调度器与 etcd 的交互延迟,或进行跨集群调度优化实验,请使用 Kind 方案。
4.2 使用 Docker Compose 搭建 3 节点 etcd 集群
以下配置将启动一个 3 节点 etcd 集群,节点间通过静态 peer URL 通信,并暴露客户端端口供压测工具连接。
4.2.1 docker-compose.yml 核心配置
version: '3.8'
services:
etcd1:
image: quay.io/coreos/etcd:v3.5.15
container_name: etcd-node1
environment:
- ETCD_NAME=etcd1
- ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- ETCD_INITIAL_CLUSTER_STATE=new
- ETCD_INITIAL_CLUSTER_=etcd-cluster-
- 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://etcd1:2379
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd1:2380
- ETCD_QUOTA_BACKEND_BYTES=8589934592 # 8GB 后端配额
- ETCD_SNAPSHOT_COUNT=10000
ports:
- "23791:2379"
- "23801:2380"
volumes:
- etcd1-data:/etcd-data
networks:
- etcd-net
etcd2:
image: quay.io/coreos/etcd:v3.5.15
container_name: etcd-node2
environment:
- ETCD_NAME=etcd2
- ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- ETCD_INITIAL_CLUSTER_STATE=new
- ETCD_INITIAL_CLUSTER_=etcd-cluster-
- 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://etcd2:2379
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd2:2380
- ETCD_QUOTA_BACKEND_BYTES=8589934592
- ETCD_SNAPSHOT_COUNT=10000
ports:
- "23792:2379"
- "23802:2380"
volumes:
- etcd2-data:/etcd-data
networks:
- etcd-net
etcd3:
image: quay.io/coreos/etcd:v3.5.15
container_name: etcd-node3
environment:
- ETCD_NAME=etcd3
- ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- ETCD_INITIAL_CLUSTER_STATE=new
- ETCD_INITIAL_CLUSTER_=etcd-cluster-
- 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://etcd3:2379
- ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd3:2380
- ETCD_QUOTA_BACKEND_BYTES=8589934592
- ETCD_SNAPSHOT_COUNT=10000
ports:
- "23793:2379"
- "23803:2380"
volumes:
- etcd3-data:/etcd-data
networks:
- etcd-net
volumes:
etcd1-data:
etcd2-data:
etcd3-data:
networks:
etcd-net:
driver: bridge
4.2.2 启动与验证
# 启动集群
docker compose -f docker-compose.yml up -d
# 验证成员列表
docker exec etcd-node1 etcdctl member list --write-out=table
# 测试写入与读取
docker exec etcd-node1 etcdctl put /test/key "hello-sandbox"
docker exec etcd-node1 etcdctl get /test/key
# 检查集群健康状态
docker exec etcd-node1 etcdctl endpoint health --cluster
关键参数说明:
ETCD_QUOTA_BACKEND_BYTES:设置后端数据库配额为 8GB,避免压测时因空间不足导致写入失败。ETCD_SNAPSHOT_COUNT:每 10000 次提交触发一次快照,平衡恢复速度与 I/O 开销。- 端口映射规则:宿主机
23791/23792/23793分别对应三个节点的客户端端口,便于压测工具直接连接。
4.3 使用 Kind 搭建 K8s 内嵌 etcd 集群
Kind 方案将 etcd 以 StatefulSet 形式运行在本地 K8s 集群中,更真实地模拟生产环境下的网络拓扑与 DNS 服务发现。
4.3.1 创建 Kind 集群并配置节点
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30090
hostPort: 30090
protocol: TCP
- role: worker
- role: worker
- role: worker
# 创建集群
kind create cluster --config kind-config.yaml --name etcd-sandbox
# 验证节点状态
kubectl get nodes -o wide
4.3.2 部署 etcd StatefulSet 与 Headless Service
# etcd-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: etcd-headless
namespace: default
spec:
clusterIP: None
ports:
- name: client
port: 2379
targetPort: 2379
- name: peer
port: 2380
targetPort: 2380
selector:
app: etcd
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
namespace: default
spec:
serviceName: etcd-headless
replicas: 3
selector:
matchLabels:
app: etcd
template:
metadata:
labels:
app: etcd
spec:
containers:
- name: etcd
image: quay.io/coreos/etcd:v3.5.15
env:
- name: ETCD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: ETCD_INITIAL_CLUSTER
value: "etcd-0=http://etcd-0.etcd-headless:2380,etcd-1=http://etcd-1.etcd-headless:2380,etcd-2=http://etcd-2.etcd-headless:2380"
- name: ETCD_INITIAL_CLUSTER_STATE
value: "new"
- name: ETCD_INITIAL_CLUSTER_
value: "etcd-kind-"
- name: ETCD_LISTEN_PEER_URLS
value: "http://0.0.0.0:2380"
- name: ETCD_LISTEN_CLIENT_URLS
value: "http://0.0.0.0:2379"
- name: ETCD_ADVERTISE_CLIENT_URLS
value: "http://$(POD_NAME).etcd-headless:2379"
- name: ETCD_INITIAL_ADVERTISE_PEER_URLS
value: "http://$(POD_NAME).etcd-headless:2380"
- name: ETCD_QUOTA_BACKEND_BYTES
value: "8589934592"
ports:
- containerPort: 2379
name: client
- containerPort: 2380
name: peer
volumeMounts:
- name: data
mountPath: /etcd-data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
# 部署到 Kind 集群
kubectl apply -f etcd-statefulset.yaml
# 等待 Pod 全部 Running
kubectl get pods -l app=etcd -w
# 通过 DNS 验证集群成员
kubectl run -it --rm debug --image=busybox -- nslookup etcd-headless
注意: Kind 环境下 etcd 的
ETCD_ADVERTISE_CLIENT_URLS 必须使用 $(POD_NAME).etcd-headless:2379 格式,否则客户端无法通过 DNS SRV 记录正确发现所有成员。这是 Kind 网络与生产 K8s 环境的关键差异点。
4.4 配置 Prometheus + Grafana 监控栈
监控栈是压测沙盘的眼睛。我们将为两种环境分别配置 Prometheus 抓取 etcd 指标,并用 Grafana 展示集群状态与性能面板。
4.4.1 监控架构图
4.4.2 Prometheus 配置(Docker Compose 环境)
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'etcd-cluster'
static_configs:
- targets:
- 'etcd1:2379'
- 'etcd2:2379'
- 'etcd3:2379'
metrics_path: /metrics
scheme: http
- job_name: 'node-exporter'
static_configs:
- targets:
- 'node-exporter:9100'
在 docker-compose.yml 中追加 Prometheus 与 Grafana 服务:
prometheus:
image: prom/prometheus:v2.53.0
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- etcd-net
grafana:
image: grafana/grafana:11.1.0
container_name: grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
networks:
- etcd-net
volumes:
grafana-data:
4.4.3 Prometheus 配置(Kind 环境)
Kind 环境下推荐使用 Prometheus Operator 或直接部署 Prometheus 实例并通过 Service 发现 etcd Pod:
# prometheus-kind.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'etcd'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: etcd
action: keep
- source_labels: [__meta_kubernetes_pod_container_port_number]
regex: "2379"
action: keep
- source_labels: [__address__]
action: replace
regex: ([^:]+)(?::\d+)?
replacement: $1:2379
target_label: __address__
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:v2.53.0
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
ports:
- containerPort: 9090
volumeMounts:
- name: config
mountPath: /etc/prometheus
- name: data
mountPath: /prometheus
volumes:
- name: config
configMap:
name: prometheus-config
- name: data
emptyDir: {}
4.4.4 Grafana 仪表盘导入
Grafana 启动后,通过浏览器访问 http://localhost:3000(默认账号/密码:admin/admin)。推荐导入以下两个关键仪表盘:
| 仪表盘名称 | Grafana Dashboard ID | 核心监控指标 |
|---|---|---|
| etcd 集群概览 | 3070 | Leader 变化、Raft 提案延迟、DB 大小、磁盘 fsync 延迟 |
| etcd 性能细粒度 | 16220 | 99 分位读写延迟、后端提交耗时、WAL 写入速率、gRPC 请求分布 |
快速导入命令:
# 通过 Grafana API 导入仪表盘 3070
curl -X POST -H "Content-Type: application/json" \
-d '{"dashboard":{"id":null,"uid":"etcd-cluster","title":"etcd Cluster Overview"},"overwrite":true}' \
http://admin:admin@localhost:3000/api/dashboards/db
更推荐在 Grafana UI 中通过 Import 功能直接输入 Dashboard ID 进行可视化导入。
4.5 环境验证与压测前置检查
在开始压测前,执行以下清单确认沙盘环境就绪:
- etcd 集群健康检查: 所有节点均为
healthy,Leader 唯一且稳定。 - Prometheus 目标状态: 访问
http://localhost:9090/targets,确认三个 etcd 节点均为UP状态。 - Grafana 数据源: 已添加 Prometheus 数据源,且测试通过。
- 网络连通性: 压测工具所在容器/主机能够通过
2379端口访问所有 etcd 节点。 - 磁盘 I/O 基线: 使用
fio或iostat记录宿主机磁盘延迟基线,便于后续压测时对比。
常见问题排查:
- etcd 节点无法加入集群: 检查
ETCD_INITIAL_CLUSTER中的主机名是否与容器名或 Pod 名完全匹配,Kind 环境下注意 DNS 解析。 - Prometheus 抓取超时: 增大
scrape_timeout至 30s,或检查 etcd 节点是否因高负载导致 metrics 端点响应缓慢。 - Grafana 面板无数据: 确认 Prometheus 数据源中的 URL 是否正确(Docker Compose 使用容器名,Kind 使用 Service ClusterIP)。
4.6 沙盘环境目录结构
为便于后续章节复用,建议按以下结构组织沙盘文件:
etcd-sandbox/
├── docker-compose/
│ ├── docker-compose.yml
│ ├── prometheus.yml
│ └── grafana-dashboards/
│ ├── etcd-cluster-overview.json
│ └── etcd-performance-detailed.json
├── kind/
│ ├── kind-config.yaml
│ ├── etcd-statefulset.yaml
│ ├── prometheus-kind.yml
│ └── grafana-deployment.yaml
├── scripts/
│ ├── start-docker-compose.sh
│ ├── start-kind.sh
│ └── verify-cluster.sh
└── README.md