Policies


kubernetes 策略是管理其他配置或运行时行为的一些配置。kubernetes 提供了各种形式的策略:

  1. 使用 API 对象应用策略
    • NetworkPolicy 用于限制工作负载的出入站流量
    • LimitRange 管理多个不同对象类别的资源分配约束
    • ResourceQuota 限制命名空间的资源消耗
  2. 使用准入控制器应用策略 准入控制器允许在 API 服务器上,可以验证或变更 API 请求,某些准入控制器用于应用策略。例如:AlwaysPullImages 准入控制会将 Pod 中容器的镜像拉取策略设置为 Always
  3. 使用 ValidatingAdmissionPolicy 应用策略 允许使用表达式语言 CEL 在 API 服务器中执行可配置的验证检查。如:ValidatingAdmissionPolicy 用于禁止使用 latest 镜像标签
  4. 使用动态准入控制 提供给用户自定义准入控制的一种方式,用户可以自定义自己的准入控制,然后在运行时动态加载到 kube-apiserver 的准入控制验证或变更中。
    • kubewarden 基于 Wasm 的准入控制,允许用户通过自定义策略检查 kubernetes 对象,如 Pod,Deployment,Service 等的创建,更新和删除请求,并在请求被接受或拒绝时执行这些策略
    • kyverno 用于实施声明式的策略管理,允许 kubernetes 用户定义和实施基于资源的策略
    • OPA Gatekeeper 用于执行和管理策略
    • Polaris 提供对集群健康和最佳实践的自动化检查和建议,可以帮助用户识别和解决潜在的问题
  5. 使用 kubelet 配置应用策略,kubernetes 允许每个节点上配置 kubelet,一些 kubelet 配置可以视为策略。
    • 进程 ID 限制和保留:限制或保留可分配的 PID
    • 节点资源管理器:为低延迟和高吞吐量的工作负载管理 CPU,内存和其他设备资源

LimitRange

默认情况下,kubernetes 集群上的容器运行使用的计算资源没有限制。使用命名空间资源配额,可以限制命名空间的资源使用上限;使用 LimitRange 可以限制一个 Pod 可以使用的资源上限和下限。LimitRange 提供限制能力包括:

  1. 限制在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量
  2. 限制在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小
  3. 控制在一个命名空间中实施对一种资源的申请值和限制值的比例
  4. 设置在一个命名空间中对计算资源的默认申请和限制值,并且自动的在运行时注入到多个 Container 中

资源限制和请求的约束

注意事项:

  1. LimitRange 仅在 Pod 准入阶段进行,不对任何正在运行的 Pod 进行验证。如果添加 namespace resourceQuota 时已有对应运行的 Pod 且 Pod 添加了 resourceQuota,则 namespace 配额会更新已使用的配额。
  2. 如果一个命名空间中存在多个 LimitRange,则应用哪个默认值是不确定的。

Pod 的 LimitRange 和准入检查

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-resource-constraint
spec:
  limits:
    - default:
        cpu: 500m # 如果没有定义containers[*].resources.limits.cpu,那将使用此值
      defaultRequest:
        cpu: 500m # 如没有定义containers[*].resources.requests.cpu,那将使用此值
      maxLimitRequestRatio:
        cpu: "10" # 表示对于非零的containers[*].resources.[limits/requests].cpu,每个容器的limits.cpu最大可以是requests.cpu的10倍
      max:
        cpu: "1" # 限制container.resource.cpu.limit的最大值为1000m
      min:
        cpu: 100m # 限制container.resource.cpu.limit的最小值为100m
      type: Container # 作用在containers上,包括initContainers

资源配额

通过ResourceQuota对象来定义,对每个命名空间的资源消耗总量提供限制。它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命名空间中的 Pod 可以使用的计算资源总上限。工作方式如下:

  1. 集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象
  2. 当用户在命名空间下创建资源(如 Pod,Service 等)时,kubernetes 的配额系统会跟踪并确保资源用量不会超过 ResourceQuota 中定义的硬性资源限额
  3. 如果资源创建或者更新违反了配额约束,则该请求会报 http 403 FORBIDDEN 的错误,并在消息中给出有可能违反的约束
  4. 如果命名空间下的计算资源被配额被启用,则 Pod 的创建必须指定资源的 request 和 limit,否则无法创建 Pod。当然,如果使用了 LimitRanger 准入控制器,Pod 没有配置资源用量,则 LimitRanger 会为这些 Pod 设置默认值。

NOTES: 对于其他资源:ResourceQuota 可以工作,并且会忽略命名空间中的 Pod,而无需为该资源设置 limit/request 限额。这意味着,即便资源配额限制了此命名空间的临时存储,也可以创建没有 limit/request 临时存储的新 Pod。也可以使用 LimitRange 自动设置对这些资源的默认值。

启用资源配额

kube-apiserver --enable-admission-plugins=ResourceQuota,若命名空间中存在 ResourceQuota 对象,则该命名空间的资源配额就是开启的。

计算资源配额

资源名称 描述
limits.cpu 所有非终止状态的 Pod,其 CPU 限额总量不超过该值
limits.memory
requests.cpu 所有非终止状态的 Pod,其 CPU 需求总量不能超过该值,也可以理解为是一个初始值
requests.memory
hugepages- 所有非终止状态的 Pod,针对指定尺寸的大块内存请求总数不能超过此值

扩展资源的配额

由于扩展资源不可超量分配,因此没有必要在配额中为同一扩展资源同时指定 requests 和 limits,对于扩展资源目前仅允许使用前缀为requests.的配额项。如:requests.nvidia.com/gpu: 4,限制请求的 GPU 用量为 4。

存储资源配额

用户可以对给定命名空间下的存储资源总量进行限制,还可以根据相关的 StorageClass 来限制存储资源的消耗。

资源名称 描述
requests.storage 所有 PVC 存储资源的需求总量不能超过该值
persistentvolumeclaims 在该命名空间下所允许的 PVC 总量
.storageclass.storage.k8s.io/requests.storage 在所有与相关的 PVC 中,存储请求的总和不能超过该值
.storageclass.storage.k8s.io/persistentvolumeclaims 在所有与相关的 PVC 中,命名空间中可以存在的 PVC 总数

在 v1.8 版本中,本地临时存储的配额支持已经是 alpha 功能:

资源名称 描述
requests.ephemeral-storage 在命名空间的所有 Pod 中,本地临时存储 requests 的总和不能超过此值
limits.ephemeral-storage 在命名空间的所有 Pod 中,本地临时存储 limits 的总和不能超过此值
ephemeral-storage 与 requests.ephemeral-storage 相同

NOTES: 如果所使用的是 CRI 容器运行时,容器日志会被记入临时存储配额。这可能会导致存储配额耗尽的 Pod 被意外地 evict 节点。

对象数量的配额

可以使用以下语法对所有标准的、命名空间域的资源类型进行配额限制:

  • count/<resource>.<group> 用于非 core 组的资源
  • count/<resource>用于 core 组的资源

也可以用于自定义资源,如:要对example.comAPI 组中的自定义资源widgets设置配额,count/widgets.example.com

当使用count/*资源配额时,如果对象存在于服务器存储中,则会根据配额管理资源。如:集群中存在过多的 Secret 实际上会导致服务器无法启动;配置不当的 CronJob 在命名空间中创建太多 Job 而导致集群拒绝服务。

资源名称 描述
pods 该命名空间中允许存在的非终止态(.status.phase in [Failed, Succeed])的 Pod 总数上限,主要是为了防止 Pod 过多而耗尽集群所能提供的 Pod IP 地址

配额作用域

resourcequota.spec.scopeSelector,resourcequota.spec.scopes:每个配额仅会对作用域内的资源生效,配额机制仅统计所列举的作用域的交集中的资源用量。

当一个作用域被添加到配额中后,它会对作用域相关的资源数量作限制。如配额中制定了允许(作用域)集合之外的资源,会导致验证错误。

作用域 描述
Terminating 匹配所有 spec.activeDeadlineSeconds不小于 0 的 Pod
NotTerminating 匹配所有spec.activeDeadlineSeconds是 nil 的 Pod
BestEffort 匹配所有 QoS 是 BestEffort 的 Pod
NotBestEffort 匹配所有 QoS 不是 BestEffort 的 Pod
PriorityClass 匹配所有引用了所指定的优先级类的 Pods
CrossNamespacePodAffinity 匹配那些设置了跨名字空间 podAffinity 和 podAntiAffinity
scopeSelector:
  matchExpressions:
    - scopeName: PriorityClass
      operator: In
      values:
        - middle

基于 PriorityClass 来设置资源配额

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high
value: 1000
description: A demo high priority.
---
apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
    - name: high-priority
      image: ubuntu
      command: ["/bin/sh"]
      args: ["-c", "while true; do echo hello; sleep 10;done"]
      resources:
        requests:
          memory: "10Gi"
          cpu: "500m"
        limits:
          memory: "10Gi"
          cpu: "500m"
  priorityClassName: high

priority-quota

跨名字空间的 PodAffinity 配额

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0" # 禁用了该名字空间创建Pod
  scopeSelector:
    matchExpressions:
      - scopeName: CrossNamespacePodAffinity
        operator: Exists # 如果存在跨命名空间Pod亲和性功能,则应用该资源配额

禁止使用CrossNamespacePodAffinityCrossNamespacePodAntiAffinity,可以配置kube-apiserver --admissiion-control-config-file=<下面的yaml>

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: "ResourceQuota"
    configuration:
      apiVersion: apiserver.config.k8s.io/v1
      kind: ResourceQuotaConfiguration
      limitedResources:
        - resource: pods
          matchScopes:
            - scopeName: CrossNamespacePodAffinity
              operator: Exists # 当存在跨命名空间Pod亲和性功能时应用资源配额的限制

进程 ID 约束与预留

kubernetes 允许用户限制一个 Pod 中可以使用的 PID 数目。用户也可以为每个节点预留一点数量的可分配的 PID,供操作系统和守护进程(非 Pod)使用。

PID 时节点上的一种基础资源。很容易就会在尚未超出其他资源约束的时候就已经触及任务个数上限,进而导致宿主机不稳定。

集群管理员需要一定的机制来确保集群中运行的 Pod 不会导致 PID 资源枯竭,甚至造成宿主机上的守护进程(如:kubelet 或 kube-proxy,以及容器运行时本身)无法正常运行。此外,确保 Pod 中的 PID 的个数受限对于保证其不会影响到同一节点上其他负载也很重要。

NOTES: 默认 PID 上限可能为 32768,用户可以通过修改/proc/sys/kernel/pid_max来扩大 PID 的上限。

节点级别 PID 限制

可以为 kubelet 使用--system-reserved--kube-reserved命令行选项参数pid=<number>,分别对应整个操作系统和 kubernetes 系统守护进程所保留的 PID 数目。

Pod 级别的 PID 限制

也可以限制 POD 中的 PID 个数,在节点级别设置这一限制,而不是为特定的 Pod 来将其设置为资源限制,每个节点都可以有不同的 PID 限制设置。使用kubelet --pod-max-pids或者在 kubelet 配置文件中设置PodPidsLimit

基于 PID 的 eviction

可以配置 kubelet 使之在 Pod 行为不正常或者消耗不正常数量资源的时候将其终止。可以针对不同的驱逐信号配置资源不足的处理。使用pid.available驱逐信号来配置 Pod 使用的 PID 个数的阈值,可以设置硬性的和软性的驱逐策略。不过即使使用硬性的驱逐策略,如果 PID 个数增长过快,节点仍然可能因为初级节点 PID 限制而进入一种不稳定状态。驱逐信号的取值时周期性计算的,而不是一直能够强制实施约束。

Pod 级别和节点级别的 PID 限制会设置硬性限制,一旦触及限制,工作负载会在尝试获得新的 PID 时遇到问题。这可能会也可能不会导致 Pod 被重新调度,取决于工作负载如何应对这类失败以及 Pod 的存货行和就绪态探测时如何配置的。可是,如果限制值被正确设置,用户可以确保其他 Pod 负载和系统进程不会因为某个 Pod 行为不正常而没有 PID 可用。

节点资源管理器

kubernetes 提供了一组资源管理器,用于直至延迟敏感的、高吞吐量的工作负载。资源管理器的目标时协调和优化节点资源,以支持对 CPU、设备和内存(hugepage)等资源有特殊需求的 Pod。

主管理器(Topology Manager),是一个 kubelet 组件,通过策略,协调全局的资源管理过程。

各个管理器的配置方式会在专项文档中详细阐述。

CPU 管理器

默认情况下,kubelet 使用[CFS 策略]来执行 Pod 的 CPU 约束。当节点上运行了很多 CPU 密集的 Pod 时,工作负载可能会迁移到不同的 CPU 核,这取决于调度时 Pod 是否被扼制,以及哪些 CPU 核是可用的。许多工作负载对这种迁移不敏感,因此无需任何干预即可正常工作。

然而,有些工作负载的性能明显地收到 CPU 缓存亲和性以及调度延迟的影响。对此 kubelet 提供了可选的 CPU 管理策略,来确定节点上的一些分配偏好。

要配置 CPU 管理策略,可以通过kubelet --cpu-manager-policy或使用KubeletConfiguration中的cpuManagerPolicy字段来指定,支持 2 中策略:

  1. none: 默认策略
  2. static: 允许为节点上具有某些资源特征的 Pod 赋予增强的 CPU 亲和性和独占性。

CPU 管理器定期通过 CRI 写入资源更新,以保证内存中 CPU 分配与 cgroupfs 一致。同步频率通过 kubelet 配置参数--cpu-manager-reconcile-period设置,如不指定,默认与--node-status-update-frequency的周期相同。

Static 策略的行为可以使用--cpu-manager-policy-options=key1=value1,key2=value2参数来微调。

除了顶级的 CPUManagerPolicyOptions feature gate,策略选项分为 2 组:alpha(默认隐藏)和 beta(默认可见)。分别由CPUManagerPolicyAlphaOptionsCPUManagerPolicyBetaOptionsfeature gate 来管控。

内存管理器

使用 NUMA(Non-Uniform Memory Access)为 Guaranteed QoS 类的 Pods 提供可保证的内存及 hugepage 分配能力。

内存管理器使用提示生成协议来为 Pod 生成最合适的 NUMA 亲和性配置。内存管理器将这类亲和性提示输入给中央管理器(Topology Manager)。基于所给的提示和 Topology Manager 的策略设置,Pod 或者会被某节点接受,或者被该节点拒绝。

此外,内存管理器还全包 Pod 所请求的内存是从尽量少的 NUMA 节点分配而来。

内存管理器仅使用与 Linux 主机。

设备管理器 TBD