Skip to content

生产部署 ​

本章将学习如何在生产环境中部署、运维和监控 etcd 集群。

生产架构设计 ​

集群拓扑 ​

单机房部署 ​

┌──────────────────────────────────────────────────────────┐
│                        机房 A                              │
│                                                           │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐          │
│   │  Node 1  │    │  Node 2  │    │  Node 3  │          │
│   │ (Leader) │◄──►│(Follower)│◄──►│(Follower)│          │
│   └──────────┘    └──────────┘    └──────────┘          │
│                                                           │
│   网络延迟 < 10ms                                         │
└──────────────────────────────────────────────────────────┘

适用场景:

  • 中小型企业
  • 网络延迟低
  • 运维简单

跨机房部署 ​

┌─────────────────┐         ┌─────────────────┐
│      机房 A      │         │      机房 B      │
│                 │         │                 │
│  ┌──────────┐  │         │  ┌──────────┐  │
│  │  Node 1  │  │◄────────►│  │  Node 2  │  │
│  │ (Leader) │  │         │  │(Follower)│  │
│  └──────────┘  │         │  └──────────┘  │
│                 │         │                 │
│  ┌──────────┐  │         │  ┌──────────┐  │
│  │  Node 3  │  │         │  │  Node 4  │  │
│  │(Follower)│◄─┼─────────┼─►│(Follower)│  │
│  └──────────┘  │         │  └──────────┘  │
│                 │         │                 │
└─────────────────┘         └─────────────────┘

注意:跨机房网络延迟应尽量低(建议 < 50ms)

适用场景:

  • 大型企业
  • 容灾要求高
  • 网络稳定

节点规划 ​

集群规模容忍故障数适用场景
3 节点1小规模生产
5 节点2中规模生产
7 节点3大规模生产

硬件要求 ​

项目最低要求推荐配置
CPU2 核4-8 核
内存8 GB16-32 GB
存储10 GB50-100 GB SSD
网络1 Gbps10 Gbps

关键要求:

  • SSD 存储:顺序写入性能至关重要
  • 低延迟网络:节点间延迟 < 10ms(同机房)
  • 独立部署:避免与其他服务竞争资源

Kubernetes 部署 ​

StatefulSet 配置 ​

yaml
apiVersion: v1
kind: Service
metadata:
  name: etcd
  labels:
    app: etcd
spec:
  clusterIP: None
  ports:
  - port: 2379
    name: client
  - port: 2380
    name: peer
  selector:
    app: etcd

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: etcd
spec:
  serviceName: etcd
  replicas: 3
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                app: etcd
            topologyKey: kubernetes.io/hostname
      containers:
      - name: etcd
        image: quay.io/coreos/etcd:v3.5.17
        ports:
        - containerPort: 2379
          name: client
        - containerPort: 2380
          name: peer
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        command:
        - /bin/sh
        - -c
        - |
          PEERS="etcd-0=http://etcd-0.etcd:2380,etcd-1=http://etcd-1.etcd:2380,etcd-2=http://etcd-2.etcd:2380"
          exec etcd --name ${POD_NAME} \
            --data-dir /var/lib/etcd \
            --listen-client-urls http://0.0.0.0:2379 \
            --advertise-client-urls http://${POD_NAME}.etcd.${POD_NAMESPACE}.svc.cluster.local:2379 \
            --listen-peer-urls http://0.0.0.0:2380 \
            --initial-advertise-peer-urls http://${POD_NAME}.etcd.${POD_NAMESPACE}.svc.cluster.local:2380 \
            --initial-cluster ${PEERS} \
            --initial-cluster-token my-etcd-cluster \
            --initial-cluster-state new \
            --snapshot-count 10000 \
            --heartbeat-interval 100 \
            --election-timeout 1000
        volumeMounts:
        - name: etcd-data
          mountPath: /var/lib/etcd
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
          limits:
            cpu: 2
            memory: 4Gi
  volumeClaimTemplates:
  - metadata:
      name: etcd-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: ssd-storage
      resources:
        requests:
          storage: 50Gi

持久化存储 ​

使用 StorageClass 配置 SSD 存储:

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd-storage
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  fsType: ext4
volumeBindingMode: WaitForFirstConsumer

性能调优 ​

关键参数 ​

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

# 快照和压缩
--snapshot-count=10000           # 触发快照的日志数
--auto-compaction-retention=1h   # 自动压缩保留时间
--auto-compaction-mode=periodic  # 压缩模式

# quota
--quota-backend-bytes=8589934592 # 数据上限(8GB)

# 性能
--max-request-bytes=15728640     # 单次请求最大字节数(15MB)
--max-txn-ops=128                # 单次事务最大操作数

性能基准 ​

bash
# 运行性能基准测试
etcdctl check perf --load=s

# 输出
Pass: read-only requests are within limit.
Pass: read-write requests are within limit.
Pass: write-only requests are within limit.

性能指标 ​

指标目标值
P99 读延迟< 10ms
P99 写延迟< 20ms
QPS10,000+
数据大小< 8GB

监控告警 ​

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
etcd_disk_wal_fsync_duration_seconds_sum
etcd_disk_backend_commit_duration_seconds_sum

# 网络
etcd_network_peer_received_bytes_total
etcd_network_peer_sent_bytes_total
etcd_network_client_received_bytes_total
etcd_network_client_sent_bytes_total

# Raft
etcd_raft_proposal_failed_total
etcd_raft_proposal_committed_total
etcd_raft_proposal_applied_total

Prometheus 配置 ​

yaml
global:
  scrape_interval: 15s

scrape_configs:
- job_name: 'etcd'
  static_configs:
  - targets:
    - 'etcd-0:2379'
    - 'etcd-1:2379'
    - 'etcd-2:2379'
  metrics_path: '/metrics'

关键告警规则 ​

yaml
groups:
- name: etcd-alerts
  rules:
  # Leader 变更频繁
  - alert: EtcdLeaderChangesFrequent
    expr: increase(etcd_server_leader_changes_seen_total[1h]) > 10
    for: 5m
    severity: warning
    message: "etcd Leader 变更频繁"

  # 没有 Leader
  - alert: EtcdNoLeader
    expr: etcd_server_has_leader == 0
    for: 1m
    severity: critical
    message: "etcd 集群没有 Leader"

  # 数据库过大
  - alert: EtcdDatabaseSizeTooLarge
    expr: etcd_mvcc_db_total_size_in_bytes > 8589934592
    for: 5m
    severity: warning
    message: "etcd 数据库大小超过 8GB"

  #慢请求
  - alert: EtcdSlowRequests
    expr: etcd_server_slow_read_indexes_total > 100
    for: 5m
    severity: warning
    message: "etcd 慢请求过多"

  # 磁盘延迟高
  - alert: EtcdDiskFsyncSlow
    expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) > 0.1
    for: 5m
    severity: warning
    message: "etcd WAL fsync 延迟过高"

Grafana Dashboard ​

使用官方 Dashboard:

关键监控面板:

  • 集群健康状态
  • 读写 QPS 和延迟
  • 数据库大小
  • 网络流量
  • Leader 变更次数

维护操作 ​

数据备份 ​

bash
# 创建快照
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db \
  --endpoints=https://etcd-0:2379 \
  --cacert=/etc/etcd/ca.pem \
  --cert=/etc/etcd/client.pem \
  --key=/etc/etcd/client-key.pem

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

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

自动备份脚本 ​

bash
#!/bin/bash
# etcd-backup.sh

BACKUP_DIR="/backup/etcd"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/etcd-${DATE}.db"

# 创建快照
etcdctl snapshot save ${BACKUP_FILE}

# 检查快照
if etcdctl snapshot status ${BACKUP_FILE} &>/dev/null; then
    echo "Backup succeeded: ${BACKUP_FILE}"
else
    echo "Backup failed"
    rm -f ${BACKUP_FILE}
    exit 1
fi

# 清理旧备份(保留 7 天)
find ${BACKUP_DIR} -name "etcd-*.db" -mtime +7 -delete

# 上传到远程存储(示例:AWS S3)
aws s3 cp ${BACKUP_FILE} s3://my-bucket/etcd-backups/

数据恢复 ​

bash
# 1. 停止所有 etcd 节点
kubectl delete statefulset etcd

# 2. 在每个节点上恢复数据
for i in 0 1 2; do
    etcdctl snapshot restore /backup/etcd-20250106-120000.db \
        --name etcd-${i} \
        --data-dir /var/lib/etcd-${i} \
        --initial-cluster etcd-0=http://etcd-0.etcd:2380,etcd-1=http://etcd-1.etcd:2380,etcd-2=http://etcd-2.etcd:2380 \
        --initial-cluster-token my-etcd-cluster \
        --initial-advertise-peer-urls http://etcd-${i}.etcd:2380
done

# 3. 启动 etcd 集群(使用 --initial-cluster-state new)
kubectl apply -f etcd-statefulset.yaml

数据压缩 ​

bash
# 获取当前 revision
REVISION=$(etcdctl endpoint status --write-out="json" | grep -o '"revision":[0-9]*' | head -1 | grep -o '[0-9]*')

# 压缩历史版本
etcdctl compact ${REVISION}

# 碎片整理
etcdctl defrag --endpoints=http://etcd-0:2379,http://etcd-1:2379,http://etcd-2:2379

# 检查数据库大小
etcdctl endpoint status --write-out="table"

自动压缩配置 ​

bash
# 启动时配置自动压缩
etcd \
  --auto-compaction-retention=1h \
  --auto-compaction-mode=periodic

添加成员 ​

bash
# 添加新成员(先作为 learner)
etcdctl member add etcd-3 \
  --peer-urls=http://etcd-3:2380 \
  --learner=true

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

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

移除成员 ​

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

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

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

安全加固 ​

TLS 配置 ​

bash
# 客户端 TLS
etcd \
  --client-cert-auth \
  --trusted-ca-file /etc/etcd/ca.pem \
  --cert-file /etc/etcd/server.pem \
  --key-file /etc/etcd/server-key.pem

# 对等 TLS
etcd \
  --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

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 readwrite /app/

etcdctl user grant-role app-user app-role

# 测试认证
etcdctl --user app-user:password get /app/config

证书自动轮换 ​

使用 cert-manager 自动管理证书:

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: etcd-server
spec:
  secretName: etcd-server-tls
  issuerRef:
    name: etcd-ca
    kind: Issuer
  commonName: etcd
  dnsNames:
  - etcd-0.etcd
  - etcd-1.etcd
  - etcd-2.etcd
  - localhost
  duration: 2160h  # 90 天
  renewBefore: 360h  # 自动续期

故障处理 ​

节点故障 ​

bash
# 检查节点状态
etcdctl endpoint health --cluster

# 输出
http://etcd-0:2379 is healthy: successfully committed proposal: took = 1.2ms
http://etcd-1:2379 is unhealthy: failed to connect
http://etcd-2:2379 is healthy: successfully committed proposal: took = 1.5ms

# 移除故障节点
etcdctl member remove <member-id>

# 添加新节点替换
etcdctl member add etcd-1-new --peer-urls=http://etcd-1-new:2380

Leader 选举失败 ​

bash
# 检查 Leader 状态
etcdctl endpoint status --cluster -w table

# 如果无 Leader,检查网络和磁盘
# 重启节点尝试恢复

数据损坏 ​

bash
# 从健康节点恢复数据
# 1. 创建快照
etcdctl snapshot save /backup/etcd-recovery.db

# 2. 在损坏节点上恢复
etcdctl snapshot restore /backup/etcd-recovery.db \
  --name etcd-0 \
  --data-dir /var/lib/etcd \
  --initial-cluster-state new

网络分区 ​

bash
# 检查集群状态
etcdctl endpoint status --cluster

# 如果发生网络分区:
# 1. 确认多数派节点可达
# 2. 多数派继续工作
# 3. 少数派节点等待网络恢复
# 4. 网络恢复后自动同步

运维最佳实践 ​

日常运维检查清单 ​

检查项命令频率
集群健康etcdctl endpoint health --cluster每小时
数据库大小etcdctl endpoint status --cluster每天
备份状态etcdctl snapshot status每天备份后
证书有效期检查证书到期时间每月
性能基准etcdctl check perf每周

性能优化建议 ​

  1. 存储优化

    • 使用 SSD 存储
    • 定期压缩和碎片整理
    • 控制数据大小 < 8GB
  2. 网络优化

    • 同机房部署,延迟 < 10ms
    • 使用高速网络(10 Gbps)
    • 避免 NAT 和代理
  3. 调优配置

    • 根据负载调整心跳和选举超时
    • 合理设置快照阈值
    • 启用自动压缩

容灾策略 ​

  1. 备份策略

    • 每天创建快照备份
    • 备份存储到远程存储
    • 定期验证恢复流程
  2. 多机房部署

    • 跨机房部署至少 3 个节点
    • 网络延迟 < 50ms
    • 节点分布在不同机房
  3. 故障演练

    • 定期进行故障演练
    • 验证自动恢复流程
    • 测试备份恢复

Kubernetes etcd 特殊注意 ​

  1. Kubernetes 集群状态

    • etcd 存储 Kubernetes 所有状态
    • etcd 故障会导致 Kubernetes 不可用
    • 必须定期备份
  2. 资源隔离

    • etcd 独立部署
    • 使用专用的 SSD 存储
    • 避免资源竞争
  3. 监控集成

    • 监控 Kubernetes API Server 对 etcd 的访问
    • 监控 etcd 响应时间
    • 设置合理的告警阈值

总结 ​

生产部署 etcd 需要关注:

  1. 架构设计:集群拓扑、节点规划、硬件配置
  2. 性能调优:参数配置、性能基准、监控指标
  3. 运维管理:数据备份、恢复、压缩、成员管理
  4. 安全加固:TLS 配置、RBAC、证书管理
  5. 故障处理:节点故障、数据损坏、网络分区
  6. 最佳实践:日常检查、性能优化、容灾策略

掌握这些运维知识,才能保障 etcd 集群在生产环境中稳定运行。