etcd性能测试
本文参考官方文档完成etcd性能测试,提供etcd官方推荐的性能测试方案。
1. 理解性能:延迟与吞吐量
etcd 提供稳定、持续的高性能。有两个因素决定性能:延迟和吞吐量。延迟是完成一项操作所花费的时间。吞吐量是在某个时间段内完成的操作总数。通常情况下,当 etcd 接受并发的客户端请求时,随着整体吞吐量的增加,平均延迟也会上升。
etcd 使用 Raft 共识算法在成员之间复制请求并达成一致。共识性能,尤其是提交延迟,受到两个物理限制因素的制约:网络输入输出(IO)延迟和磁盘输入输出(IO)延迟。完成一个 etcd 请求的最短时间是成员之间的网络往返时间(RTT),再加上 fdatasync 将数据提交到永久存储所需的时间。数据中心内的往返时间可能长达几百微秒。在美国境内典型的往返时间约为 50 毫秒,而在各大洲之间可能慢至 400 毫秒。对于传统机械硬盘,典型的 fdatasync 延迟约为 10 毫秒。对于固态硬盘(SSD),延迟通常低于 1 毫秒。为了提高吞吐量,etcd 将多个请求成批处理,然后提交给 Raft 算法。这种批处理策略使得 etcd 即使在重负载情况下也能实现高吞吐量。
2. 基准测试
对 etcd 性能进行基准测试可以使用 etcd 自带的基准测试命令行工具来完成。
为了获取一些基准性能数据,我们考虑搭建一个由三个成员组成的 etcd 集群,其硬件配置如下:
- Kubernetes v1.28.2,使用kubeadm部署;
- 3 台机器,每台配置为 4 个虚拟 CPU + 8GB 内存 + 50GB 固态硬盘(虚拟,SCSI);机器使用VMware Workstation虚拟机。
- 1 台(客户端)机器,配置为 4个虚拟 CPU + 16GB 内存 + 50GB 固态硬盘(虚拟,SCSI);
- 操作系统为 Ubuntu 20.04;
- etcd 版本为 3.5.9,Go 语言版本为 1.22.2;
说明
benchmark是etcd项目自带的压测工具,项目源码:https://github.com/etcd-io/etcd/tree/master/tools/benchmark
2.1 测试环境准备
benchmark安装
工具包括在etcd源码中,克隆仓库进行安装。这里将benchmark安装到测试的客户端机器中:
git clone https://github.com/etcd-io/etcd.git
cd etcd
# 安装
$ go install -v ./tools/benchmark
安装会将可执行文件放在 $GOPATH/bin
中。如果未设置GOPATH
环境变量,则该工具将安装到 $HOME/go/bin
中。
etcd环境准备
这里使用k8s的etcd集群环境作为测试对象,etcd使用kubeadm自动部署。也可以自行部署etcd环境:
# etcd集群环境信息如下,此时leader节点为https://192.168.0.52:2379
root@master1:~# etcdctl endpoint status member list --cluster -w table
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.0.53:2379 | 94d50163269e24a | 3.5.9 | 47 MB | false | false | 140 | 2530830 | 2530830 | |
| https://192.168.0.51:2379 | 520e81a36ed5d9f0 | 3.5.9 | 46 MB | false | false | 140 | 2530830 | 2530830 | |
| https://192.168.0.52:2379 | 626555ae08021005 | 3.5.9 | 45 MB | true | false | 140 | 2530830 | 2530830 | |
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
# etcd测试key
root@master1:~# etcdctl put testkey testvalue
OK
root@master1:~# etcdctl get testkey
testkey
testvalue
测试客户端配置
由于etcd使用https进行访问,这里配置benchmark连接使用https,将k8s相应证书目录拷贝到客户端机器下:
root@master1:/etc# ls /etc/kubernetes/
admin.conf controller-manager.conf kubelet.conf manifests pki scheduler.conf tmp
root@master1:/etc# scp -r kubernetes/ root@192.168.0.54:`pwd`
The authenticity of host '192.168.0.54 (192.168.0.54)' can't be established.
ED25519 key fingerprint is SHA256:4KsD0Oi0sAARWMDCznPaRK+rcgOB5lN8XmBWt0rFCcc.
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:1: [hashed name]
~/.ssh/known_hosts:4: [hashed name]
~/.ssh/known_hosts:5: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.0.54' (ED25519) to the list of known hosts.
# benchmark工具带证书路径运行即可
root@worker1:~# benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 range YOUR_KEY --consistency=l --total=10000 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key
# 配置命令别名
root@worker1:~# alias benchmark="benchmark --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key"
# 设置host地址
root@worker1:~# HOST_3=https://192.168.0.53:2379
root@worker1:~# HOST_2=https://192.168.0.52:2379
root@worker1:~# HOST_1=https://192.168.0.51:2379
2.2 etcd读性能
线性化(Linearizable)读请求会经过集群成员中的法定人数成员以达成一致性,从而获取最新的数据。可序列化(Serializable)读请求比线性化读请求的开销更低,因为可序列化读请求由任何单个 etcd 成员来处理,而无需大部分节点的参与,其代价是可能会返回过期的数据。
使用的性能测试命令如下:
# 单连接线性化读请求
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 range testkey --consistency=l --total=10000
# 单连接可序列化读请求
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 range testkey --consistency=s --total=10000
# 多连接并发线性化读请求
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 range testkey --consistency=l --total=100000
# 多连接并发可序列化读请求
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 range testkey --consistency=s --total=100000
etcd读性能测试数据如下:
请求数量 | Key的大小(字节) | Value的大小(字节) | 连接数 | 客户端数量 | 一致性(Consistency) | 平均读QPS | 请求平均延迟 |
---|---|---|---|---|---|---|---|
10,000 | 8 | 256 | 1 | 1 | Linearizable | 218 | 4.6ms |
10,000 | 8 | 256 | 1 | 1 | Serializable | 573 | 1.7ms |
100,000 | 8 | 256 | 100 | 1000 | Linearizable | 2430 | 403ms |
100,000 | 8 | 256 | 100 | 1000 | Serializable | 4374 | 216ms |
2.3 etcd写性能
etcd写性能使用的性能测试命令如下:
# 向etcd集群的leader发起写请求,单连接。备注:${HOST_2}为集群的leader,见上面查询结果
benchmark --endpoints=${HOST_2} --target-leader --conns=1 --clients=1 put --key-size=8 --sequential-keys --total=10000 --val-size=256
# 向etcd集群的leader发起写请求,多连接
benchmark --endpoints=${HOST_2} --target-leader --conns=100 --clients=1000 put --key-size=8 --sequential-keys --total=100000 --val-size=256
# 向etcd集群所有成员发起写请求,多连接
benchmark --endpoints=${HOST_2},${HOST_2},${HOST_3} --conns=100 --clients=1000 put --key-size=8 --sequential-keys --total=100000 --val-size=256
在这种配置下,etcd 写入性能测试数据:
key的数量 | Key的大小(字节) | Value的大小(字节) | 连接数 | 客户端数量 | 请求的etcd目标服务器 | 平均写QPS | 请求平均延迟 | 内存消耗(Average server RSS) |
---|---|---|---|---|---|---|---|---|
10,000 | 8 | 256 | 1 | 1 | 只请求leader节点 | 254 | 3.9ms | xx MB |
100,000 | 8 | 256 | 100 | 1000 | 只请求leader节点 | 1695 | 306ms | xx MB |
100,000 | 8 | 256 | 100 | 1000 | 请求所有节点 | 1507 | 283ms | xx MB |
建议在新环境中首次设置 etcd 集群时运行基准测试,以确保该集群达到足够的性能;集群的延迟和吞吐量可能会因细微的环境差异而受到影响,例如将etcd和其他高I/O的应用程序部署到同一个节点上,将严重影响etcd的读写效率,甚至影响etcd集群的稳定性。
3. 参考资料
etcd performance:https://etcd.io/docs/v3.6/op-guide/performance/#benchmarks