前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大规模集群仿真模拟与调度器压测方法

大规模集群仿真模拟与调度器压测方法

作者头像
腾讯云原生
发布2023-01-18 12:11:50
2K0
发布2023-01-18 12:11:50
举报

星辰算力平台基于深入优化云原生统一接入和多云调度,加固容器运行态隔离,挖掘技术增量价值,平台承载了腾讯内部的CPU和异构算力服务,是腾讯内部大规模离线作业、资源统一调度平台。

背景

在大规模 Kubernetes 集群中,集群瞬息万变,每时每刻可能都有相关用户、集群组件、运维人员对集群进行操作。根据大规模集群的注意事项,Kubernetes v1.26 单个集群支持的最大节点数为 5000。更具体地说,Kubernetes 旨在适应满足以下所有标准的配置:

  • 每个节点的 Pod 数量不超过 110
  • 节点数不超过 5000
  • Pod 总数不超过 150000
  • 容器总数不超过 300000

在这样大规模的集群下,通常我们需要压测各类组件来保障集群在突发状况(如高峰时间段)下的性能和可靠性。对于 apiserver、etcd 这类基础组件,我们只要将服务启动后,可以非常容易地进行压测,如通过 clusterloader2 并发创建大量的请求等方式。但针对调度器,我们却需要一个含有大量节点的集群进行模拟测试,但通常情况下很难短时准备如此多的空闲节点,且测试时对节点资源也是一种浪费。

万幸的是,调度器是负责将 Pod 调度到合适的 Node 上,并不关心后续 Pod 的生产过程。如果能够在集群中虚拟出大量的 Node,就可以完成大规模集群的模拟环境搭建。碰巧,Kubernetes 社区开源的新项目 KWOK(Kubernetes WithOut Kubelet) 为我们带来了解决方案。本文将阐述如何快速模拟大规模测试环境(你甚至可以在自己的 minikube 上搭建),并简要给出调度器的压测结果。同时,由于我们在生产环境中大量使用拓扑感知调度功能并已经贡献至 Crane 开源社区,本文中的压测环境也是基于带有拓扑感知调度增强功能的 crane-scheduler。

  • 大规模集群的注意事项:https://kubernetes.io/zh-cn/docs/setup/best-practices/cluster-large/
  • clusterloader2:https://github.com/kubernetes/perf-tests/blob/master/clusterloader2/docs/design.md
  • KWOK:https://github.com/kubernetes-sigs/kwok
  • Crane 开源社区:https://gocrane.io/
  • crane-scheduler:https://github.com/gocrane/crane-scheduler

环境准备

虚拟节点

环境信息:

  • 测试环境:含有300个真实节点的Kubernetes v1.22集群
  • kwok 版本:v0.0.1(这个版本用于调度测试可能有点小问题,见文章末尾)

部署 kwok:

  • 按照此文档进行部署(https://kwok-demo.netlify.app/docs/user/kwok-in-cluster/)
  • 查看 kwok-controller 已被正确部署:kubectl get pod -n kube-system app=kwok-controller

创建节点,按照生产环境下的 CVM 机型进行配置:

cat << EOF > /tmp/node.yaml 
apiVersion: v1
kind: Node
metadata:
  annotations:
    node.alpha.kubernetes.io/ttl: "0"
    kwok.x-k8s.io/node: fake
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: {NODE_NAME}
    kubernetes.io/os: linux
    kubernetes.io/role: agent
    node-role.kubernetes.io/agent: ""
    type: kwok
  name: {NODE_NAME}
spec:
  taints: # Avoid scheduling actual running pods to fake Node
    - effect: NoSchedule
      key: kwok.x-k8s.io/node
      value: fake
status:
  allocatable:
    cpu: 179800m
    ephemeral-storage: "289839513121"
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 64836160Ki
    pods: "64"
  capacity:
    cpu: "180"
    ephemeral-storage: 307125Mi
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 65655360Ki
    pods: "64"
  nodeInfo:
    architecture: amd64
    bootID: ""
    containerRuntimeVersion: ""
    kernelVersion: ""
    kubeProxyVersion: fake
    kubeletVersion: fake
    machineID: ""
    operatingSystem: linux
    osImage: ""
    systemUUID: ""
  phase: Running
EOF

创建100个节点:

for i in {0..99}; do sed "s/{NODE_NAME}/kwok-node-$i/g" /tmp/node.yaml | kubectl apply -f -; done

可以看到100个节点均已 running:

$ kubectl get node
NAME           STATUS   ROLES                  AGE    VERSION
9.134.230.65   Ready    control-plane,master   122d   v1.23.3
kwok-node-0    Ready    agent                  70m    fake
kwok-node-1    Ready    agent                  71m    fake
...
kwok-node-99   Ready    agent                  70m    fake

部署Pod

在集群中按照文档创建 Pod 进行调度器测试。由于 kwok 的节点含有特定的标签和污点,所以 Pod 最好带有特定的  nodeAffinity 和  tolerations,这样可以确保 Pod 被调度到虚拟的节点上。

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fake-pod
  namespace: default
spec:
  replicas: 10
  selector:
    matchLabels:
      app: fake-pod
  template:
    metadata:
      labels:
        app: fake-pod
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: type
                    operator: In
                    values:
                      - kwok
      # A taints was added to an automatically created Node.
      # You can remove taints of Node or add this tolerations.
      tolerations:
        - key: "kwok.x-k8s.io/node"
          operator: "Exists"
          effect: "NoSchedule"
      containers:
        - name: fake-container
          image: fake-image
EOF

部署NRT对象(可选)

针对带有拓扑感知插件的 crane-scheduler,需要创建 NRT 对象。

cat << EOF > /tmp/nrt.yaml 
apiVersion: topology.crane.io/v1alpha1
kind: NodeResourceTopology
metadata:
  name: {NODE_NAME}
  ownerReferences:
  - apiVersion: v1
    blockOwnerDeletion: true
    controller: true
    kind: Node
    name: {NODE_NAME}
    uid: {NODE_UID}
craneManagerPolicy:
  cpuManagerPolicy: Static
  topologyManagerPolicy: SingleNUMANodePodLevel
reserved:
  cpu: 200m
  memory: 700Mi
zones:
- costs:
  - name: node0
    value: 10
  - name: node1
    value: 20
  name: node0
  resources:
    allocatable:
      cpu: 89800m
      memory: 31932648Ki
    capacity:
      cpu: "90"
      memory: 32649448Ki
    reservedCPUNums: 1
  type: Node
- costs:
  - name: node0
    value: 20
  - name: node1
    value: 10
  name: node1
  resources:
    allocatable:
      cpu: "90"
      memory: 33005912Ki
    capacity:
      cpu: "90"
      memory: 33005912Ki
  type: Node
EOF

创建对应的 NRT 对象:

for i in {0..99}; do uid=$(kubectl get node kwok-node-$i -ojsonpath='{.metadata.uid}'); sed "s/{NODE_NAME}/kwok-node-$i/g;s/{NODE_UID}/$uid/g" /tmp/nrt.yaml | kubectl apply -f -; done

删除虚拟节点

测试完成后如果想删除虚拟节点,可以直接删除掉所有的 kwok 节点。

kubectl delete node -l type=kwok

测试程序与误差分析

这里简单准备了一个测试程序(https://github.com/Garrybest/k8s-example)进行调度测试,它会统计某一个 namespace 下所有 Pod 的调度时间,通过 Pod 的 Condition 和 CreationTimestamp 进行判断,最后进行加和以及求平均的工作,这种方式是相对更加精确的。但由于 metav1.Time 结构在传输时采用 RFC3339 进行编码,只能精确到秒,因此会损失部分精度,在大规模测试中可以忽略不计。

当然,你也可以通过暴露调度器的 metrics 来做这项工作,但是我十分不建议在大规模压测时通过 grafana 看板进行调度时间的统计。因为调度器暴露的调度时间指标是通过 histogram 直方图的方式,而 histogram 是假定位于每个 bucket 的样本在该 bucket 内满足均匀分布。当压测进行时,短时间大量创建 Pod,必定有部分 Pod 的调度时长达到分钟级别,此时其所属的 bucket 范围更广,均匀分布的条件就越不可能成立,从 metrics 这统计的调度时间会产生很大的误差(https://hulining.gitbook.io/prometheus/practices/histograms#errors-of-quantile-estimation)。

测试结果

下表展示了最终测试结果。在我的环境下并没有进行调度器调优,也没有使用 clusterloader2 进行更加复杂的测试。读者可根据自己的需求选择不同的压测程序和工具。测试时设置调度器的QPS=200,Burst=400。

测试用例

节点数

并发创建Pod数

总调度时间(s)

平均调度时间(s)

每秒调度Pod数

CPU峰值

内存峰值(GiB)

goroutines峰值

1

5000

1000

8

3.849

125

0.29

1.93

502

2

5000

2000

17

8.917

117.6470588

0.578

3.32

1090

3

5000

5000

45

25.7236

111.1111111

1.39

6.09

1900

4

5000

10000

92

53.2814

108.6956522

3.15

10.3

3300

可以明显看出,每秒调度Pod数大概是在110左右达到极限,这对于大部分场景来说已经够了。我们发现耗时较多的阶段主要是Bind,通过调大QPS和Burst参数还可以将每秒调度Pod数继续提升至160左右。同时短时间创建大量的 Pod 会导致调度器内存飙升,这可能与调度器内部的 cache 有关。

后记

测试过程中顺手修了两个 kwok 的 Bug:

  • #141:kwok 原本把 PodScheduled 的 Condition 覆盖了,导致测试程序获取不到调度时长。
  • #142:kwok 在某些特殊情况下无法正常删除虚拟的节点。

这在 kwok-v0.0.1 的镜像里面是没有包含的,读者可自行从 master 分支编译一个镜像或等待下一个 release 版本发布。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯云原生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 环境准备
    • 虚拟节点
      • 部署Pod
        • 部署NRT对象(可选)
          • 删除虚拟节点
          • 测试程序与误差分析
          • 测试结果
          • 后记
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档