kubemark overview
Kubemark是K8s官方提供的一个对K8s集群进行性能测试的工具。它可以模拟出一个K8s cluster(Kubemark cluster),不受资源限制,从而能够测试的集群规模比真实集群大的多。这个cluster中master是真实的机器,所有的nodes是Hollow nodes。Hollow nodes执行的还是真实的K8s程序,只是不会调用Docker,因此测试会走一套K8s API调用的完整流程,但是不会真正创建pod。
Kubermark是在模拟的Kubemark cluster上跑E2E测试,从而获得集群的性能指标。Kubermark cluster的测试数据,虽然与真实集群的稍微有点误差,不过可以代表真实集群的数据,因此,可以借用Kubermark,直接在真实集群上跑E2E测试,从而对我们真实集群进行性能测试。
kubemark的架构图:

kubemark in practice
搭建方法
根据官方文档,k8s中的脚本只适用于GCE。
the steps below use gcloud for SSH and SCP to master, and should be easy to do outside of GCE
Kubemark当然可以在本地运行,于是我找到了这个文档,然而这个脚本存在缩进问题,与rbac权限问题,需要进行小小的修改。
为了搭建kubemark环境,我们需要两个集群,一个集群成为workers cluster,用来承载kubemark的虚拟节点(虚拟节点只是一个pod);一个作为masters cluster,用来承载kubemark的控制面。
事实上,如果只用一个集群也是可以的,但是daemonset资源会在虚拟的节点上创建,影响masters cluster的环境。所以最好的方式还是workers cluster选择一个容量较大的集群,而masters cluster用虚拟机搭一个最简单的集群,因为kubemark对workers cluster是没有影响的
所以最终的完整的搭建步骤如下:
编译kubemark镜像
由于k8s的编译比较耗费资源,且我测试了go 1.13在mac上交叉编译会失败,所以建议选择linux服务器进行编译。
# 进入k8s的目录
make WHAT='cmd/kubemark'
cp _output/bin/kubemark cluster/images/kubemark/
cd cluster/images/kubemark/
sudo make build
# 再打个tag上传就好
不打tag也可以,make的时候传入registry
等环境变量是一样的。
要注意编译的版本要和masters cluster相同,不然运行起来之后kubelet会报错的。
准备masters和workers集群
准备完毕之后,准备好masters集群的Kubeconfig文件,在workers集群上执行
kubectl create ns kubemark
kubectl create configmap node-configmap -n kubemark --from-literal=content.type="test-cluster"
kubectl create secret generic kubeconfig --type=Opaque --namespace=kubemark --from-file=kubelet.kubeconfig=config --from-file=kubeproxy.kubeconfig=config
启动hollow-nodes
将下面的yaml文件命名为hollow-nodes-sts.yaml,这个脚本已经修复了RBAC问题与缩进问题,替换镜像名称之后可以直接食用。并且我无视了容器的错误日志,因为实在太多了,且错误原因是kubemark无法mock存储,并不是环境本身有问题。
apiVersion: v1
kind: Service
metadata:
name: hollow-node
namespace: kubemark
spec:
clusterIP: None
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
name: hollow-node
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubemark
namespace: kubemark
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: kubemark
rules:
- apiGroups: [""]
resources: ["services", "pods", "nodes", "endpoints"]
verbs: ["watch", "list", "get"]
- apiGroups: [""]
resources: ["events"]
verbs: ["*"]
- apiGroups: ["node.k8s.io"]
resources: ["runtimeclasses"]
verbs: ["watch", "list", "get"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: kubemark
subjects:
- kind: ServiceAccount
name: kubemark
namespace: kubemark
roleRef:
kind: ClusterRole
name: kubemark
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: hollow-node
namespace: kubemark
spec:
podManagementPolicy: Parallel
replicas: 150
selector:
matchLabels:
name: hollow-node
serviceName: hollow-node
template:
metadata:
labels:
name: hollow-node
spec:
serviceAccountName: kubemark
initContainers:
- name: init-inotify-limit
image: busybox:latest
command: ['sysctl', '-w', 'fs.inotify.max_user_instances=200']
securityContext:
privileged: true
volumes:
- name: kubeconfig-volume
secret:
secretName: kubeconfig
- name: logs-volume
hostPath:
path: /var/log
containers:
- name: hollow-kubelet
image: xxxxxxxxx
ports:
- containerPort: 4194
- containerPort: 10250
- containerPort: 10255
env:
- name: CONTENT_TYPE
valueFrom:
configMapKeyRef:
name: node-configmap
key: content.type
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
command:
- /bin/sh
- -c
- /kubemark --morph=kubelet --name=$(NODE_NAME) --kubeconfig=/kubeconfig/kubelet.kubeconfig $(CONTENT_TYPE) --v=2 2>/dev/null
volumeMounts:
- name: kubeconfig-volume
mountPath: /kubeconfig
readOnly: true
- name: logs-volume
mountPath: /var/log
resources:
requests:
cpu: 20m
memory: 50M
securityContext:
privileged: true
- name: hollow-proxy
image: xxxxxxxx
env:
- name: CONTENT_TYPE
valueFrom:
configMapKeyRef:
name: node-configmap
key: content.type
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
command:
- /bin/sh
- -c
- /kubemark --morph=proxy --name=$(NODE_NAME) --use-real-proxier=false --kubeconfig=/kubeconfig/kubeproxy.kubeconfig $(CONTENT_TYPE) --alsologtostderr --v=2 2>/dev/null
volumeMounts:
- name: kubeconfig-volume
mountPath: /kubeconfig
readOnly: true
- name: logs-volume
mountPath: /var/log
resources:
requests:
cpu: 20m
memory: 50M
tolerations:
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
之后在workers集群上执行kubectl create -f hollow-nodes-sts.yaml
即可。等待片刻后能看到起来了一堆一堆的pod
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
hollow-node-0 2/2 Running 0 22m xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx <none>
hollow-node-1 2/2 Running 0 22m xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx <none>
hollow-node-10 2/2 Running 0 18m xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx <none>
hollow-node-100 2/2 Running 0 18m xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx <none>
切换到masters cluster上执行kubectl get nodes
可以看到一堆一堆的假的节点了。
dive into kubemark
由于工作原因,我希望kubemark虚拟出的节点可以支持extended resources,所以阅读了kubemark的源码之后,我修改了kubemark的源码,使其支持GPU资源、RDMA资源等等的模拟。我向社区提出了这个issue,希望可以得到回复吧。如果你也有类似的需求,欢迎与我讨论。
再简单说一下Kubemark的源码。入口在cmd/kubemark/hollow-node.go中。可以分为两部分阅读,一部分是proxy,这部分使用了真实的proxy,而非mock的接口。另一部分是kubelet,这个都是mock的接口。
在kubelet中,和节点配置有关的代码都在pkg/kubelet/cm/container_manager_stub.go
中,如果愿意,甚至可以用kubemark做一个仿真平台,节点的意外挂掉等等都是可以模拟出来的,然而这要这样还不如用matlab。