Skip to content

核心概念 - 集群管理 ​

etcd 集群的管理是运维工作的核心。本章将学习如何搭建、配置和管理 etcd 集群。

集群架构 ​

节点角色 ​

etcd 集群中的每个节点都是平等的,没有主从之分,但 Raft 会选举出 Leader。

┌───────────────────────────────────────────────────────┐
│                   etcd 群                              │
│                                                        │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐       │
│   │  Node 1  │◄──►│  Node 2  │◄──►│  Node 3  │       │
│   │ (Leader) │    │(Follower)│    │(Follower)│       │
│   └──────────┘    └──────────┘    └──────────┘       │
│                                                        │
│   客户端端口: 2379    对等端口: 2380                  │
└───────────────────────────────────────────────────────┘

集群规模 ​

集群大小容忍故障节点数适用场景
10开发测试
31生产环境(推荐)
52高可用生产环境
73关键业务

推荐奇数节点,因为:

  • 多数派要求:至少 N/2 + 1 个节点存活
  • 3 个节点可以容忍 1 个故障
  • 4 个节点也只能容忍 1 个故障(不比 3 强)

集群搭建 ​

静态配置 ​

预先知道所有节点地址,适合小规模集群。

本地开发集群 ​

bash
# 启动 3 节点本地集群
# 节点 1
docker run -d --name etcd-node1 \
  -p 2379:2379 -p 2380:2380 \
  quay.io/coreos/etcd:v3.5.17 \
  /usr/local/bin/etcd \
  --name node1 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://node1:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://node1:2380 \
  --initial-cluster node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380 \
  --initial-cluster-token my-etcd-cluster \
  --initial-cluster-state new

# 节点 2
docker run -d --name etcd-node2 \
  -p 22379:2379 -p 22380:2380 \
  quay.io/coreos/etcd:v3.5.17 \
  /usr/local/bin/etcd \
  --name node2 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://node2:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://node2:2380 \
  --initial-cluster node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380 \
  --initial-cluster-token my-etcd-cluster \
  --initial-cluster-state new

# 节点 3
docker run -d --name etcd-node3 \
  -p 32379:2379 -p 32380:2380 \
  quay.io/coreos/etcd:v3.5.17 \
  /usr/local/bin/etcd \
  --name node3 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://node3:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://node3:2380 \
  --initial-cluster node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380 \
  --initial-cluster-token my-etcd-cluster \
  --initial-cluster-state new

生产环境配置 ​

bash
# 节点配置示例
etcd \
  --name etcd-node1 \
  --data-dir /var/lib/etcd \
  --listen-client-urls https://10.0.1.10:2379 \
  --advertise-client-urls https://10.0.1.10:2379 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --initial-advertise-peer-urls https://10.0.1.10:2380 \
  --initial-cluster etcd-node1=https://10.0.1.10:2380,etcd-node2=https://10.0.1.11:2380,etcd-node3=https://10.0.1.12:2380 \
  --initial-cluster-token production-cluster \
  --initial-cluster-state new \
  --client-cert-auth \
  --trusted-ca-file /etc/etcd/ca.crt \
  --cert-file /etc/etcd/server.crt \
  --key-file /etc/etcd/server.key \
  --peer-client-cert-auth \
  --peer-trusted-ca-file /etc/etcd/ca.crt \
  --peer-cert-file /etc/etcd/peer.crt \
  --peer-key-file /etc/etcd/peer.key

动态发现 ​

通过 Discovery Service 获取节点列表,适合大规模集群。

bash
# 使用 Discovery Service
DISCOVERY_URL="https://discovery.etcd.io/new?size=3"

etcd \
  --name node1 \
  --data-dir /etcd-data \
  --discovery $DISCOVERY_URL \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://node1:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://node1:2380

Kubernetes StatefulSet ​

在 Kubernetes 中部署 etcd 集群。

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: etcd
spec:
  serviceName: etcd
  replicas: 3
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      containers:
      - name: etcd
        image: quay.io/coreos/etcd:v3.5.17
        ports:
        - containerPort: 2379
          name: client
        - containerPort: 2380
          name: peer
        command:
        - /bin/sh
        - -c
        - |
          etcd --name etcd-$(HOSTNAME) \
            --data-dir /var/lib/etcd \
            --listen-client-urls http://0.0.0.0:2379 \
            --advertise-client-urls http://etcd-$(HOSTNAME).etcd.default.svc.cluster.local:2379 \
            --listen-peer-urls http://0.0.0.0:2380 \
            --initial-advertise-peer-urls http://etcd-$(HOSTNAME).etcd.default.svc.cluster.local:2380 \
            --initial-cluster etcd-0=http://etcd-0.etcd.default.svc.cluster.local:2380,etcd-1=http://etcd-1.etcd.default.svc.cluster.local:2380,etcd-2=http://etcd-2.etcd.default.svc.cluster.local:2380 \
            --initial-cluster-token my-etcd-cluster \
            --initial-cluster-state new
        volumeMounts:
        - name: etcd-data
          mountPath: /var/lib/etcd
  volumeClaimTemplates:
  - metadata:
      name: etcd-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi

配置参数 ​

关键配置项 ​

参数说明默认值
--name节点名称default
--data-dir数据存储目录default.etcd
--listen-client-urls客户端监听地址http://127.0.0.1:2379
--advertise-client-urls客户端广播地址http://127.0.0.1:2379
--listen-peer-urls对等节点监听地址http://127.0.0.1:2380
--initial-advertise-peer-urls对等节点广播地址http://127.0.0.1:2380
--initial-cluster初始集群配置根据名称自动生成
--initial-cluster-token集群标识new-cluster
--initial-cluster-state集群初始状态new

性能调优参数 ​

bash
# 快照设置
--snapshot-count=10000         # 触发快照的日志条目数
--auto-compaction-retention=1h # 自动压缩保留时间

# 心跳和选举超时(单位:tick)
--heartbeat-interval=100       # 心跳间隔(毫秒)
--election-timeout=1000        # 选举超时(毫秒)

# quota 设置
--quota-backend-bytes=8GB      # 数据存储上限

配置文件 ​

推荐使用配置文件管理:

yaml
# etcd.conf.yml
name: etcd-node1
data-dir: /var/lib/etcd

listen-client-urls: http://0.0.0.0:2379
advertise-client-urls: http://10.0.1.10:2379

listen-peer-urls: http://0.0.0.0:2380
initial-advertise-peer-urls: http://10.0.1.10:2380

initial-cluster:
  etcd-node1: http://10.0.1.10:2380
  etcd-node2: http://10.0.1.11:2380
  etcd-node3: http://10.0.1.12:2380
initial-cluster-token: production-cluster
initial-cluster-state: new

snapshot-count: 10000
heartbeat-interval: 100
election-timeout: 1000

成员管理 ​

添加成员 ​

bash
# 添加新成员(需要先通知集群)
etcdctl member add node4 \
  --peer-urls=http://10.0.1.13:2380 \
  --learner=true  # 先作为 learner 加入

# 启动新节点
etcd \
  --name node4 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://10.0.1.13:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://10.0.1.13:2380 \
  --initial-cluster-state existing

# 将 learner 提升为正式成员
etcdctl member promote <member-id>

移除成员 ​

bash
# 查看成员列表
etcdctl member list

# 移除成员
etcdctl member remove <member-id>

# 被移除的节点需要清空数据目录才能重新加入

更新成员 ​

bash
# 更新成员的 peer URLs
etcdctl member update <member-id> \
  --peer-urls=http://new-address:2380

查看成员状态 ​

bash
# 成员列表
etcdctl member list -w table

# 成员详细状态
etcdctl endpoint status --cluster -w table

# 输出示例
+------------------------+------------------+---------+---------+-----------+------------+-----------+
|        ENDPOINT        |        ID        | VERSION | DB SIZE | IS LEADER | RAFT INDEX | RAFT TERM |
+------------------------+------------------+---------+---------+-----------+------------+-----------+
| http://10.0.1.10:2379 | 4917a7ab173fabe7 |  3.5.17 |   25 kB |      true |      12    |     2     |
| http://10.0.1.11:2379 | 592b7c8f7c5b4c3a |  3.5.17 |   25 kB |     false |      12    |     2     |
| http://10.0.1.12:2379 | 6a3b8d9e0c1a2b4c |  3.5.17 |   25 kB |     false |      12    |     2     |
+------------------------+------------------+---------+---------+-----------+------------+-----------+

安全配置 ​

TLS 加密 ​

生成证书 ​

bash
# 创建 CA
cfssl gencert -initca ca-csr.json | cfssljson -bare ca

# 创建服务器证书
cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname="node1,node2,node3,localhost,127.0.0.1" \
  server-csr.json | cfssljson -bare server

# 创建对等证书
cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname="node1,node2,node3,localhost,127.0.0.1" \
  peer-csr.json | cfssljson -bare peer

# 创建客户端证书
cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  client-csr.json | cfssljson -bare client

配置 TLS ​

bash
etcd \
  --name node1 \
  --client-cert-auth \
  --trusted-ca-file /etc/etcd/ca.pem \
  --cert-file /etc/etcd/server.pem \
  --key-file /etc/etcd/server-key.pem \
  --peer-client-cert-auth \
  --peer-trusted-ca-file /etc/etcd/ca.pem \
  --peer-cert-file /etc/etcd/peer.pem \
  --peer-key-file /etc/etcd/peer-key.pem

使用 TLS 连接 ​

bash
# 使用客户端证书
etcdctl \
  --endpoints=https://node1:2379 \
  --cacert=/etc/etcd/ca.pem \
  --cert=/etc/etcd/client.pem \
  --key=/etc/etcd/client-key.pem \
  put /foo bar

RBAC 认证 ​

bash
# 启用认证
etcdctl auth enable

# 创建用户
etcdctl user add root
etcdctl user add app-user

# 创建角色
etcdctl role add app-role

# 授权角色访问键范围
etcdctl role grant-permission app-role read /app/
etcdctl role grant-permission app-role write /app/

# 为用户分配角色
etcdctl user grant-role app-user app-role

# 用户登录
etcdctl --user app-user:password get /app/config

故障处理 ​

集群健康检查 ​

bash
# 检查集群健康
etcdctl endpoint health --cluster

# 输出
http://node1:2379 is healthy: successfully committed proposal: took = 1.2ms
http://node2:2379 is healthy: successfully committed proposal: took = 2.3ms
http://node3:2379 is healthy: successfully committed proposal: took = 1.8ms

数据备份 ​

bash
# 创建快照
etcdctl snapshot save backup.db

# 查看快照状态
etcdctl snapshot status backup.db -w table

# 输出
+----------+----------+------------+------------+
|  HASH    | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| 2a56c3e8 |     1024 |        256 |     1.2 MB |
+----------+----------+------------+------------+

数据恢复 ​

bash
# 从快照恢复集群
# 1. 停止所有 etcd 节点
# 2. 在每个节点上恢复数据
etcdctl snapshot restore backup.db \
  --name node1 \
  --data-dir /var/lib/etcd-node1 \
  --initial-cluster node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380 \
  --initial-cluster-token my-etcd-cluster

etcdctl snapshot restore backup.db \
  --name node2 \
  --data-dir /var/lib/etcd-node2 \
  --initial-cluster node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380 \
  --initial-cluster-token my-etcd-cluster

etcdctl snapshot restore backup.db \
  --name node3 \
  --data-dir /var/lib/etcd-node3 \
  --initial-cluster node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380 \
  --initial-cluster-token my-etcd-cluster

# 3. 启动所有节点(使用 --initial-cluster-state new)

运维操作 ​

bash
# 数据压缩(删除历史版本)
etcdctl compact 1000

# 碎片整理
etcdctl defrag --endpoints=http://node1:2379

# 检查数据完整性
etcdctl check perf
etcdctl check datascale

监控指标 ​

Prometheus 指标 ​

etcd 提供丰富的 Prometheus 指标:

# 集群健康
etcd_server_has_leader
etcd_server_leader_changes_seen_total

# 性能指标
etcd_server_read_indexes_failed_total
etcd_server_slow_read_indexes_total
etcd_mvcc_db_total_size_in_bytes

# Raft 指标
etcd_raft_commit_index
etcd_raft_applied_index
etcd_raft_proposal_failed_total

# 网络
etcd_network_peer_received_bytes_total
etcd_network_peer_sent_bytes_total

Grafana Dashboard ​

使用官方 Grafana Dashboard 监控 etcd:

运维最佳实践 ​

  1. 集群规划

    • 生产环境至少 3 个节点
    • 跨机房部署时考虑网络延迟
    • 节点数量保持奇数
  2. 硬件要求

    • CPU: 2-4 核
    • 内存: 8-16 GB
    • 存储: SSD,至少 10GB(建议 50GB+)
    • 网络: 低延迟(<10ms)
  3. 安全加固

    • 启用 TLS 加密通信
    • 启用 RBAC 认证
    • 定期更新证书
  4. 备份策略

    • 定期创建快照(每天或每周)
    • 快照存储到远程存储
    • 定期验证恢复流程
  5. 监控告警

    • 监控 Leader 变更次数
    • 监控数据库大小
    • 监控请求延迟和吞吐量

总结 ​

etcd 集群管理涉及:

  1. 集群搭建:静态配置、动态发现、Kubernetes 部署
  2. 成员管理:添加、删除、更新成员
  3. 安全配置:TLS 加密、RBAC 认证
  4. 故障处理:备份恢复、数据压缩
  5. 监控运维:健康检查、性能监控

掌握这些运维技能是保障 etcd 集群稳定运行的关键。在下一章中,我们将学习如何使用 Go 客户端连接和操作 etcd。