本文主要记录了如何部署生产可用的高可用 etcd 集群。
按照本文操作,你可以获得一个由 systemd 管理的,开启 tls 认证的生产可用的 3 节点 etcd 集群。
生产环境推荐为 etcd 生成证书,以 https 方式对外暴露服务以提升安全性。
之前实验时使用 docker 部署的,具体见:etcd教程(一)—通过docker安装etcd集群 ,但是在生产环境肯定不能这样简单粗暴,一般是使用 systemd 来管理,而且对硬件也有一定要求。
都提供了相应脚本,只需要替换部分变量即可实现快速部署。
0. 环境准备
准备 3 个节点来部署 etcd 集群。
官方文档:Hardware recommendations
一般来说推荐使用 4C8G 50G SSD + 1000Mbps 网卡
,这个配置就够用了。
CPU
etcd 不怎么需要 cpu,因此一般的集群 2 或者 4核 cpu 的节点即可。
对于 每秒服务数千个客户端或数万个请求的大型 etcd 集群,则建议 8 或者 16核。
内存
Etcd 同样不太需要内存,etcd 内存消耗可以分为两部分:
Blotdb 数据缓存(treeindex),这部分一般不会超过 2 G watcher,客户端调用 watch 方法后,需要在内存中存储大量数据,这部分会比较消耗内存 还有就是 etcd 启动时会把 blotdb 中的所有数据加载到内存中,因此启动时对内存的消耗会比较大。
一般 8G 即可,对于具有数千个观察者和数百万个密钥的集群,则建议分配 16GB 到 64GB 内存。
磁盘
磁盘速度是 etcd 部署性能和稳定性的最关键因素。
不过 etcd 作为元数据存储,对磁盘容量没有太大要求,社区一般推荐 etcd 数据库大小到 8G,那么磁盘分配 50G 就绰绰有余了。
主要是对性能(延迟)有要求,建议使用 FIO 工具进行测试,测试命令如下:
需要 fio 3.5 以上版本,否则报告中没有 fdatasync 的百分位数据
1
2
mkdir test-data
fio --rw= write --ioengine= sync --fdatasync= 1 --directory= test-data --size= 22m --bs= 2300 --name= mytest
fdatasync p99 超过 10,000 us (10 milliseconds) 的话就说明磁盘性能对 etcd 来说不够用,小于这个值则说明磁盘足够快。
在测试期间,整个 I/O 负载来自 fio。在真实的场景中,除了与 wal_fsync_duration_seconds 相关的请求之外,很可能还会有其他写入请求。额外的负载将增加 wal_fsync_duration_seconds 的值。因此,如果第 99 百分位数几乎达到 10 毫秒,则说明您的磁盘将不够快。
比如下面是一个 fio 测试报告,可以看到 p99 为 23200(23ms),远大于阈值,说明这个磁盘对 etcd 来说就比较慢。
1
2
3
4
5
6
7
8
fsync/fdatasync/sync_file_range:
sync ( usec) : min = 857, max = 358913, avg = 2528.48, stdev = 5491.03
sync percentiles ( usec) :
| 1.00th=[ 1106] , 5.00th=[ 1237] , 10.00th=[ 1319] , 20.00th=[ 1418] ,
| 30.00th=[ 1500] , 40.00th=[ 1565] , 50.00th=[ 1647] , 60.00th=[ 1745] ,
| 70.00th=[ 1860] , 80.00th=[ 2057] , 90.00th=[ 2999] , 95.00th=[ 4555] ,
| 99.00th=[ 23200] , 99.50th=[ 24511] , 99.90th=[ 32375] , 99.95th=[ 40633] ,
| 99.99th=[ 181404]
对于 SSD 这个值一般在 4ms 以内。
Etcd 对磁盘带宽需求不大,因为 etcd 作为一个元数据的存储服务,db 大小最大一般也就 8G,不会有太大的读写量,但是重启或故障恢复的时候磁盘带宽越大,恢复速度会越快。一般来说 100 MB/s 就够用了。
注意:SSD 很重要,其他配置低一点都可以,SSD 真不行
网络
网络的低延迟可以保证 etcd 节点之间能高速通信。
高网络带宽则可以减少节点恢复时间(例如新加入节点会从其他节点同步所有数据)
一般网络带宽在 1GbE 即可,至于延迟的话内网部署可以忽略。
如果为了高可用做跨数据中心部署,则会因为网络延迟降低性能,这个即时性能和可用性之间的取舍了。
1. 下载 etcd
先将 etcd 相关二进制文件下载到各个节点上
可以先到 release 页面 查看最新版本,然后替换一下变量的值即可。
1
2
3
4
5
version = v3.5.9
wget https://github.com/etcd-io/etcd/releases/download/" $version " /etcd-" $version " -linux-amd64.tar.gz
tar -zxvf etcd-" $version " -linux-amd64.tar.gz
mv etcd-" $version " -linux-amd64/etcd* /usr/local/bin/
etcd --version
命令执行完成,正常应该会打印出版本号
1
2
3
4
5
[ root@etcd-1 ~] # etcd --version
etcd Version: 3.5.9
Git SHA: bdbbde998
Go Version: go1.19.9
Go OS/Arch: linux/amd64
2. 生成证书
这里使用官方推荐的 cfssl 工具来生成证书。
安装 cfssl
下载 cfssl 工具用于生成证书
其他版本见 release 页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
version = 1.6.4
wget https://github.com/cloudflare/cfssl/releases/download/v" $version " /cfssl_" $version " _linux_amd64 -O cfssl
wget https://github.com/cloudflare/cfssl/releases/download/v" $version " /cfssljson_" $version " _linux_amd64 -O cfssljson
wget https://github.com/cloudflare/cfssl/releases/download/v" $version " /cfssl-certinfo_" $version " _linux_amd64 -O cfssl-certinfo
chmod +x cfssl cfssljson cfssl-certinfo
mv cfssl* /usr/local/bin
cfssl version
cfssljson -version
制作证书
CA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
mkdir -p tls-setup/config
cd tls-setup
cat > config/ca-csr.json << EOF
{
"CN": "Autogenerated CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"O": "Honest Achmed's Used Certificates",
"OU": "Hastily-Generated Values Divison",
"L": "San Francisco",
"ST": "California",
"C": "US"
}
]
}
EOF
cat > config/ca-config.json << EOF
{
"signing": {
"default": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "876000h"
}
}
}
EOF
mkdir -p certs
cfssl gencert -initca config/ca-csr.json | cfssljson -bare certs/ca
自签证书
使用刚才生成的 CA 签名 etcd-server、etcd-peer 证书。
首先需要准备一个 csr 文件,用于生成证书。
注意📢 :
hosts 列表始终需要指定 127.0.0.1
和 localhost
hosts 部分需要指定所有节点 IP,如果有 vip 也需要签名进来。 修改后大概长这样:
除了两个默认 host 外,还添加了 3 个节点的 ip 以及一个 域名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cat > config/req-csr.json << EOF
{
"CN": "etcd",
"hosts": [
"localhost",
"127.0.0.1",
"192.168.10.13",
"192.168.10.52",
"192.168.10.75",
"192.168.10.27",
"etcd.demo.com"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"O": "autogenerated",
"OU": "etcd cluster",
"L": "the internet"
}
]
}
EOF
生成 etcd-server 证书
1
2
3
4
5
cfssl gencert \
-ca certs/ca.pem \
-ca-key certs/ca-key.pem \
-config config/ca-config.json \
config/req-csr.json | cfssljson -bare certs/etcd
生成 etcd-peer 证书
和 etcd-server 证书是一样的,只是用不同的名字,区分一下。
1
2
3
4
5
cfssl gencert \
-ca certs/ca.pem \
-ca-key certs/ca-key.pem \
-config config/ca-config.json \
config/req-csr.json | cfssljson -bare certs/peer
生成的证书保证在 certs
目录,具体如下:
1
2
[ root@etcd-1 tls-setup] # ls certs
ca.csr ca-key.pem ca.pem etcd.csr etcd-key.pem etcd.pem peer.csr peer-key.pem peer.pem
包括以下几类证书:
没有单独生成 client 证书,可以直接把 peer 证书作为 client 证书使用。
重签证书
如果要增加新节点,需要重新为所有节点 签发证书,和部署时制作证书的流程是一样的。
也就是 req-csr.json 中的 hosts 增加新节点 IP 重新生成。
由于签署的证书只用于数据传输,不用于数据加密,所以即使更换 CA ,重新签署证书,也是没有问题的。但是,如果我们首次启动etcd集群的时候使用的是非加密方式,后面改成SSL方式启动etcd集群的时候就会报错。
将证书拷贝到 3 个部署节点。
1
2
3
scp -r certs root@$HOST_1 :/etc/etcd/
scp -r certs root@$HOST_2 :/etc/etcd/
scp -r certs root@$HOST_3 :/etc/etcd/
至此,证书准备好了,接下来开始部署。
3. 部署 etcd
和不带 tls 的命令有两个区别:
1)url 中的 http 改为 https 2)增加 tls 相关参数 创建 env 文件
所有节点执行, apply 一些通用变量
IP 修改为真实值
1
2
3
4
5
6
7
8
9
10
11
12
TOKEN = my-etcd
CLUSTER_STATE = new
HOST_1 = 10.0.0.124
HOST_2 = 10.0.0.229
HOST_3 = 10.0.0.31
NAME_1 = etcd1
NAME_2 = etcd2
NAME_3 = etcd3
CLUSTER = " $NAME_1 " = https://" $HOST_1 " :2380," $NAME_2 " = https://" $HOST_2 " :2380," $NAME_3 " = https://" $HOST_3 " :2380
DATA_DIR = /data/etcd
mkdir -p " $DATA_DIR "
到每个节点执行对应 命令,不同节点有区别的变量,需要分别到 apply 到对应节点
1
2
3
4
5
6
7
8
9
10
11
# 节点 1:生成节点1 的env 文件
THIS_NAME = " $NAME_1 "
THIS_IP = " $HOST_1 "
# 节点 2
THIS_NAME = " $NAME_2 "
THIS_IP = " $HOST_2 "
# 节点 3
THIS_NAME = " $NAME_3 "
THIS_IP = " $HOST_3 "
到所有节点执行以生成 env 文件,这个命令由于使用环境变量进行替换,因此各个节点都一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 证书相关变量
CERTS = /etc/etcd/certs
CA = " $CERTS " /ca.pem
ETCD_CERT = " $CERTS " /etcd.pem
ETCD_KEY = " $CERTS " /etcd-key.pem
ETCD_PEER_CERT = " $CERTS " /peer.pem
ETCD_PEER_KEY = " $CERTS " /peer-key.pem
mkdir -p /etc/etcd
cat > /etc/etcd/conf << EOF
TOKEN=${TOKEN}
CLUSTER_STATE=${CLUSTER_STATE}
DATA_DIR=${DATA_DIR}
CLUSTER=${CLUSTER}
THIS_NAME=${THIS_NAME}
THIS_IP=${THIS_IP}
# tls
CA=${CA}
ETCD_CERT=${ETCD_CERT}
ETCD_KEY=${ETCD_KEY}
ETCD_PEER_CERT=${ETCD_PEER_CERT}
ETCD_PEER_KEY=${ETCD_PEER_KEY}
EOF
cat /etc/etcd/conf
systemd.service
到所有节点执行,然后把启动命令封装成 systemd.service,同样的以环境变量方式引用,因此节点都一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cat > /usr/lib/systemd/system/etcd.service << 'EOF'
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/etcd/conf
ExecStart=/usr/local/bin/etcd \
--name ${THIS_NAME} \
--data-dir ${DATA_DIR} \
--listen-client-urls https://${THIS_IP}:2379 \
--advertise-client-urls https://${THIS_IP}:2379 \
--listen-peer-urls https://${THIS_IP}:2380 \
--initial-advertise-peer-urls https://${THIS_IP}:2380 \
--initial-cluster ${CLUSTER} \
--initial-cluster-token ${TOKEN} \
--initial-cluster-state ${CLUSTER_STATE} \
--cert-file=${ETCD_CERT} \
--key-file=${ETCD_KEY} \
--peer-cert-file=${ETCD_PEER_CERT} \
--peer-key-file=${ETCD_PEER_KEY} \
--trusted-ca-file=${CA} \
--peer-trusted-ca-file=${CA}
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
cat /usr/lib/systemd/system/etcd.service
启动
到所有节点执行,启动 etcd
1
2
3
4
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
systemctl status etcd
4. 验证
任意一个节点执行即可,组装 endpoints
1
2
3
4
5
6
ENDPOINT_1 = https://" $HOST_1 " :2379
ENDPOINT_2 = https://" $HOST_2 " :2379
ENDPOINT_3 = https://" $HOST_3 " :2379
ENDPOINTS = $ENDPOINT_1 ,$ENDPOINT_2 ,$ENDPOINT_3
ETCDCTL_API = 3
开启 tls 之后,etcdctl 也必须指定证书才能访问。
查看集群 member 列表
1
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY member list -w table
查看 endpoint 的状态(用于查看那个是 leader)
1
2
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY endpoint status -w table
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY endpoint status -w json
crud 测试
1
2
3
4
5
6
7
8
9
10
11
# 写入数据并查看
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY put key1 value1
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY get key1
# 修改后再次查看
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY put key1 value2
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY get key1
# 删除数据
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY del key1
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY get key1
批量操作
1
2
3
4
5
6
# 循环写入
for i in { 1..1000} ; do
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY put key" $i " value" $i "
done
# 批量删除
etcdctl --endpoints= $ENDPOINTS --cacert= $CA --cert= $ETCD_CERT --key= $ETCD_KEY del "key" --prefix
5. 清理
停止掉各个节点上的 etcd 服务
1
2
3
4
systemctl stop etcd
systemctl disable etcd
systemctl daemon-reload
rm -rf /usr/lib/systemd/system/etcd.service
然后移除对应数据目录
最后移除配置文件目录
6. 小结
本文主要分享了如何使用 systemd 部署 etcd 集群。对于生产环境 etcd 集群的性能、可用性、安全性等方面都有较高的要求。
1)准备环境,使用官方推荐的硬件,特别是磁盘一定要用 SSD 2)配置 TLS,提升安全性 3)多节点集群提升可用性,3节点环境下故障一个节点也不影响集群使用