etcd教程(十六)---如何搭建生产可用高可用集群
本文主要记录了如何部署生产可用的高可用 etcd 集群。
按照本文操作,你可以获得一个由 systemd 管理的,开启 tls 认证的生产可用的 3 节点 etcd 集群。
生产环境推荐为 etcd 生成证书,以 https 方式对外暴露服务以提升安全性。
之前实验时使用 docker 部署的,具体见:etcd教程(一)—通过docker安装etcd集群,但是在生产环境肯定不能这样简单粗暴,一般是使用 systemd 来管理,而且对硬件也有一定要求。
都提供了相应脚本,只需要替换部分变量即可实现快速部署。
0. 环境准备
准备 3 个节点来部署 etcd 集群。
一般来说推荐使用 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 的百分位数据
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 来说就比较慢。
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 页面 查看最新版本,然后替换一下变量的值即可。
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
命令执行完成,正常应该会打印出版本号
[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 页面
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
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 以及一个 域名。
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 证书
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 证书是一样的,只是用不同的名字,区分一下。
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
目录,具体如下:
[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
包括以下几类证书:
- CA
- Etcd-server
- Etcd-peer
没有单独生成 client 证书,可以直接把 peer 证书作为 client 证书使用。
重签证书
如果要增加新节点,需要重新为所有节点签发证书,和部署时制作证书的流程是一样的。
也就是 req-csr.json 中的 hosts 增加新节点 IP 重新生成。
由于签署的证书只用于数据传输,不用于数据加密,所以即使更换 CA,重新签署证书,也是没有问题的。但是,如果我们首次启动etcd集群的时候使用的是非加密方式,后面改成SSL方式启动etcd集群的时候就会报错。
将证书拷贝到 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 修改为真实值
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:生成节点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 文件,这个命令由于使用环境变量进行替换,因此各个节点都一样:
# 证书相关变量
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,同样的以环境变量方式引用,因此节点都一样:
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
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
systemctl status etcd
4. 验证
任意一个节点执行即可,组装 endpoints
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 列表
etcdctl --endpoints=$ENDPOINTS --cacert=$CA --cert=$ETCD_CERT --key=$ETCD_KEY member list -w table
查看 endpoint 的状态(用于查看那个是 leader)
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 测试
# 写入数据并查看
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
批量操作
# 循环写入
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 服务
systemctl stop etcd
systemctl disable etcd
systemctl daemon-reload
rm -rf /usr/lib/systemd/system/etcd.service
然后移除对应数据目录
rm -rf "$DATA_DIR"
最后移除配置文件目录
rm -rf /etc/etcd
6. 小结
本文主要分享了如何使用 systemd 部署 etcd 集群。对于生产环境 etcd 集群的性能、可用性、安全性等方面都有较高的要求。
- 1)准备环境,使用官方推荐的硬件,特别是磁盘一定要用 SSD
- 2)配置 TLS,提升安全性
- 3)多节点集群提升可用性,3节点环境下故障一个节点也不影响集群使用