Kubernetes测试系列 - 性能测试

南楼画角 提交于 2020-03-03 10:20:11

序言

对软件系统而言,性能测试通常包括两部分,一是制造合适的负载,二是在负载条件下进行合理的观测。

定义观测对象

任何系统都是有性能边界的,保证性能需要成本、人力、时间,不可能无休止地亦或是全方位地要求性能指标。
开发软件系统的目的是为了更好的服务用户,用户对系统有品质要求。影响到用户品质要求的性能指标是主要的、核心的指标,其它的是次要的指标(比如程序员拍脑袋想的指标)。因此,在进行性能测试之前,定义哪些指标会影响到用户品质要求是必须的工作,哪怕定义的指标不全面。盲目进行性能测试得到的通常是毫无意义的结果。

通常云厂商使用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的开发测试是不科学的。因此,社区开发了kubemarkVirtual Kubelet等工具用于模拟真实Node,使用少量机器也可以制造出足够大的负载。在小米的测试过程中,20台真实机器可以模拟3000台Node、10万个Pod的负载情况。
  • 多样的负载
    在实际运行环境中,真实负载情况是多种多样的,不同的负载可能对系统的性能指标有不同的影响,在测试过程中模拟多种负载情况是必要的。为了方便模拟多样的负载情况,性能测试提供负载的程序可配置性应当尽可能强。在早期的Kubernetes代码中,性能测试程序的各项参数写死在代码中,要修改Kubernetes源码重新编译后,拷贝到测试环境,才能修改一次测试参数。测试过程十分痛苦。
    因此社区开发了perf-test/clusterloader2,可配置性极强,并且配备了相应的性能指标观测代码,十分推荐。

工具使用说明

Kubernetes性能测试的前置要求:

  • 准备运行良好的Kubernetes集群

本文接下来介绍性能测试工具kubemarkperf-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测试策略

  1. 启动若干个观测程序
    测试策略配置文件中,所有的Measurement都是用于采集数据的观测程序。perf-tests提供了十几个Measurement,density测试使用了其中5个,包括APIResponsivenessSaturationPodStartupLatencyWaitForRunningSaturationRCsSchedulingThroughputPodStartupLatency
  2. 系统调度吞吐测试
    kubernetes认为每个Node运行30个Pod是机器正常负载情况。一次性发布#node*30个Pod用于测试集群的调度吞吐性能,可以说明“系统发生大规模故障后,从零到正常负载所需恢复时间”,调度吞吐量定义如下:
              调度吞吐量=一段较长时间内最大可以发布的Pod数量/总发布时间
    值得注意的是,clusterloader2不是简单地使用一个rc创建#node*30个Pod,因为kubernetes对系统的使用有一些基本假设,包括“每个namespace的Pod数量不超过3000”,“每个node的Pod数量不能超过110”等。为了满足这些基本假设,clusterloader2在创建rc时,做了不少的特殊处理。
  3. 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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!