使用 Pod 级资源与 kubelet 资源管理器

使用 Pod 级资源与 kubelet 资源管理器

特性状态: Beta since Kubernetes v1.37; (默认禁用)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the PodLevelResourceManagers feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

本教程演示如何配置 kubelet 的资源管理器(拓扑、CPU 和内存) 以支持 Pod 级资源规约。你可以定义混合分配模型: 其中一些容器获得独占的、NUMA 对齐的基础设施资源, 而另一些容器则共享 Pod 级共享池中的剩余资源。

要了解该特性背后的概念,请阅读 Pod 级资源管理器概念页面。

教程目标

  • 配置 kubelet 资源管理器(CPU、内存和拓扑)以支持 Pod 级资源。
  • 使用 pod 拓扑管理器作用域部署工作负载,以实现混合的独占容器和共享容器,并达到单 NUMA 对齐。
  • 查看并验证 CPU 和内存资源如何在独占容器与 Pod 共享池之间划分。
  • 了解当 Pod 级共享池将为空时的准入拒绝规则。
  • 使用 container 拓扑管理器作用域和混合的容器分配部署工作负载。

准备开始

你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.36.

要获知版本信息,请输入 kubectl version.

要完成本教程,你需要:

  • 一个带有 Linux 工作节点的 Kubernetes 集群 (Pod 级资源管理器在 Windows 节点上不受支持)。
  • 至少一个具有 NUMA 拓扑的工作节点 (最好有多个 NUMA 节点,以便观察对齐情况)。
  • 在工作节点上拥有管理权限(root 或 sudo), 以修改 kubelet 配置并重启 kubelet 服务。
  • 拥有 kubectl 访问权限,且具备创建名字空间和 Pod 的权限。

确保为控制平面和工作节点启用了以下特性门控:

  • PodLevelResources
  • PodLevelResourceManagers

创建一个名字空间

创建一个名字空间,使本教程中创建的资源与集群的其余部分隔离开来:

kubectl create namespace plrm-tutorial

在混合分配场景中使用 Pod 作用域

当拓扑管理器的作用域设置为 pod 时,kubelet 会基于 .spec.resources 为整个 Pod 执行一次 NUMA 对齐。得到的资源预算随后被划分: 请求 Guaranteed 资源的容器获得独占切片, 而未获得独占分配的容器则在 Pod 级共享池中共享剩余的预算。

第 1 步:针对 Pod 作用域配置 kubelet

若要启用此行为,请在希望以所需策略运行这些工作负载的目标工作节点上配置 kubelet。 你可以按如下方式更新这些节点的 kubelet 配置:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cpuManagerPolicy: "static"
memoryManagerPolicy: "Static"
topologyManagerScope: "pod"
topologyManagerPolicy: "single-numa-node"
  • 对于 topologyManagerPolicy,有效值为 single-numa-node、restricted 或 best-effort。 使用 Pod 级资源管理时,你不能指定其他任何值。

重启 kubelet 以应用该配置。例如,在使用 systemd 的 Linux 上: systemctl restart kubelet.service。

第 2 步:部署混合分配的 Pod

请考虑以下示例 Pod 清单。该 Pod 在 Pod 级别(.spec.resources) 请求总计 4 个 CPU 的预算。在该 Pod 内部:

  • main-app 容器请求 2 个完整 CPU 核心的独占分配 (requests = limits = 2 CPU)。
  • 由于 metrics-sidecar 和 logging-sidecar 未指定容器级别的 requests, 这两个边车容器共享 Pod 级共享池中剩余的 CPU 核心:2 个 CPU 核心。
apiVersion: v1
kind: Pod
metadata:
  name: pod-scope-mixed
  annotations:
    kubernetes.io/description: "A pod demonstrating pod-level scope where one container gets exclusive resources and others share the remaining pod resources in a shared pool."
spec:
  # 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
  # main-app 容器在容器级别满足 Guaranteed QoS 类的要求,而各边车容器未指定任何资源 request。
  # 在 pod 范围下,这意味着 kubelet 可以静态地为整个 Pod 分配
  # 4 个 CPU,其中 2 个独占分配给 main-app 容器,剩余的 2 个由边车在该 Pod 的共享池中共享。
  resources:
    requests:
      cpu: "4"
      memory: "4Gi"
    limits:
      cpu: "4"
      memory: "4Gi"
  initContainers:
  - name: metrics-sidecar
    # 注意:这是仅用于演示的占位镜像,并不是真正的 metrics 助手。
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
  - name: logging-sidecar
    # 注意:这是仅用于演示的占位镜像,并不是真正的日志代理。
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
  containers:
  - name: main-app
    # 注意:这是仅用于演示的占位镜像。
    image: registry.k8s.io/pause:3.9
    resources:
      requests:
        cpu: "2"
        memory: "2Gi"
      limits:
        cpu: "2"
        memory: "2Gi"

将清单应用到你的集群:

kubectl apply -f https://k8s.io/examples/pods/resource/pod-level-resource-managers-pod-scope-mixed.yaml --namespace=plrm-tutorial

第 3 步:验证和检查资源划分

  1. 检查 Pod 是否成功运行:

    kubectl get pod pod-scope-mixed --namespace=plrm-tutorial
    
  1. 了解背后发生了什么:

    flowchart TD
        subgraph Pod["Pod-Level Budget: 4 CPUs, 4Gi Memory"]
            direction TB
            C1["main-app<br/>(Exclusive: 2 CPUs, 2Gi Memory)"]
            subgraph Pool["Pod Shared Pool: 2 CPUs, 2Gi Memory"]
                C2["metrics-sidecar"]
                C3["logging-sidecar"]
            end
        end
    • Pod 对齐: 拓扑管理器评估了 4 个 CPU 的 Pod 请求 (spec.resources),并将整个 Pod 分配到单个 NUMA 节点。
    • 独占分配: CPU 管理器为 main-app 分配了一个专属的 2 个 CPU 切片。 main-app 的 CPU CFS 配额节流被禁用,使其能够不受节流地访问这些独占核心。
    • Pod 共享池: 剩余的 2 个 CPU 构成一个 Pod 级共享池。 metrics-sidecar 和 logging-sidecar 未指定任何容器级资源 (resources: {}),因此它们在这个 Pod 隔离的共享池中运行, 并启用了 CFS 配额强制执行。虽然该池由这两个边车共享, 但它与节点上的外部工作负载隔离开来,为这些边车提供专属的 NUMA 局部性,并防止节点级资源争用。

观察空共享池的准入限制

使用 pod 作用域时,如果存在需要共享池的容器,而某些 Pod 规约会导致 Pod 共享池为空, 则 kubelet 的准入控制会拒绝这些 Pod 规约。

如果来自 Guaranteed 容器的独占资源请求之和等于 Pod 级总预算, 并且至少还有一个容器需要共享池,那么 kubelet 将拒绝该 Pod。

第 4 步:检查无效的 Pod 清单

请考虑以下清单。该 Pod 请求总计 4 个 CPU 的预算。 container-a 请求独占的 1 个 CPU,container-b 请求独占的 3 个 CPU (合计 4 个 CPU)。container-c 未请求独占资源且需要共享池,但已剩下 0 个 CPU:

apiVersion: v1
kind: Pod
metadata:
  name: empty-shared-pool
  annotations:
    kubernetes.io/description: "A pod demonstrating a configuration that is rejected because exclusive containers consume the entire pod resource budget, leaving no resources for the remaining container in the shared pool."
spec:
  # 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
  # main-app 和 metrics-sidecar 容器在容器级别满足 Guaranteed QoS 类的要求,而
  # logging-sidecar 容器未指定任何资源 request。由于这些 Guaranteed 容器耗尽了
  # Pod 的全部资源预算,logging-sidecar 所需的共享池中已剩下 0 个 CPU,
  # 因此该 Pod 会在准入时被拒绝。
  resources:
    requests:
      cpu: "4"
      memory: "4Gi"
    limits:
      cpu: "4"
      memory: "4Gi"
  initContainers:
  - name: metrics-sidecar
    # 注意:这是仅用于演示的占位镜像,并不是真正的 metrics 助手。
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
    resources:
      requests:
        cpu: "1"
        memory: "1Gi"
      limits:
        cpu: "1"
        memory: "1Gi"
  - name: logging-sidecar
    # 注意:这是仅用于演示的占位镜像,并不是真正的日志代理。
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
  containers:
  - name: main-app
    # 注意:这是仅用于演示的占位镜像。
    image: registry.k8s.io/pause:3.9
    resources:
      requests:
        cpu: "3"
        memory: "3Gi"
      limits:
        cpu: "3"
        memory: "3Gi"

第 5 步:尝试部署并观察拒绝

  1. 应用此清单:

    kubectl apply -f https://k8s.io/examples/pods/resource/pod-level-resource-managers-empty-shared-pool.yaml --namespace=plrm-tutorial
    
  1. 查看 Pod 事件以观察准入错误:

    kubectl describe pod empty-shared-pool --namespace=plrm-tutorial
    

    请注意事件消息,它说明 kubelet 拒绝该 Pod 的原因是: 对于需要共享资源的容器而言,Pod 级共享池将为空:

    Status:           Failed
    Reason:           TopologyAffinityError
    Message:          Pod was rejected: Pod Scope pod with pod-level resources failed admission under pod-scope topology manager
    
    flowchart TD
        subgraph Pod["Pod-Level Budget: 4 CPUs, 4Gi Memory"]
            direction TB
            C1["container-a<br/>(Exclusive: 1 CPU, 1Gi Memory)"]
            C2["container-b<br/>(Exclusive: 3 CPUs, 3Gi Memory)"]
            subgraph Pool["Pod Shared Pool: 0 CPUs, 0Gi Memory"]
                C3["container-c<br/>(Requires shared pool)"]
            end
        end
        Pool --> Rejection["Admission Error: Pod Rejected!"]
        style Rejection fill:#ffcccc,stroke:#ff0000,stroke-width:2px

在混合分配场景下使用 container 作用域

你也可以将拓扑管理器的作用域配置为 container。在此模式下,kubelet 会逐个评估每个容器以进行独占分配, 而 .spec.resources 中的 Pod 总体预算仍会强制执行 QoS 和 cgroup 限额边界。

第 6 步:为 container 作用域配置 kubelet

在目标工作节点上更新你的 kubelet 配置,以使用 container 作用域:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cpuManagerPolicy: "static"
memoryManagerPolicy: "Static"
topologyManagerScope: "container"
topologyManagerPolicy: "single-numa-node"

重启 kubelet 以应用该配置。例如,在使用 systemd 的 Linux 上: systemctl restart kubelet.service。

第 7 步:部署容器作用域的混合工作负载

请考虑以下示例 Pod 清单。该 Pod 的总预算为 4 个 CPU:

  • infrastructure-sidecar 请求独占的 2 个 CPU 切片(requests = limits = 2 CPU)。
  • worker-1 和 worker-2 未指定容器级别的 requests,而是在常规的节点级共享池中运行。
apiVersion: v1
kind: Pod
metadata:
  name: container-scope-mixed
  annotations:
    kubernetes.io/description: "A pod demonstrating container-level scope where one container gets exclusive resources and others run in the node's shared pool."
spec:
  # 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
  # infrastructure-sidecar 容器在容器级别满足 Guaranteed QoS 类的要求,而各 worker
  # 容器未指定任何资源 request。在 container 范围下,kubelet 会逐个评估容器以进行独占分配。
  # 这意味着 infrastructure-sidecar 获得独占的 2 个 CPU 切片,
  # 而 worker 容器在节点的常规共享池中运行,同时所有容器的总量仍受 Pod 级限额约束。
  resources:
    requests:
      cpu: "4"
      memory: "4Gi"
    limits:
      cpu: "4"
      memory: "4Gi"
  initContainers:
  - name: infrastructure-sidecar
    # 注意:这是仅用于演示的占位镜像,并不是真正的基础设施助手。
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
    resources:
      requests:
        cpu: "2"
        memory: "2Gi"
      limits:
        cpu: "2"
        memory: "2Gi"
  containers:
  - name: worker-1
    # 注意:这是仅用于演示的占位镜像。
    image: registry.k8s.io/pause:3.9
  - name: worker-2
    # 注意:这是仅用于演示的占位镜像。
    image: registry.k8s.io/pause:3.9

应用该清单:

kubectl apply -f https://k8s.io/examples/pods/resource/pod-level-resource-managers-container-scope-mixed.yaml --namespace=plrm-tutorial

第 8 步:验证部署

  1. 检查 Pod 是否正在运行:

    kubectl get pod container-scope-mixed --namespace=plrm-tutorial
    
  1. 了解背后发生了什么:

    flowchart TD
        subgraph Pod["Pod-Level Budget: 4 CPUs, 4Gi Memory"]
            direction TB
            C1["infrastructure-sidecar<br/>(Exclusive NUMA Slice: 2 CPUs, 2Gi Memory)"]
            subgraph NodePool["Node Shared Pool: Pod-level limit"]
                C3["worker-2"]
                C2["worker-1"]
            end
        end
    • 容器作用域对齐: 在 container 作用域下,kubelet 会逐个评估容器。 infrastructure-sidecar 直接从节点的可分配池中获得一个独占的、NUMA 对齐的 2 个 CPU 切片。
    • 节点共享池: worker-1 和 worker-2 未指定任何容器级资源请求 (resources: {}),因此在 container 作用域下,它们在节点的常规共享池中运行(而不是在 Pod 隔离的池中)。
    • Pod 限额强制执行: 所有容器的 CPU 消耗总量仍由 Pod 级限额(spec.resources.limits)限制在 4 个 CPU 以内。

清理现场

删除本教程中创建的名字空间和所有示例 Pod:

kubectl delete namespace plrm-tutorial

接下来

最后修改 October 15, 2026 at 10:20 AM PST: [zh] Add use-pod-level-resource-managers.md (560cfe427d)