背景
Kubernetes是容器集群管理系统,为容器化的应用提供资源调度、部署运行、滚动升级、扩容缩容等功能。容器集群管理给业务带来了便利,但是随着业务的不断增长,应用数量可能会发生爆发式的增长。那在这种情况下,Kubernetes能否快速地完成扩容、扩容到大规模时Kubernetes管理能力是否稳定成了挑战。
提前测试目前 Kubernetes 能承载的最大 Node、Pod 数量。
SLI & SLO
SLI(Service Level Indicator):服务等级指标,其实就是我们选择哪些指标来衡量我们的稳定性。
SLO(Service Level Objective):服务等级目标,指的就是我们设定的稳定性目标,比如“几个 9”这样的目标。
SLO 是 SLI 要达成的目标,我们需要选择合适的 SLI,设定对应的 SLO。
官方提供了三个指标,如下
SLI | SLO | 测试方法 |
---|---|---|
Latency of processing mutating API calls for single objects for every (resource, verb) pair, measured as 99th percentile over last 5 minutes | 99Line <= 1s | 官方 Kubemark + perf-test |
Latency of processing non-streaming read-only API calls for every (resource, scope) pair, measured as 99th percentile over last 5 minutes | (a) <= 1s if scope=resource (b) <= 30s otherwise (if scope=namespace or scope=cluster ) |
官方 Kubemark + perf-test |
Startup latency of schedulable stateless pods, excluding time to pull images and run init containers, measured from pod creation timestamp to when all its containers are reported as started and observed via watch, measured as 99th percentile over last 5 minutes | 99Line <= 5s | 官方 Kubemark + perf-test |
上面我们有了“性能指标”,以及这些性能指标的“判断条件”。那么怎么测试?
Kubernetes开源了Clusterloader2性能测试框架,帮助我们完成上面的测试过程,并且统计测试结果。
Clusterloader2主要提供了两个测试用例:
(1)密度测试:该测试用例主要用来测试节点规模和容器规模的性能指标。它的大致思路是:在一个有N个节点的集群中,连续创建30*N个Pod,然后再删除这些Pod,然后跟踪这个过程中,上面的三个SLO是否满足。
(2)负载测试:该测试用例的主要思路是,向K8S进行大量的各种类型的资源创建、删除、LIST以及其他操作,然后跟踪这个过程中,上面的三个SLO是否满足。
另外,由于在大多数场景中,无法真实创建5000个节点,Kubernetes开源了一个kubemark项目,用来模拟真实节点。
使用 kubemark 模拟100个 Node 节点
环境说明:
- work 环境作为 性能测试环境
- test 环境节点,作为 node 提供方
kubemark项目编译及镜像制作
# 下载指定版本源码
git clone -b v1.18.10 https://github.com/kubernetes/kubernetes.git
cd kubernetes/./hack/build-go.sh cmd/kubemark/
cp _output/bin/kubemark cluster/images/kubemark/
cd cluster/images/kubemark/
# 修改 Dockerfile 中镜像为 centos:7
make build
# 改镜像仓库,改tag,push,方便其他节点使用
docker tag staging-k8s.gcr.io/kubemark:latest wangzhichidocker/kubemark:0.1
docker push wangzhichidocker/kubemark:0.1# test master 操作
kubectl create ns kubemark
kubectl create configmap node-configmap -n kubemark --from-literal=content.type="test-cluster"
# 将 work 环境master 节点的 /root/.kube/config 拷贝过来
kubectl create secret generic kubeconfig --type=Opaque --namespace=kubemark --from-file=kubelet.kubeconfig=config --from-file=kubeproxy.kubeconfig=config# 打标签
kubectl label node $NodeName name=hollow-node# NodeName 修改maxPods值 2000
kubectl apply -f deploy.yaml
# deploy.yaml 内容如下
kubemark 虚化建立 100 个 node
apiVersion: apps/v1
kind: Deployment
metadata:name: hollow-nodenamespace: kubemarklabels:name: hollow-node
spec:replicas: 100 ###启动的虚拟节点的数量selector:matchLabels:name: hollow-nodetemplate:metadata:labels:name: hollow-nodespec:nodeSelector:name: hollow-nodeinitContainers:- name: init-inotify-limitimage: busyboximagePullPolicy: IfNotPresentcommand: ['sysctl', '-w', 'fs.inotify.max_user_instances=524288']securityContext:privileged: truevolumes:- name: kubeconfig-volumesecret:secretName: kubeconfigcontainers:- name: hollow-kubeletimage: wangzhichidocker/kubemark:0.1imagePullPolicy: IfNotPresentports:- containerPort: 4194- containerPort: 10250- containerPort: 10255env:- name: CONTENT_TYPEvalueFrom:configMapKeyRef:name: node-configmapkey: content.type- name: NODE_NAMEvalueFrom:fieldRef:fieldPath: metadata.namecommand:- /bin/sh- -c- /kubemark --morph=kubelet --name=$(NODE_NAME) --kubeconfig=/kubeconfig/kubelet.kubeconfig $(CONTENT_TYPE) --alsologtostderr --v=2volumeMounts:- name: kubeconfig-volumemountPath: /kubeconfigreadOnly: truesecurityContext:privileged: true- name: hollow-proxyimage: wangzhichidocker/kubemark:0.1imagePullPolicy: IfNotPresentenv:- name: CONTENT_TYPEvalueFrom:configMapKeyRef:name: node-configmapkey: content.type- name: NODE_NAMEvalueFrom:fieldRef:fieldPath: metadata.namecommand:- /bin/sh- -c- /kubemark --morph=proxy --name=$(NODE_NAME) --use-real-proxier=false --kubeconfig=/kubeconfig/kubeproxy.kubeconfig $(CONTENT_TYPE) --alsologtostderr --v=2volumeMounts:- name: kubeconfig-volumemountPath: /kubeconfigreadOnly: truetolerations:- operator: "Exists"affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 硬策略nodeSelectorTerms:- matchExpressions:- key