第 5 章 配额

5.1. 项目的资源配额

资源配额ResourceQuota 对象定义,提供约束来限制各个项目的聚合资源消耗。它可根据类型限制项目中创建的对象数量,以及该项目中资源可以消耗的计算资源和存储的总和。

本指南阐述了资源配额如何工作,集群管理员如何以项目为基础设置和管理资源配额,以及开发人员和集群管理员如何查看配额。

5.1.1. 配额管理的资源

下方描述了可通过配额管理的一系列计算资源和对象类型。

注意

如果 status.phase in (Failed, Succeeded) 为 true,则 Pod 处于终端状态。

表 5.1. 配额管理的计算资源

资源名称描述

cpu

非终端状态的所有 Pod 的 CPU 请求总和不能超过这个值。CPUrequests.cpu 的值相同,并可互换使用。

memory

非终端状态的所有 Pod 的 内存请求总和不能超过这个值。memoryrequests.memory 的值相同,并可互换使用。

ephemeral-storage

非终端状态的所有本地临时存储请求总和不能超过这个值。ephemeral-storagerequests.ephemeral-storage 的值相同,并可互换使用。

requests.cpu

非终端状态的所有 Pod 的 CPU 请求总和不能超过这个值。CPUrequests.cpu 的值相同,并可互换使用。

requests.memory

非终端状态的所有 Pod 的 内存请求总和不能超过这个值。memoryrequests.memory 的值相同,并可互换使用。

requests.ephemeral-storage

非终端状态的所有临时存储请求总和不能超过这个值。ephemeral-storagerequests.ephemeral-storage 的值相同,并可互换使用。

limits.cpu

非终端状态的所有 Pod 的 CPU 限值总和不能超过这个值。

limits.memory

非终端状态的所有 Pod 的内存限值总和不能超过这个值。

limits.ephemeral-storage

非终端状态的所有 Pod 的临时存储限值总和不能超过这个值。

表 5.2. 配额管理的存储资源

资源名称描述

requests.storage

处于任何状态的所有持久性卷声明的存储请求总和不能超过这个值。

persistentvolumeclaims

项目中可以存在的持久性卷声明的总数。

<storage-class-name>.storageclass.storage.k8s.io/requests.storage

在处于任何状态且具有匹配存储类的所有持久性卷声明中,存储请求总和不能超过这个值。

<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims

项目中可以存在的具有匹配存储类的持久性卷声明的总数。

表 5.3. 配额管理的对象计数

资源名称描述

pods

项目中可以存在的处于非终端状态的 Pod 总数。

replicationcontrollers

项目中可以存在的 ReplicationController 的总数。

resourcequotas

项目中可以存在的资源配额总数。

services

项目中可以存在的服务总数。

services.loadbalancers

项目中可以存在的 LoadBalancer 类型的服务总数。

services.nodeports

项目中可以存在的 NodePort 类型的服务总数。

secrets

项目中可以存在的 secret 的总数。

configmaps

项目中可以存在的 ConfigMap 对象的总数。

persistentvolumeclaims

项目中可以存在的持久性卷声明的总数。

openshift.io/imagestreams

项目中可以存在的镜像流的总数。

5.1.2. 配额范围

每个配额都有一组关联的范围。配额只在与枚举的范围交集匹配时才会测量资源的使用量。

为配额添加范围会限制该配额可应用的资源集合。指定允许的集合之外的资源会导致验证错误。

影响范围

描述

Terminating

匹配 spec.activeDeadlineSeconds >= 0 的 Pod。

NotTerminating

匹配 spec.activeDeadlineSecondsnil 的 Pod。

BestEffort

匹配 cpumemory 具有最佳服务质量的 Pod。

NotBestEffort

匹配 cpumemory 没有最佳服务质量的 Pod。

BestEffort 范围将配额仅限为限制以下资源:

  • pods

TerminatingNotTerminatingNotBestEffort 范围将配额仅限为跟踪以下资源:

  • pods
  • memory
  • requests.memory
  • limits.memory
  • cpu
  • requests.cpu
  • limits.cpu
  • ephemeral-storage
  • requests.ephemeral-storage
  • limits.ephemeral-storage

5.1.3. 配额强制

在项目中首次创建资源配额后,项目会限制您创建可能会违反配额约束的新资源,直到它计算了更新后的使用量统计。

在创建了配额并且更新了使用量统计后,项目会接受创建新的内容。当您创建或修改资源时,配额使用量会在请求创建或修改资源时立即递增。

在您删除资源时,配额使用量在下一次完整重新计算项目的配额统计时才会递减。可配置的时间量决定了将配额使用量统计降低到其当前观察到的系统值所需的时间。

如果项目修改超过配额使用量限值,服务器会拒绝该操作,并将对应的错误消息返回给用户,解释违反了配额约束,并说明系统中目前观察到的使用量统计。

5.1.4. 请求与限值

在分配计算资源时,每个容器可能会为 CPU、内存和临时存储各自指定请求和限制值。配额可以限制任何这些值。

如果配额具有为 requests.cpurequests.memory 指定的值,那么它要求每个传入的容器都明确请求那些资源。如果配额具有为 limits.cpulimits.memory 指定的值,那么它要求每个传入的容器为那些资源指定一个显性限值。

5.1.5. 资源配额定义示例

core-object-counts.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: core-object-counts
spec:
  hard:
    configmaps: "10" 1
    persistentvolumeclaims: "4" 2
    replicationcontrollers: "20" 3
    secrets: "10" 4
    services: "10" 5
    services.loadbalancers: "2" 6

1
项目中可以存在的 ConfigMap 对象的总数。
2
项目中可以存在的持久性卷声明 (PVC) 的总数。
3
项目中可以存在的复制控制器的总数。
4
项目中可以存在的 secret 的总数。
5
项目中可以存在的服务总数。
6
项目中可以存在的 LoadBalancer 类型的服务总数。

openshift-object-counts.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: openshift-object-counts
spec:
  hard:
    openshift.io/imagestreams: "10" 1

1
项目中可以存在的镜像流的总数。

compute-resources.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4" 1
    requests.cpu: "1" 2
    requests.memory: 1Gi 3
    requests.ephemeral-storage: 2Gi 4
    limits.cpu: "2" 5
    limits.memory: 2Gi 6
    limits.ephemeral-storage: 4Gi 7

1
项目中可以存在的处于非终端状态的 Pod 总数。
2
在非终端状态的所有 Pod 中,CPU 请求总和不能超过 1 个内核。
3
在非终端状态的所有 Pod 中,内存请求总和不能超过 1Gi。
4
在非终端状态的所有 Pod 中,临时存储请求总和不能超过 2Gi。
5
在非终端状态的所有 Pod 中,CPU 限值总和不能超过 2 个内核。
6
在非终端状态的所有 Pod 中,内存限值总和不能超过 2Gi。
7
在非终端状态的所有 Pod 中,临时存储限值总和不能超过 4Gi。

besteffort.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: besteffort
spec:
  hard:
    pods: "1" 1
  scopes:
  - BestEffort 2

1
项目中可以存在的具有 BestEffort 服务质量的非终端状态 Pod 的总数。
2
将配额仅限为在内存或 CPU 方面具有 BestEffort 服务质量的匹配 Pod。

compute-resources-long-running.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources-long-running
spec:
  hard:
    pods: "4" 1
    limits.cpu: "4" 2
    limits.memory: "2Gi" 3
    limits.ephemeral-storage: "4Gi" 4
  scopes:
  - NotTerminating 5

1
处于非终端状态的 Pod 总数。
2
在非终端状态的所有 Pod 中,CPU 限值总和不能超过这个值。
3
在非终端状态的所有 Pod 中,内存限值总和不能超过这个值。
4
在非终端状态的所有 Pod 中,临时存储限值总和不能超过这个值。
5
将配额仅限为 spec.activeDeadlineSeconds 设为 nil 的匹配 Pod。构建 Pod 会归入 NotTerminating 下,除非应用了 RestartNever 策略。

compute-resources-time-bound.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources-time-bound
spec:
  hard:
    pods: "2" 1
    limits.cpu: "1" 2
    limits.memory: "1Gi" 3
    limits.ephemeral-storage: "1Gi" 4
  scopes:
  - Terminating 5

1
处于终止状态的 pod 总数。
2
在处于终止状态的所有 Pod 中,CPU 限值总和不能超过这个值。
3
在处于终止状态的所有 Pod 中,内存限值总和不能超过这个值。
4
在处于终止状态的所有 Pod 中,临时存储限值总和不能超过这个值。
5
将配额仅限为 spec.activeDeadlineSeconds >=0 的匹配 Pod。例如,此配额适用于构建或部署器 Pod,而非 Web 服务器或数据库等长时间运行的 Pod。

storage-consumption.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storage-consumption
spec:
  hard:
    persistentvolumeclaims: "10" 1
    requests.storage: "50Gi" 2
    gold.storageclass.storage.k8s.io/requests.storage: "10Gi" 3
    silver.storageclass.storage.k8s.io/requests.storage: "20Gi" 4
    silver.storageclass.storage.k8s.io/persistentvolumeclaims: "5" 5
    bronze.storageclass.storage.k8s.io/requests.storage: "0" 6
    bronze.storageclass.storage.k8s.io/persistentvolumeclaims: "0" 7

1
项目中的持久性卷声明总数
2
在一个项目中的所有持久性卷声明中,请求的存储总和不能超过这个值。
3
在一个项目中的所有持久性卷声明中,金级存储类中请求的存储总和不能超过这个值。
4
在一个项目中的所有持久性卷声明中,银级存储类中请求的存储总和不能超过这个值。
5
在一个项目中的所有持久性卷声明中,银级存储类中声明总数不能超过这个值。
6
在一个项目中的所有持久性卷声明中,铜级存储类中请求的存储总和不能超过这个值。如果此值设为 0,则表示铜级存储类无法请求存储。
7
在一个项目中的所有持久性卷声明中,铜级存储类中请求的存储总和不能超过这个值。如果此值设为 0,则表示铜级存储类无法创建声明。

5.1.6. 创建配额

您可以通过创建配额,来约束给定项目中的资源使用量。

流程

  1. 在一个文件中定义配额。
  2. 使用该文件创建配额,并将其应用到项目:

    $ oc create -f <file> [-n <project_name>]

    例如:

    $ oc create -f core-object-counts.yaml -n demoproject

5.1.6.1. 创建对象数配额

您可以为 OpenShift Container Platform 上的所有标准命名空间资源类型创建对象数配额,如 BuildConfigDeploymentConfig 对象。对象配额数将定义的配额施加于所有标准命名空间资源类型。

在使用资源配额时,对象会根据创建的配额进行收费。这些类型的配额对防止耗尽资源很有用处。只有在项目中有足够的备用资源时,才能创建配额。

流程

为资源配置对象数配额:

  1. 运行以下命令:

    $ oc create quota <name> \
        --hard=count/<resource>.<group>=<quota>,count/<resource>.<group>=<quota> 1
    1
    <resource> 变量是资源名称,<group> 则是 API 组(如果适用)。使用 oc api-resources 命令可以列出资源及其关联的 API 组。

    例如:

    $ oc create quota test \
        --hard=count/deployments.extensions=2,count/replicasets.extensions=4,count/pods=3,count/secrets=4

    输出示例

    resourcequota "test" created

    本例将列出的资源限制为集群中各个项目的硬限值。

  2. 验证是否创建了配额:

    $ oc describe quota test

    输出示例

    Name:                         test
    Namespace:                    quota
    Resource                      Used  Hard
    --------                      ----  ----
    count/deployments.extensions  0     2
    count/pods                    0     3
    count/replicasets.extensions  0     4
    count/secrets                 0     4

5.1.6.2. 为扩展资源设定资源配额

扩展资源不允许过量使用资源,因此您必须在配额中为相同扩展资源指定 requestslimits。目前,扩展资源只允许使用带有前缀 requests. 配额项。以下是如何为 GPU 资源 nvidia.com/gpu 设置资源配额的示例场景。

流程

  1. 确定集群中某个节点中有多少 GPU 可用。例如:

    # oc describe node ip-172-31-27-209.us-west-2.compute.internal | egrep 'Capacity|Allocatable|gpu'

    输出示例

                        openshift.com/gpu-accelerator=true
    Capacity:
     nvidia.com/gpu:  2
    Allocatable:
     nvidia.com/gpu:  2
      nvidia.com/gpu  0           0

    本例中有 2 个 GPU 可用。

  2. 在命名空间 nvidia 中设置配额。本例中配额为 1

    # cat gpu-quota.yaml

    输出示例

    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: gpu-quota
      namespace: nvidia
    spec:
      hard:
        requests.nvidia.com/gpu: 1

  3. 创建配额:

    # oc create -f gpu-quota.yaml

    输出示例

    resourcequota/gpu-quota created

  4. 验证命名空间是否设置了正确的配额:

    # oc describe quota gpu-quota -n nvidia

    输出示例

    Name:                    gpu-quota
    Namespace:               nvidia
    Resource                 Used  Hard
    --------                 ----  ----
    requests.nvidia.com/gpu  0     1

  5. 定义一个请求单个 GPU 的 Pod。以下示例定义文件名为 gpu-pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: gpu-pod-
      namespace: nvidia
    spec:
      restartPolicy: OnFailure
      containers:
      - name: rhel7-gpu-pod
        image: rhel7
        env:
          - name: NVIDIA_VISIBLE_DEVICES
            value: all
          - name: NVIDIA_DRIVER_CAPABILITIES
            value: "compute,utility"
          - name: NVIDIA_REQUIRE_CUDA
            value: "cuda>=5.0"
        command: ["sleep"]
        args: ["infinity"]
        resources:
          limits:
            nvidia.com/gpu: 1
  6. 创建 pod:

    # oc create -f gpu-pod.yaml
  7. 验证 Pod 是否在运行:

    # oc get pods

    输出示例

    NAME              READY     STATUS      RESTARTS   AGE
    gpu-pod-s46h7     1/1       Running     0          1m

  8. 验证配额计数器 Used 是否正确:

    # oc describe quota gpu-quota -n nvidia

    输出示例

    Name:                    gpu-quota
    Namespace:               nvidia
    Resource                 Used  Hard
    --------                 ----  ----
    requests.nvidia.com/gpu  1     1

  9. 尝试在 nvidia 命名空间中创建第二个 GPU Pod。从技术上讲,该节点支持 2 个 GPU,因为它有 2 个 GPU:

    # oc create -f gpu-pod.yaml

    输出示例

    Error from server (Forbidden): error when creating "gpu-pod.yaml": pods "gpu-pod-f7z2w" is forbidden: exceeded quota: gpu-quota, requested: requests.nvidia.com/gpu=1, used: requests.nvidia.com/gpu=1, limited: requests.nvidia.com/gpu=1

    应该会显示此 Forbidden 错误消息,因为您有设为 1 个 GPU 的配额,但这一 Pod 试图分配第二个 GPU,而这超过了配额。

5.1.7. 查看配额

您可以在 Web 控制台导航到项目的 Quota 页面,查看与项目配额中定义的硬限值相关的使用量统计。

您还可以使用命令行来查看配额详情。

流程

  1. 获取项目中定义的配额列表。例如,对于名为 demoproject 的项目:

    $ oc get quota -n demoproject

    输出示例

    NAME                AGE
    besteffort          11m
    compute-resources   2m
    core-object-counts  29m

  2. 描述您关注的配额,如 core-object-counts 配额:

    $ oc describe quota core-object-counts -n demoproject

    输出示例

    Name:			core-object-counts
    Namespace:		demoproject
    Resource		Used	Hard
    --------		----	----
    configmaps		3	10
    persistentvolumeclaims	0	4
    replicationcontrollers	3	20
    secrets			9	10
    services		2	10

5.1.8. 配置显式资源配额

在项目请求模板中配置显式资源配额,以便在新项目中应用特定资源配额。

先决条件

  • 使用具有 cluster-admin 角色的用户访问集群。
  • 安装 OpenShift CLI(oc)。

流程

  1. 在项目请求模板中添加资源配额定义:

    • 如果集群中不存在项目请求模板:

      1. 创建 bootstrap 项目模板并将其输出到名为 template.yaml 的文件:

        $ oc adm create-bootstrap-project-template -o yaml > template.yaml
      2. template.yaml 中添加资源配额定义。以下示例定义了名为 'storage-consumption' 的资源配额。定义必须在模板的 parameter: 部分前添加:

        - apiVersion: v1
          kind: ResourceQuota
          metadata:
            name: storage-consumption
            namespace: ${PROJECT_NAME}
          spec:
            hard:
              persistentvolumeclaims: "10" 1
              requests.storage: "50Gi" 2
              gold.storageclass.storage.k8s.io/requests.storage: "10Gi" 3
              silver.storageclass.storage.k8s.io/requests.storage: "20Gi" 4
              silver.storageclass.storage.k8s.io/persistentvolumeclaims: "5" 5
              bronze.storageclass.storage.k8s.io/requests.storage: "0" 6
              bronze.storageclass.storage.k8s.io/persistentvolumeclaims: "0" 7
        1
        项目中的持久性卷声明总数。
        2
        在一个项目中的所有持久性卷声明中,请求的存储总和不能超过这个值。
        3
        在一个项目中的所有持久性卷声明中,金级存储类中请求的存储总和不能超过这个值。
        4
        在一个项目中的所有持久性卷声明中,银级存储类中请求的存储总和不能超过这个值。
        5
        在一个项目中的所有持久性卷声明中,银级存储类中声明总数不能超过这个值。
        6
        在一个项目中的所有持久性卷声明中,铜级存储类中请求的存储总和不能超过这个值。如果此值设为 0,则 bronze 存储类无法请求存储。
        7
        在一个项目中的所有持久性卷声明中,铜级存储类中请求的存储总和不能超过这个值。如果此值设为 0,则 bronze 存储类无法创建声明。
      3. 通过 openshift-config 命名空间中修改的 template.yaml 文件创建项目请求模板:

        $ oc create -f template.yaml -n openshift-config
        注意

        要将配置作为 kubectl.kubernetes.io/last-applied-configuration注解包括,将 --save-config 选项添加到 oc create 命令中。

        默认情况下,模板称为 project-request

    • 如果项目请求模板已在集群中存在:

      注意

      如果您使用配置文件以声明性或必要方式管理集群中的对象,请使用这些文件编辑现有项目请求模板。

      1. 列出 openshift-config 命名空间中的模板:

        $ oc get templates -n openshift-config
      2. 编辑现有项目请求模板:

        $ oc edit template <project_request_template> -n openshift-config
      3. 将资源配额定义(如前面的 storage-consumption 示例)添加到现有模板中。定义必须在模板的 parameter: 部分前添加。
  2. 如果您创建了项目请求模板,在集群的项目配置资源中引用它:

    1. 访问项目配置资源进行编辑:

      • 使用 web 控制台:

        1. 导航至 AdministrationCluster Settings 页面。
        2. 点击 Global Configuration,查看所有配置资源。
        3. 找到 Project 的条目,并点击 Edit YAML
      • 使用 CLI:

        1. 编辑 project.config.openshift.io/cluster 资源:

          $ oc edit project.config.openshift.io/cluster
    2. 更新项目配置资源的 spec 部分,使其包含 projectRequestTemplatename 参数。以下示例引用了默认项目请求模板(名称为 project-request):

      apiVersion: config.openshift.io/v1
      kind: Project
      metadata:
        ...
      spec:
        projectRequestTemplate:
          name: project-request
  3. 验证在创建项目时是否应用了资源配额:

    1. 创建一个项目:

      $ oc new-project <project_name>
    2. 列出项目的资源配额:

      $ oc get resourcequotas
    3. 详细描述资源配额:

      $ oc describe resourcequotas <resource_quota_name>