在构建高可用的分布式系统时,Etcd 作为可靠的键值存储,其安全性至关重要。相比于集成在 Kubernetes 中由 kubeadm 自动管理证书的场景,当你需要独立部署 Etcd 集群用于其他服务(如服务发现、配置存储)时,手动管理证书是必须掌握的技能。本文将详细演示如何从零开始,使用 CFSSL 工具生成 TLS 证书,并部署一个带双向认证的三节点 Etcd 集群。
环境规划
部署前,需要规划好集群节点。以下是示例规划,你可以根据实际网络环境调整主机名和 IP 地址。
| 角色 |
IP |
| etcd-1 |
192.168.31.100 |
| etcd-2 |
192.168.31.101 |
| etcd-3 |
192.168.31.102 |
证书工具准备
我们将使用 Cloudflare 开源的 PKI/TLS 工具 cfssl 来生成证书。这个方法在早期 Kubernetes 手动部署中也被广泛使用,非常成熟可靠。
在任一节点(如 etcd-1)上执行以下命令安装工具并创建必要的目录:
# 安装cfssl(证书生成工具)
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64 -o /usr/local/bin/cfssl
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64 -o /usr/local/bin/cfssljson
chmod +x /usr/local/bin/cfssl /usr/local/bin/cfssljson
# 创建对应的数据目录,为了避免默认这里目录生成了一个新的
mkdir -p /etc/etcd/certs /var/lib/etcdnew
生成证书
一个完整的 TLS 认证体系至少包含 CA(证书颁发机构)根证书以及由它签发的服务端/客户端证书。验证时,需要使用 CA 证书的公钥来验证下级证书的合法性。
所有证书生成操作在 etcd-1 节点上进行:
mkdir -p /etc/etcd/certs && cd /etc/etcd/certs
# 1. 重新创建ca-config.json(此文件无地域信息,无需修改)
cat > ca-config.json << 'EOF'
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"etcd": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "87600h"
}
}
}
}
EOF
# 2. 重新创建ca-csr.json(ST和L字段替换为Chengdu)
cat > ca-csr.json << 'EOF'
{
"CN": "etcd-ca",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Chengdu",
"L": "Chengdu",
"O": "etcd",
"OU": "etcd-cluster"
}
]
}
EOF
# 3. 重新创建etcd-csr.json(纯IP版,ST和L字段替换为Chengdu)
cat > etcd-csr.json << 'EOF'
{
"CN": "etcd",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Chengdu",
"L": "Chengdu",
"O": "etcd",
"OU": "etcd-cluster"
}
],
“hosts”: [
“127.0.0.1”,
“192.168.31.100”,
“192.168.31.101”,
“192.168.31.102”
]
}
EOF
# 生成证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd etcd-csr.json | cfssljson -bare etcd -
chmod 600 /etc/etcd/certs/*-key.pem
# 同步证书到其他节点
scp /etc/etcd/certs/* root@192.168.31.101:/etc/etcd/certs/
scp /etc/etcd/certs/* root@192.168.31.102:/etc/etcd/certs/
执行完成后,使用 ls -l 命令检查证书目录,应看到如下文件:

为了方便理解,下表列出了生成的所有文件及其用途:
| 文件名 |
文件类型 |
核心用途 |
ca-config.json |
JSON 配置文件 |
CA 证书的签发规则配置(如有效期、证书用途),是生成证书的 “规则文件” |
ca-csr.json |
JSON 请求文件 |
CA 根证书的签名请求文件,定义 CA 证书的基本信息(如地域、密钥算法) |
ca.pem |
CA 根证书(公钥) |
集群的根证书,用于验证所有 etcd 节点证书的合法性(核心信任凭证) |
ca-key.pem |
CA 根私钥 |
生成 CA 证书的私钥,用于签发 etcd 节点证书,极其敏感,需严格保密 |
ca.csr |
CA 证书签名请求 |
生成 CA 证书过程中产生的临时文件,包含证书请求的原始信息,生成后可删除 |
etcd-csr.json |
JSON 请求文件 |
etcd 节点证书的签名请求文件,定义节点证书的 IP、地域等信息 |
etcd.pem |
etcd 节点证书 |
etcd 节点的服务端 / 客户端证书,用于节点间通信加密和客户端身份认证 |
etcd-key.pem |
etcd 节点私钥 |
与 etcd.pem 配对的私钥,用于解密通信数据,需严格保密 |
etcd.csr |
etcd 证书请求 |
生成 etcd 节点证书的临时文件,包含节点证书的请求信息,生成后可删除 |
配置并启动 Etcd 服务
接下来,在每个节点上创建启动脚本。脚本采用前台运行方式,方便调试。
节点 1(192.168.31.100)
cat > /usr/local/bin/start-etcd.sh << 'EOF'
#!/bin/bash
etcd \
--name=etcd-1 \
--data-dir=/var/lib/etcdnew \
--listen-client-urls=https://192.168.31.100:2379 \
--advertise-client-urls=https://192.168.31.100:2379 \
--listen-peer-urls=https://192.168.31.100:2380 \
--initial-advertise-peer-urls=https://192.168.31.100:2380 \
--initial-cluster=etcd-1=https://192.168.31.100:2380,etcd-2=https://192.168.31.101:2380,etcd-3=https://192.168.31.102:2380 \
--initial-cluster-token=etcd-cluster-token \
--initial-cluster-state=new \
--cert-file=/etc/etcd/certs/etcd.pem \
--key-file=/etc/etcd/certs/etcd-key.pem \
--peer-cert-file=/etc/etcd/certs/etcd.pem \
--peer-key-file=/etc/etcd/certs/etcd-key.pem \
--trusted-ca-file=/etc/etcd/certs/ca.pem \
--peer-trusted-ca-file=/etc/etcd/certs/ca.pem \
--client-cert-auth \
--peer-client-cert-auth
EOF
# 添加执行权限
chmod +x /usr/local/bin/start-etcd.sh
节点 2(192.168.31.101)
cat > /usr/local/bin/start-etcd.sh << 'EOF'
#!/bin/bash
etcd \
--name=etcd-2 \
--data-dir=/var/lib/etcdnew \
--listen-client-urls=https://192.168.31.101:2379 \
--advertise-client-urls=https://192.168.31.101:2379 \
--listen-peer-urls=https://192.168.31.101:2380 \
--initial-advertise-peer-urls=https://192.168.31.101:2380 \
--initial-cluster=etcd-1=https://192.168.31.100:2380,etcd-2=https://192.168.31.101:2380,etcd-3=https://192.168.31.102:2380 \
--initial-cluster-token=etcd-cluster-token \
--initial-cluster-state=new \
--cert-file=/etc/etcd/certs/etcd.pem \
--key-file=/etc/etcd/certs/etcd-key.pem \
--peer-cert-file=/etc/etcd/certs/etcd.pem \
--peer-key-file=/etc/etcd/certs/etcd-key.pem \
--trusted-ca-file=/etc/etcd/certs/ca.pem \
--peer-trusted-ca-file=/etc/etcd/certs/ca.pem \
--client-cert-auth \
--peer-client-cert-auth
EOF
# 添加执行权限
chmod +x /usr/local/bin/start-etcd.sh
节点 3(192.168.31.102)
cat > /usr/local/bin/start-etcd.sh << 'EOF'
#!/bin/bash
etcd \
--name=etcd-3 \
--data-dir=/var/lib/etcdnew \
--listen-client-urls=https://192.168.31.102:2379 \
--advertise-client-urls=https://192.168.31.102:2379 \
--listen-peer-urls=https://192.168.31.102:2380 \
--initial-advertise-peer-urls=https://192.168.31.102:2380 \
--initial-cluster=etcd-1=https://192.168.31.100:2380,etcd-2=https://192.168.31.101:2380,etcd-3=https://192.168.31.102:2380 \
--initial-cluster-token=etcd-cluster-token \
--initial-cluster-state=new \
--cert-file=/etc/etcd/certs/etcd.pem \
--key-file=/etc/etcd/certs/etcd-key.pem \
--peer-cert-file=/etc/etcd/certs/etcd.pem \
--peer-key-file=/etc/etcd/certs/etcd-key.pem \
--trusted-ca-file=/etc/etcd/certs/ca.pem \
--peer-trusted-ca-file=/etc/etcd/certs/ca.pem \
--client-cert-auth \
--peer-client-cert-auth
EOF
# 添加执行权限
chmod +x /usr/local/bin/start-etcd.sh
依次在三个节点上执行 start-etcd.sh 脚本启动服务。观察前台日志,确认所有节点都启动成功且无报错。
验证集群
集群启动后,需要进行健康检查和基本读写测试。必须提前设置好证书环境变量,否则 etcdctl 无法通过 TLS 认证连接到集群。
在任意已安装 etcdctl 的节点上执行:
# 设置环境变量
export ETCDCTL_API=3
export ETCDCTL_CACERT=/etc/etcd/certs/ca.pem
export ETCDCTL_CERT=/etc/etcd/certs/etcd.pem
export ETCDCTL_KEY=/etc/etcd/certs/etcd-key.pem
# 检查集群健康状态(仅用IP)
etcdctl --endpoints=https://192.168.31.100:2379,https://192.168.31.101:2379,https://192.168.31.102:2379 endpoint health
# 测试数据读写
etcdctl --endpoints=https://192.168.31.100:2379 put testkey “hello etcd (only IP)”
etcdctl --endpoints=https://192.168.31.100:2379 get testkey

如果看到所有端点均为 healthy,并且能正常写入和读取数据,说明基于 TLS 的 集群部署 成功。
我们也可以使用 curl 命令通过 HTTPS 接口访问 Etcd,同样需要携带证书:
[root@localhost ~]# curl -v \
> --cacert /etc/etcd/certs/ca.pem \
> --cert /etc/etcd/certs/etcd.pem \
> --key /etc/etcd/certs/etcd-key.pem \
> https://192.168.31.102:2379/version
* About to connect() to 192.168.31.102 port 2379 (#0)
* Trying 192.168.31.102...
* Connected to 192.168.31.102 (192.168.31.102) port 2379 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/etcd/certs/ca.pem
CApath: none
* NSS: client certificate from file
* subject: CN=etcd,OU=etcd-cluster,O=etcd,L=Chengdu,ST=Chengdu,C=CN
* start date: Feb 08 15:01:00 2026 GMT
* expire date: Feb 06 15:01:00 2036 GMT
* common name: etcd
* issuer: CN=etcd-ca,OU=etcd-cluster,O=etcd,L=Chengdu,ST=Chengdu,C=CN
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
* subject: CN=etcd,OU=etcd-cluster,O=etcd,L=Chengdu,ST=Chengdu,C=CN
* start date: Feb 08 15:01:00 2026 GMT
* expire date: Feb 06 15:01:00 2036 GMT
* common name: etcd
* issuer: CN=etcd-ca,OU=etcd-cluster,O=etcd,L=Chengdu,ST=Chengdu,C=CN
> GET /version HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.31.102:2379
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Sun, 08 Feb 2026 15:25:15 GMT
< Content-Length: 45
<
* Connection #0 to host 192.168.31.102 left intact
从输出中可以看到,服务器返回的证书信息正是我们前面所生成的。至此,一个带 TLS 双向认证的 Etcd 集群就部署完成了。任何客户端(包括 etcdctl、应用程序)在连接此集群时,都必须提供有效的证书才能通过认证,这为你的 分布式系统 提供了坚实的安全基础。
对于更复杂的运维场景,例如监控、自动化部署等,可以深入探索相关的工具链与实践。如果你想查看更多关于 数据库、中间件 部署与优化的实战内容,欢迎访问 云栈社区 的 运维 & 测试 板块进行交流学习。