5.4. 推荐的节点主机实践

OpenShift Container Platform 节点配置文件包含重要的选项。例如,控制可以为节点调度的最大 pod 数量的两个参数: podsPerCoremaxPods

当两个参数都被设置时,其中较小的值限制了节点上的 pod 数量。超过这些值可导致:

  • CPU 使用率增加。
  • 减慢 pod 调度的速度。
  • 根据节点中的内存数量,可能出现内存耗尽的问题。
  • 耗尽 IP 地址池。
  • 资源过量使用,导致用户应用程序性能变差。
重要

在 Kubernetes 中,包含单个容器的 pod 实际使用两个容器。第二个容器用来在实际容器启动前设置联网。因此,运行 10 个 pod 的系统实际上会运行 20 个容器。

注意

云供应商的磁盘 IOPS 节流可能会对 CRI-O 和 kubelet 产生影响。当节点上运行大量 I/O 高负载的 pod 时,可能会出现超载的问题。建议您监控节点上的磁盘 I/O,并使用有足够吞吐量的卷。

podsPerCore 根据节点中的处理器内核数来设置节点可运行的 pod 数量。例如:在一个有 4 个处理器内核的节点上将 podsPerCore 设为 10 ,则该节点上允许的最大 pod 数量为 40

kubeletConfig:
  podsPerCore: 10

podsPerCore 设置为 0 可禁用这个限制。默认为 0podsPerCore 不能超过 maxPods

maxPods 把节点可以运行的 pod 数量设置为一个固定值,而不需要考虑节点的属性。

 kubeletConfig:
    maxPods: 250

5.4.1. 创建 KubeletConfig CRD 来编辑 kubelet 参数

kubelet 配置目前被序列化为 Ignition 配置,因此可以直接编辑。但是,在 Machine Config Controller (MCC) 中同时添加了新的 kubelet-config-controller 。这可让您使用 KubeletConfig 自定义资源 (CR) 来编辑 kubelet 参数。

注意

因为 kubeletConfig 对象中的字段直接从上游 Kubernetes 传递给 kubelet,kubelet 会直接验证这些值。kubeletConfig 对象中的无效值可能会导致集群节点不可用。有关有效值,请参阅 Kubernetes 文档

请考虑以下指导:

  • 为每个机器配置池创建一个 KubeletConfig CR,带有该池需要更改的所有配置。如果要将相同的内容应用到所有池,则所有池仅需要一个 KubeletConfig CR。
  • 编辑现有的 KubeletConfig CR 以修改现有设置或添加新设置,而不是为每个更改创建一个 CR。建议您仅创建一个 CR 来修改不同的机器配置池,或用于临时更改,以便您可以恢复更改。
  • 根据需要,创建多个 KubeletConfig CR,每个集群限制为 10。对于第一个 KubeletConfig CR,Machine Config Operator (MCO) 会创建一个机器配置,并附带 kubelet。对于每个后续 CR,控制器会创建另一个带有数字后缀的 kubelet 机器配置。例如,如果您有一个带有 -2 后缀的 kubelet 机器配置,则下一个 kubelet 机器配置会附加 -3

如果要删除机器配置,以相反的顺序删除它们,以避免超过限制。例如,在删除 kubelet-2 机器配置前删除 kubelet-3 机器配置。

注意

如果您有一个带有 kubelet-9 后缀的机器配置,并且创建了另一个 KubeletConfig CR,则不会创建新的机器配置,即使少于 10 个 kubelet 机器配置。

KubeletConfig CR 示例

$ oc get kubeletconfig

NAME                AGE
set-max-pods        15m

显示 KubeletConfig 机器配置示例

$ oc get mc | grep kubelet

...
99-worker-generated-kubelet-1                  b5c5119de007945b6fe6fb215db3b8e2ceb12511   3.2.0             26m
...

以下流程演示了如何配置 worker 节点上的每个节点的最大 pod 数量。

先决条件

  1. 为您要配置的节点类型获取与静态 MachineConfigPool CR 关联的标签。执行以下步骤之一:

    1. 查看机器配置池:

      $ oc describe machineconfigpool <name>

      例如:

      $ oc describe machineconfigpool worker

      输出示例

      apiVersion: machineconfiguration.openshift.io/v1
      kind: MachineConfigPool
      metadata:
        creationTimestamp: 2019-02-08T14:52:39Z
        generation: 1
        labels:
          custom-kubelet: set-max-pods 1

      1
      如果添加了标签,它会出现在 labels 下。
    2. 如果标签不存在,则添加一个键/值对:

      $ oc label machineconfigpool worker custom-kubelet=set-max-pods

流程

  1. 查看您可以选择的可用机器配置对象:

    $ oc get machineconfig

    默认情况下,与 kubelet 相关的配置为 01-master-kubelet01-worker-kubelet

  2. 检查每个节点的最大 pod 的当前值:

    $ oc describe node <node_name>

    例如:

    $ oc describe node ci-ln-5grqprb-f76d1-ncnqq-worker-a-mdv94

    Allocatable 小节中找到 value: pods: <value>

    输出示例

    Allocatable:
     attachable-volumes-aws-ebs:  25
     cpu:                         3500m
     hugepages-1Gi:               0
     hugepages-2Mi:               0
     memory:                      15341844Ki
     pods:                        250

  3. 通过创建一个包含 kubelet 配置的自定义资源文件,设置 worker 节点上的每个节点的最大 pod:

    apiVersion: machineconfiguration.openshift.io/v1
    kind: KubeletConfig
    metadata:
      name: set-max-pods
    spec:
      machineConfigPoolSelector:
        matchLabels:
          custom-kubelet: set-max-pods 1
      kubeletConfig:
        maxPods: 500 2
    1
    输入机器配置池中的标签。
    2
    添加 kubelet 配置。在本例中,使用 maxPods 设置每个节点的最大 pod。
    注意

    kubelet 与 API 服务器进行交互的频率取决于每秒的查询数量 (QPS) 和 burst 值。如果每个节点上运行的 pod 数量有限,使用默认值(kubeAPIQPS50kubeAPIBurst100)就可以。如果节点上有足够 CPU 和内存资源,则建议更新 kubelet QPS 和 burst 速率。

    apiVersion: machineconfiguration.openshift.io/v1
    kind: KubeletConfig
    metadata:
      name: set-max-pods
    spec:
      machineConfigPoolSelector:
        matchLabels:
          custom-kubelet: set-max-pods
      kubeletConfig:
        maxPods: <pod_count>
        kubeAPIBurst: <burst_rate>
        kubeAPIQPS: <QPS>
    1. 为带有标签的 worker 更新机器配置池:

      $ oc label machineconfigpool worker custom-kubelet=large-pods
    2. 创建 KubeletConfig 对象:

      $ oc create -f change-maxPods-cr.yaml
    3. 验证 KubeletConfig 对象是否已创建:

      $ oc get kubeletconfig

      输出示例

      NAME                AGE
      set-max-pods        15m

      根据集群中的 worker 节点数量,等待每个 worker 节点被逐个重启。对于有 3 个 worker 节点的集群,这个过程可能需要大约 10 到 15 分钟。

  4. 验证更改是否已应用到节点:

    1. 在 worker 节点上检查 maxPods 值已更改:

      $ oc describe node <node_name>
    2. 找到 Allocatable 小节:

       ...
      Allocatable:
        attachable-volumes-gce-pd:  127
        cpu:                        3500m
        ephemeral-storage:          123201474766
        hugepages-1Gi:              0
        hugepages-2Mi:              0
        memory:                     14225400Ki
        pods:                       500 1
       ...
      1
      在本例中,pods 参数应报告您在 KubeletConfig 对象中设置的值。
  5. 验证 KubeletConfig 对象中的更改:

    $ oc get kubeletconfigs set-max-pods -o yaml

    这应该显示 True 状态和 type:Success,如下例所示:

    spec:
      kubeletConfig:
        maxPods: 500
      machineConfigPoolSelector:
        matchLabels:
          custom-kubelet: set-max-pods
    status:
      conditions:
      - lastTransitionTime: "2021-06-30T17:04:07Z"
        message: Success
        status: "True"
        type: Success

5.4.2. 修改不可用 worker 节点的数量

默认情况下,在对可用的 worker 节点应用 kubelet 相关的配置时,只允许一台机器不可用。对于大型集群来说,它可能需要很长时间才可以反映出配置的更改。在任何时候,您可以调整更新的机器数量来加快进程速度。

流程

  1. 编辑 worker 机器配置池:

    $ oc edit machineconfigpool worker
  2. maxUnavailable 设置为您需要的值:

    spec:
      maxUnavailable: <node_count>
    重要

    当设置该值时,请考虑无法使用的 worker 节点数量,而不影响在集群中运行的应用程序。

5.4.3. Control plane 节点大小

control plane 节点对资源的要求取决于集群中的节点数量。以下推荐的 control plane 节点大小是基于 control plane 密度测试的结果。control plane 测试会根据节点数在每个命名空间中在集群中创建以下对象:

  • 12 个镜像流
  • 3 个构建配置
  • 6 个构建
  • 1 个部署,带有 2 个 pod 副本,每个都挂载两个 secret
  • 2 个部署,带有 1 个 pod 副本,挂载了两个 secret
  • 3 个指向以前部署的服务
  • 3 个指向之前部署的路由
  • 10 个 secret,其中 2 个由以前的部署挂载
  • 10 个配置映射,其中 2 个由以前的部署挂载
worker 节点数量集群负载(命名空间)CPU 内核内存 (GB)

25

500

4

16

100

1000

8

32

250

4000

16

96

在具有三个 master 或 control plane 节点的大型高密度集群中,当其中一个节点停止、重启或失败时,CPU 和内存用量将会激增。故障可能是因为电源、网络或底层基础架构出现意外问题,除了在关闭集群后重启集群以节约成本的情况下。其余两个 control plane 节点必须处理负载才能高度可用,从而增加资源使用量。另外,在升级过程中还会有这个预期,因为 master 被封锁、排空并按顺序重新引导,以应用操作系统更新以及 control plane Operator 更新。为了避免级联失败,请将 control plane 节点上的总体 CPU 和内存资源使用量保留为最多 60% 的所有可用容量,以处理资源使用量激增。相应地增加 control plane 节点上的 CPU 和内存,以避免因为缺少资源而造成潜在的停机。

重要

节点大小取决于集群中的节点和对象数量。它还取决于集群上是否正在主动创建这些对象。在创建对象时,control plane 在资源使用量方面与对象处于运行(running)阶段的时间相比更活跃。

Operator Lifecycle Manager(OLM)在 control plane 节点上运行,其内存占用量取决于 OLM 在集群中管理的命名空间和用户安装的 operator 的数量。Control plane 节点需要相应地调整大小,以避免 OOM 终止。以下数据基于集群最大测试的结果。

命名空间数量处于空闲状态的 OLM 内存(GB)安装了 5 个用户 operator 的 OLM 内存(GB)

500

0.823

1.7

1000

1.2

2.5

1500

1.7

3.2

2000

2

4.4

3000

2.7

5.6

4000

3.8

7.6

5000

4.2

9.02

6000

5.8

11.3

7000

6.6

12.9

8000

6.9

14.8

9000

8

17.7

10,000

9.9

21.6

重要

您只能为以下配置修改正在运行的 OpenShift Container Platform 4.9 集群中的 control plane 节点大小:

  • 使用用户置备的安装方法安装的集群。
  • 使用安装程序置备的基础架构安装方法安装的 AWS 集群。

对于所有其他配置,您必须估计节点总数并在安装过程中使用推荐的 control plane 节点大小。

重要

建议基于在带有 OpenShiftSDN 作为网络插件的 OpenShift Container Platform 集群上捕获的数据点。

注意

在 OpenShift Container Platform 4.9 中,与 OpenShift Container Platform 3.11 及之前的版本相比,系统现在默认保留半个 CPU 内核(500 millicore)。确定大小时应该考虑这一点。

5.4.4. 设置 CPU Manager

流程

  1. 可选:标记节点:

    # oc label node perf-node.example.com cpumanager=true
  2. 编辑启用 CPU Manager 的节点的 MachineConfigPool 。在这个示例中,所有 worker 都启用了 CPU Manager:

    # oc edit machineconfigpool worker
  3. 为 worker 机器配置池添加标签:

    metadata:
      creationTimestamp: 2020-xx-xxx
      generation: 3
      labels:
        custom-kubelet: cpumanager-enabled
  4. 创建 KubeletConfigcpumanager-kubeletconfig.yaml,自定义资源 (CR) 。请参阅上一步中创建的标签,以便使用新的 kubelet 配置更新正确的节点。请参见 MachineConfigPoolSelector 部分:

    apiVersion: machineconfiguration.openshift.io/v1
    kind: KubeletConfig
    metadata:
      name: cpumanager-enabled
    spec:
      machineConfigPoolSelector:
        matchLabels:
          custom-kubelet: cpumanager-enabled
      kubeletConfig:
         cpuManagerPolicy: static 1
         cpuManagerReconcilePeriod: 5s 2
    1
    指定一个策略:
    • none.这个策略明确启用了现有的默认 CPU 关联性方案,从而不会出现超越调度程序自动进行的关联性。这是默认策略。
    • static。此策略允许保证 pod 中的容器具有整数 CPU 请求。它还限制对节点上的专用 CPU 的访问。如果为 static,则需要使用一个小些 s
    2
    可选。指定 CPU Manager 协调频率。默认值为 5s
  5. 创建动态 kubelet 配置:

    # oc create -f cpumanager-kubeletconfig.yaml

    这会在 kubelet 配置中添加 CPU Manager 功能,如果需要,Machine Config Operator(MCO)将重启节点。要启用 CPU Manager,则不需要重启。

  6. 检查合并的 kubelet 配置:

    # oc get machineconfig 99-worker-XXXXXX-XXXXX-XXXX-XXXXX-kubelet -o json | grep ownerReference -A7

    输出示例

           "ownerReferences": [
                {
                    "apiVersion": "machineconfiguration.openshift.io/v1",
                    "kind": "KubeletConfig",
                    "name": "cpumanager-enabled",
                    "uid": "7ed5616d-6b72-11e9-aae1-021e1ce18878"
                }
            ]

  7. 检查 worker 是否有更新的 kubelet.conf

    # oc debug node/perf-node.example.com
    sh-4.2# cat /host/etc/kubernetes/kubelet.conf | grep cpuManager

    输出示例

    cpuManagerPolicy: static        1
    cpuManagerReconcilePeriod: 5s   2

    1
    在创建 KubeletConfig CR 时,会定义 cpuManagerPolicy
    2
    在创建 KubeletConfig CR 时,会定义 cpuManagerReconcilePeriod
  8. 创建请求一个或多个内核的 pod。限制和请求都必须将其 CPU 值设置为一个整数。这是专用于此 pod 的内核数:

    # cat cpumanager-pod.yaml

    输出示例

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: cpumanager-
    spec:
      containers:
      - name: cpumanager
        image: gcr.io/google_containers/pause-amd64:3.0
        resources:
          requests:
            cpu: 1
            memory: "1G"
          limits:
            cpu: 1
            memory: "1G"
      nodeSelector:
        cpumanager: "true"

  9. 创建 pod:

    # oc create -f cpumanager-pod.yaml
  10. 确定为您标记的节点调度了 pod:

    # oc describe pod cpumanager

    输出示例

    Name:               cpumanager-6cqz7
    Namespace:          default
    Priority:           0
    PriorityClassName:  <none>
    Node:  perf-node.example.com/xxx.xx.xx.xxx
    ...
     Limits:
          cpu:     1
          memory:  1G
        Requests:
          cpu:        1
          memory:     1G
    ...
    QoS Class:       Guaranteed
    Node-Selectors:  cpumanager=true

  11. 确认正确配置了 cgroups。获取 pause 进程的进程 ID(PID):

    # ├─init.scope
    │ └─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 17
    └─kubepods.slice
      ├─kubepods-pod69c01f8e_6b74_11e9_ac0f_0a2b62178a22.slice
      │ ├─crio-b5437308f1a574c542bdf08563b865c0345c8f8c0b0a655612c.scope
      │ └─32706 /pause

    服务质量(QoS)等级为 Guaranteed 的 pod 被放置到 kubepods.slice 中。其它 QoS 等级的 pod 会位于 kubepods 的子 cgroups 中:

    # cd /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod69c01f8e_6b74_11e9_ac0f_0a2b62178a22.slice/crio-b5437308f1ad1a7db0574c542bdf08563b865c0345c86e9585f8c0b0a655612c.scope
    # for i in `ls cpuset.cpus tasks` ; do echo -n "$i "; cat $i ; done

    输出示例

    cpuset.cpus 1
    tasks 32706

  12. 检查任务允许的 CPU 列表:

    # grep ^Cpus_allowed_list /proc/32706/status

    输出示例

     Cpus_allowed_list:    1

  13. 确认系统中的另一个 pod(在这个示例中,QoS 等级为 burstable 的 pod)不能在为等级为Guaranteed 的 pod 分配的内核中运行:

    # cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-podc494a073_6b77_11e9_98c0_06bba5c387ea.slice/crio-c56982f57b75a2420947f0afc6cafe7534c5734efc34157525fa9abbf99e3849.scope/cpuset.cpus
    0
    # oc describe node perf-node.example.com

    输出示例

    ...
    Capacity:
     attachable-volumes-aws-ebs:  39
     cpu:                         2
     ephemeral-storage:           124768236Ki
     hugepages-1Gi:               0
     hugepages-2Mi:               0
     memory:                      8162900Ki
     pods:                        250
    Allocatable:
     attachable-volumes-aws-ebs:  39
     cpu:                         1500m
     ephemeral-storage:           124768236Ki
     hugepages-1Gi:               0
     hugepages-2Mi:               0
     memory:                      7548500Ki
     pods:                        250
    -------                               ----                           ------------  ----------  ---------------  -------------  ---
      default                                 cpumanager-6cqz7               1 (66%)       1 (66%)     1G (12%)         1G (12%)       29m
    
    Allocated resources:
      (Total limits may be over 100 percent, i.e., overcommitted.)
      Resource                    Requests          Limits
      --------                    --------          ------
      cpu                         1440m (96%)       1 (66%)

    这个 VM 有两个 CPU 内核。system-reserved 设置保留 500 millicores,这代表一个内核中的一半被从节点的总容量中减小,以达到 Node Allocatable 的数量。您可以看到 Allocatable CPU 是 1500 毫秒。这意味着您可以运行一个 CPU Manager pod,因为每个 pod 需要一个完整的内核。一个完整的内核等于 1000 毫秒。如果您尝试调度第二个 pod,系统将接受该 pod,但不会调度它:

    NAME                    READY   STATUS    RESTARTS   AGE
    cpumanager-6cqz7        1/1     Running   0          33m
    cpumanager-7qc2t        0/1     Pending   0          11s