3. etcd 集群部署:单节点、静态集群、动态发现与 TLS 安全配置
etcd 作为分布式键值存储系统,其部署方式直接影响系统的可用性、扩展性和安全性。本章将系统讲解四种核心部署模式:单节点部署用于开发测试、静态集群部署用于生产环境、动态发现部署用于自动化场景,以及 TLS 安全配置用于生产加固。掌握这些部署方式,是后续进行压测脚本开发的基础。
3.1 单节点部署
单节点部署是 etcd 最简单的运行模式,适用于本地开发、功能验证和压测脚本调试。它不提供高可用能力,但启动速度快、配置简单。
适用场景: 本地开发环境、CI/CD 流水线中的集成测试、压测脚本的快速验证。
3.1.1 启动命令
# 下载 etcd 二进制后,直接启动单节点
etcd --name=etcd-single \
--data-dir=/tmp/etcd-data \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://127.0.0.1:2379 \
--listen-peer-urls=http://0.0.0.0:2380 \
--initial-advertise-peer-urls=http://127.0.0.1:2380 \
--initial-cluster=etcd-single=http://127.0.0.1:2380 \
--initial-cluster-=etcd-cluster-1 \
--initial-cluster-state=new
3.1.2 关键参数说明
| 参数 | 说明 | 默认值 |
|---|---|---|
--name |
节点名称,集群内唯一 | default |
--data-dir |
数据存储目录 | ${name}.etcd |
--listen-client-urls |
客户端监听地址 | http://localhost:2379 |
--advertise-client-urls |
对外公布的客户端地址 | http://localhost:2379 |
--initial-cluster |
初始集群成员列表 | — |
验证部署: 启动后使用
etcdctl endpoint health --endpoints=http://127.0.0.1:2379 检查节点健康状态,返回 healthy 即表示部署成功。
3.2 静态集群部署
静态集群部署是生产环境中最常用的方式,管理员在启动前预先确定所有节点的 IP 地址和端口,通过 --initial-cluster 参数一次性声明集群拓扑。这种方式配置明确、启动稳定,适合节点数量固定且网络环境可控的场景。
3.2.1 三节点集群配置示例
假设三台机器的 IP 分别为 192.168.1.10、192.168.1.11、192.168.1.12,每台机器上执行对应的启动命令:
# 节点 1 (192.168.1.10)
etcd --name=node1 \
--data-dir=/var/lib/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://192.168.1.10:2379 \
--listen-peer-urls=http://0.0.0.0:2380 \
--initial-advertise-peer-urls=http://192.168.1.10:2380 \
--initial-cluster=node1=http://192.168.1.10:2380,node2=http://192.168.1.11:2380,node3=http://192.168.1.12:2380 \
--initial-cluster-=etcd-cluster-prod \
--initial-cluster-state=new
# 节点 2 (192.168.1.11) — 仅修改 name 和 IP 地址
etcd --name=node2 \
--data-dir=/var/lib/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://192.168.1.11:2379 \
--listen-peer-urls=http://0.0.0.0:2380 \
--initial-advertise-peer-urls=http://192.168.1.11:2380 \
--initial-cluster=node1=http://192.168.1.10:2380,node2=http://192.168.1.11:2380,node3=http://192.168.1.12:2380 \
--initial-cluster-=etcd-cluster-prod \
--initial-cluster-state=new
# 节点 3 (192.168.1.12) — 仅修改 name 和 IP 地址
etcd --name=node3 \
--data-dir=/var/lib/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://192.168.1.12:2379 \
--listen-peer-urls=http://0.0.0.0:2380 \
--initial-advertise-peer-urls=http://192.168.1.12:2380 \
--initial-cluster=node1=http://192.168.1.10:2380,node2=http://192.168.1.11:2380,node3=http://192.168.1.12:2380 \
--initial-cluster-=etcd-cluster-prod \
--initial-cluster-state=new
3.2.2 集群状态验证
# 任意节点上执行成员列表查询
etcdctl member list --endpoints=http://192.168.1.10:2379
# 输出示例
# 8211f1d0f64f3269, started, node1, http://192.168.1.10:2380, http://192.168.1.10:2379, false
# 91bc3c398fb3c146, started, node2, http://192.168.1.11:2380, http://192.168.1.11:2379, false
# fd422379fdaa503d, started, node3, http://192.168.1.12:2380, http://192.168.1.12:2379, false
注意事项: 静态集群部署要求所有节点在启动前网络互通,且
--initial-cluster-state 必须为 new。如果集群已存在,需要改为 existing,否则会导致数据冲突。
3.3 动态发现部署
动态发现部署通过一个发现服务(Discovery Service)自动注册集群成员,适合节点 IP 不固定或需要自动扩缩容的场景。etcd 官方提供了公共发现服务 https://discovery.etcd.io,也支持自建发现服务。
3.3.1 获取发现
# 使用公共发现服务生成一个 3 节点集群的
curl https://discovery.etcd.io/new?size=3
# 返回类似:https://discovery.etcd.io/3e86b59922e3e5f8e3e5f8e3e5f8e3e5
3.3.2 使用发现 启动节点
# 所有节点使用相同的 discovery 启动
etcd --name=node1 \
--data-dir=/var/lib/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://192.168.1.10:2379 \
--listen-peer-urls=http://0.0.0.0:2380 \
--initial-advertise-peer-urls=http://192.168.1.10:2380 \
--discovery=https://discovery.etcd.io/3e86b59922e3e5f8e3e5f8e3e5f8e3e5 \
--initial-cluster-state=new
3.3.3 动态发现 vs 静态集群
| 对比维度 | 静态集群部署 | 动态发现部署 |
|---|---|---|
| 配置复杂度 | 低,所有信息在启动前已知 | 中,需要额外获取发现 |
| IP 变更适应性 | 差,IP 变化需重新配置 | 好,节点自动注册 |
| 扩缩容支持 | 需手动更新集群配置 | 自动感知新节点加入 |
| 生产推荐度 | 高,适合固定基础设施 | 中,适合容器化/云原生环境 |
| 依赖外部服务 | 无 | 需要发现服务可用 |
3.4 TLS 安全配置
生产环境中的 etcd 集群必须启用 TLS 加密,防止中间人攻击和数据泄露。TLS 配置分为三个层级:客户端与服务端之间的加密(Client-to-Server)、节点之间的加密(Peer-to-Peer)、以及双向认证(mTLS)。
3.4.1 证书生成
使用 cfssl 工具生成 CA 证书、服务端证书和客户端证书:
# 1. 生成 CA 证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
# 2. 生成服务端证书(包含所有节点 IP 和域名)
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-hostname="192.168.1.10,192.168.1.11,192.168.1.12,localhost,127.0.0.1" \
-profile=server server-csr.json | cfssljson -bare server
# 3. 生成客户端证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-profile=client client-csr.json | cfssljson -bare client
3.4.2 启用 TLS 的启动命令
etcd --name=node1 \
--data-dir=/var/lib/etcd \
--listen-client-urls=https://0.0.0.0:2379 \
--advertise-client-urls=https://192.168.1.10:2379 \
--listen-peer-urls=https://0.0.0.0:2380 \
--initial-advertise-peer-urls=https://192.168.1.10:2380 \
--initial-cluster=node1=https://192.168.1.10:2380,node2=https://192.168.1.11:2380,node3=https://192.168.1.12:2380 \
--initial-cluster-=etcd-cluster-tls \
--initial-cluster-state=new \
--cert-file=/etc/etcd/ssl/server.pem \
--key-file=/etc/etcd/ssl/server-key.pem \
--peer-cert-file=/etc/etcd/ssl/peer.pem \
--peer-key-file=/etc/etcd/ssl/peer-key.pem \
--trusted-ca-file=/etc/etcd/ssl/ca.pem \
--peer-trusted-ca-file=/etc/etcd/ssl/ca.pem \
--client-cert-auth=true \
--peer-client-cert-auth=true
3.4.3 使用 TLS 连接集群
# 使用 etcdctl 通过 TLS 访问
etcdctl --endpoints=https://192.168.1.10:2379 \
--cacert=/etc/etcd/ssl/ca.pem \
--cert=/etc/etcd/ssl/client.pem \
--key=/etc/etcd/ssl/client-key.pem \
endpoint health
安全建议: 生产环境务必启用
--client-cert-auth=true 和 --peer-client-cert-auth=true,实现双向 TLS 认证。证书有效期建议不超过 1 年,并建立证书轮换机制。
3.5 部署模式对比与选择
| 部署模式 | 高可用 | 配置复杂度 | 推荐场景 |
|---|---|---|---|
| 单节点 | 否 | 低 | 开发测试、压测脚本调试 |
| 静态集群 | 是 | 中 | 生产环境、固定基础设施 |
| 动态发现 | 是 | 中高 | 容器化、云原生、自动扩缩容 |
| TLS 安全集群 | 是 | 高 | 所有生产环境(必须) |
3.6 部署架构总览
下图展示了三种部署模式的架构差异,以及 TLS 安全层在集群中的位置:
3.7 压测脚本部署适配要点
在编写压测脚本时,需要根据集群部署方式调整客户端连接参数:
- 单节点: 直接连接
http://127.0.0.1:2379,无需额外配置。 - 静态集群: 在客户端配置多个 endpoint 列表,实现自动故障转移。
- 动态发现: 先通过发现服务获取当前集群成员列表,再建立连接。
- TLS 集群: 客户端必须加载 CA 证书、客户端证书和私钥,使用 HTTPS 协议。
压测脚本示例(Go 语言):
// 连接 TLS 集群的 etcd 客户端
import (
"go.etcd.io/etcd/client/v3"
"crypto/tls"
"crypto/x509"
"os"
)
func newTLSClient() (*clientv3.Client, error) {
caCert, _ := os.ReadFile("/etc/etcd/ssl/ca.pem")
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caCert)
cert, _ := tls.LoadX509KeyPair(
"/etc/etcd/ssl/client.pem",
"/etc/etcd/ssl/client-key.pem",
)
cfg := clientv3.Config{
Endpoints: []string{
"https://192.168.1.10:2379",
"https://192.168.1.11:2379",
"https://192.168.1.12:2379",
},
TLS: &tls.Config{
RootCAs: caPool,
Certificates: []tls.Certificate{cert},
},
}
return clientv3.New(cfg)
}
部署避坑指南:
- 集群节点数量建议为奇数(3、5、7),避免脑裂时无法达成共识。
- 生产环境禁止使用
--initial-cluster-state=new重启已有集群,应改为existing。 - TLS 证书中的 SAN(Subject Alternative Name)必须包含所有节点的 IP 和域名。
- 动态发现模式下,发现服务本身需要高可用,否则会成为单点故障。