序言
对软件系统而言,性能测试通常包括两部分,一是制造合适的负载,二是在负载条件下进行合理的观测。
定义观测对象
任何系统都是有性能边界的,保证性能需要成本、人力、时间,不可能无休止地亦或是全方位地要求性能指标。
开发软件系统的目的是为了更好的服务用户,用户对系统有品质要求。影响到用户品质要求的性能指标是主要的、核心的指标,其它的是次要的指标(比如程序员拍脑袋想的指标)。因此,在进行性能测试之前,定义哪些指标会影响到用户品质要求是必须的工作,哪怕定义的指标不全面。盲目进行性能测试得到的通常是毫无意义的结果。
通常云厂商使用SLA(服务等级协议)来概括用户对系统的品质要求。遗憾的是,由于Kubernetes的复杂性,截止至2019.5.18,各大云厂商都没有提供可以最终公布的SLA。
Kubernetes社区提供了一些SLI(服务等级指标)和SLO(服务等级目标)指导系统性能测试、分析,详见跳转地址。这些指标间接地描述了Kubernetes系统服务品质,是性能测试重点关注的指标。
除此之外,Master机器的CPU、Memory等指标,以及ETCD的ioutil等指标也会对Kubernetes服务造成明显影响,因此同样应当纳入性能测试的观测范围。
制造集群负载
- 便宜的负载
社区资料表明Kubernetes可以支持5000台Node、15万Pod的集群良好运行。这意味着,验证这一说法至少应该制造5000台Node、15万Pod的负载。然而获得5000台真实机器的使用权很贵,长期占用这么多机器用于Kubernetes的开发测试是不科学的。因此,社区开发了kubemark
和Virtual Kubelet
等工具用于模拟真实Node,使用少量机器也可以制造出足够大的负载。在小米的测试过程中,20台真实机器可以模拟3000台Node、10万个Pod的负载情况。 - 多样的负载
在实际运行环境中,真实负载情况是多种多样的,不同的负载可能对系统的性能指标有不同的影响,在测试过程中模拟多种负载情况是必要的。为了方便模拟多样的负载情况,性能测试提供负载的程序可配置性应当尽可能强。在早期的Kubernetes代码中,性能测试程序的各项参数写死在代码中,要修改Kubernetes源码重新编译后,拷贝到测试环境,才能修改一次测试参数。测试过程十分痛苦。
因此社区开发了perf-test/clusterloader2,可配置性极强,并且配备了相应的性能指标观测代码,十分推荐。
工具使用说明
Kubernetes性能测试的前置要求:
- 准备运行良好的Kubernetes集群
本文接下来介绍性能测试工具kubemark和perf-test/clusterloader2。
kubemark
kubemark是阉割版的kubelet,除了不调用CRI接口之外(即不调用Docker,直接返回),其它行为和kubelet基本一致。
编译
- 编译二进制
下载kubernetes源码,执行make WHAT='cmd/kubemark'
- 编译镜像
编译docker镜像的脚本在test/kubemark/start-kubemark.sh中,因为要涉及到一些代码改造,在此不作说明。
使用步骤
- 标记真实Node
1.设置Taint
假设Node名字是mytestnode
,执行以下命令设置Taint。kubectl taint nodes mytestnode role=real:NoSchedule
设置Taint的目的在于,避免压力测试的Pod调度至真实Node上。
2.设置Label
假设Node名字是mytestnode
,执行以下命令设置Label。kubectl label nodes mytestnode role=real
- 配置kubeconfig
apiVersion: v1
kind: Config
users:
- name: kubelet
user: {}
clusters:
- name: kubemark
cluster:
server: http://10.114.25.172:8083 #替换成你自己的APIServer地址
contexts:
- context:
cluster: kubemark
user: kubelet
name: kubemark-context
current-context: kubemark-context
假如上述kubeconfig保存在/home/mi/.kube/config
,执行以下命令创建secret
kubectl create secret generic kubeconfig --from-file=kubelet.kubeconfig=/home/mi/.kube/config
- 创建kubemark Pod
执行以下脚本运行kubemark:
curl -L https://gist.githubusercontent.com/Betula-L/fef068ef7e914aa4a52113ac81fc6517/raw/77abf3f9b234274e33435597dec079ef46300324/kubemark.yaml | kubectl apply -f -
注意事项
- kubemark版本尽量和master版本一致
- 上述说明只适用没有开启认证授权的master
clusterloader2
运行要求
- master节点开放ssh服务
- 测试启动时刻所有Node处于Ready状态(包括hollow node)
编译
下载pert-tests项目,运行${perf-tests}/clusterloader2/run-e2e.sh
或者如下脚本编译二进制文件。
CLUSTERLOADER_ROOT=${perf-tests}
cd ${CLUSTERLOADER_ROOT}/ && go build -o clusterloader './cmd/'
使用步骤
- 设置环境变量
# kube config for kubernetes api
KUBE_CONFIG=${HOME}/.kube/config
# Provider setting
# Supported provider for xiaomi: local, kubemark, lvm-local, lvm-kubemark
PROVIDER='kubemark'
# SSH config for metrics' collection
KUBE_SSH_KEY_PATH=$HOME/.ssh/id_rsa
MASTER_SSH_IP=10.142.43.51
MASTER_SSH_USER_NAME=root
# Clusterloader2 testing strategy config paths
# It supports setting up multiple test strategy. Each testing strategy is individual and serial.
TEST_CONFIG='configs/examples/density/config.yaml'
# Clusterloader2 testing override config paths
# It supports setting up multiple override config files. All of override config files will be applied to each testing strategy.
# OVERRIDE_CONFIG='testing/density/override/200-nodes.yaml'
# Log config
REPORT_DIR='./reports'
LOG_FILE='logs/tmp.log'
填充上述脚本内容,并使用source
命令设置好Linux环境变量
- 运行clusterloader2
$CLUSTERLOADER_BIN --kubeconfig=$KUBE_CONFIG \
--provider=$PROVIDER \
--masterip=$MASTER_SSH_IP --mastername=$MASTER_SSH_USER_NAME \
--testconfig=$TEST_CONFIG \
--report-dir=$REPORT_DIR \
--alsologtostderr 2>&1 | tee $LOG_FILE
# --testoverrides="${OVERRIDE_CONFIG:-}" \
按上述命令运行clusterloader2,其中CLUSTERLOADER_BIN
是编译好的clusterloader2二进制文件位置,其它参数是设置好的环境变量。
测试说明
pert-tests只是性能测试框架,具体的性能测试策略需要用户自己通过配置文件定义。
以kubernetes density测试为例,解释clusterloader2的测试过程及其结果。
density测试策略
- 启动若干个观测程序
在测试策略配置文件中,所有的Measurement都是用于采集数据的观测程序。perf-tests提供了十几个Measurement,density测试使用了其中5个,包括APIResponsiveness
、SaturationPodStartupLatency
、WaitForRunningSaturationRCs
、SchedulingThroughput
、PodStartupLatency
。 - 系统调度吞吐测试
kubernetes认为每个Node运行30个Pod是机器正常负载情况。一次性发布#node*30
个Pod用于测试集群的调度吞吐性能,可以说明“系统发生大规模故障后,从零到正常负载所需恢复时间”,调度吞吐量定义如下:
调度吞吐量=一段较长时间内最大可以发布的Pod数量/总发布时间
值得注意的是,clusterloader2不是简单地使用一个rc创建#node*30
个Pod,因为kubernetes对系统的使用有一些基本假设,包括“每个namespace的Pod数量不超过3000”,“每个node的Pod数量不能超过110”等。为了满足这些基本假设,clusterloader2在创建rc时,做了不少的特殊处理。 - Pod启动e2e耗时测试
为了说明Pod启动e2e耗时,在不删除“调度吞吐测试”负载的基础上,进行latency测试。
latency测试策略是每隔0.2秒创建一个pod,根据pod创建过程中产生的timestamp和event,计算出Pod启动过程中,每个阶段的耗时和总e2e耗时。如果调度吞吐量<5pods/s
,则创建Pod的时间间隔应当设置大于0.2s。
目前latency测试存在精度问题,因为kubernetes event存储的时间是RFC3339,导致部分阶段的耗时精度只到秒,对性能分析价值不大。density测试策略
测试结果
- stdout/stderr
整个性能测试过程以及部分测试结果会打印到stdout中,如果按本文所述方法运行clusterloader2,打印结果会保存一份至${LOG_FILE}
。
调度吞吐量结果:
Jan 9 13:55:47.876: INFO: E2E startup time for 10500 pods: 28m40.333662404s
Jan 9 13:55:47.876: INFO: Throughput (pods/s) during cluster saturation phase: 6.1034675
^[[1mSTEP^[[0m: Printing Pod to Node allocation data
Jan 9 13:55:47.884: INFO: Density Pods: 10500 out of 10500 created, 10500 running, 0 pending, 0 waiting, 0 inactive, 0 terminating, 0 unknown, 0 runningButNotReady
Pod启动e2e耗时结果:
Jan 9 14:09:31.598: INFO: perc50: 1614, perc90: 2241, perc99: 2541
Jan 9 14:09:31.598: INFO: Approx throughput: 7486.052881923156 pods/min
- report文件夹
详细的性能测试结果clusterloader2收集在report文件夹下,如果按本文方法运行,则结果存放在{$REPORT_DIR}
中。因为涉及到的性能测试结果非常多,本文不在此一一列举。目前Kubernetes性能分析最主要的性能指标如下:- APIServer Restful API响应时间 - APIResponsiveness
- Pod 启动总耗时 - PodStartupLatency
- Scheduler调度性能 - SchedulingThroughput, SchedulingMetrics
- ETCD指标 - EtcdMetrics
来源:CSDN
作者:qingdao666666
链接:https://blog.csdn.net/qingdao666666/article/details/104625457