資源配額

當多個使用者或團隊共用節點數量固定的叢集時,會擔心一個團隊可能使用超過其公平份額的資源。

資源配額是管理員用來解決此問題的工具。

資源配額由 ResourceQuota 物件定義,提供約束來限制每個命名空間的彙總資源消耗。它可以限制命名空間中可依類型建立的物件數量,以及該命名空間中資源可能消耗的運算資源總量。

資源配額的運作方式如下

  • 不同的團隊在不同的命名空間中工作。這可以使用 RBAC 來強制執行。

  • 管理員為每個命名空間建立一個 ResourceQuota。

  • 使用者在命名空間中建立資源 (Pod、服務等),配額系統會追蹤使用量,以確保其不超過 ResourceQuota 中定義的硬性資源限制。

  • 如果建立或更新資源違反配額約束,則請求將失敗,並顯示 HTTP 狀態碼 403 FORBIDDEN 以及說明將會違反的約束的訊息。

  • 如果在命名空間中針對 cpumemory 等運算資源啟用配額,使用者必須為這些值指定請求或限制;否則,配額系統可能會拒絕 Pod 建立。提示:使用 LimitRanger 許可控制器,針對未提出任何運算資源需求的 Pod 強制執行預設值。

    請參閱 逐步解說,以取得如何避免此問題的範例。

ResourceQuota 物件的名稱必須是有效的 DNS 子網域名稱

以下是可以使用命名空間和配額建立的策略範例

  • 在一個容量為 32 GiB RAM 和 16 個核心的叢集中,讓團隊 A 使用 20 GiB 和 10 個核心,讓團隊 B 使用 10 GiB 和 4 個核心,並保留 2 GiB 和 2 個核心作為未來分配的預留空間。
  • 限制 "testing" 命名空間使用 1 個核心和 1 GiB RAM。讓 "production" 命名空間使用任何數量。

如果叢集的總容量小於命名空間配額的總和,則可能會發生資源爭用。這將以先到先得的方式處理。

爭用或配額變更都不會影響已建立的資源。

啟用資源配額

許多 Kubernetes 發行版本預設啟用 ResourceQuota 支援。當 API 伺服器--enable-admission-plugins= 標誌包含 ResourceQuota 作為其參數之一時,即會啟用。

當特定命名空間中存在 ResourceQuota 時,就會在該命名空間中強制執行資源配額。

計算資源配額

您可以限制在給定命名空間中可以請求的 計算資源總和。

支援以下資源類型

資源名稱描述
limits.cpu在所有非終止狀態的 Pod 中,CPU 限制的總和不能超過此值。
limits.memory在所有非終止狀態的 Pod 中,記憶體限制的總和不能超過此值。
requests.cpu在所有非終止狀態的 Pod 中,CPU 請求的總和不能超過此值。
requests.memory在所有非終止狀態的 Pod 中,記憶體請求的總和不能超過此值。
hugepages-<size>在所有非終止狀態的 Pod 中,指定大小的巨頁請求數量不能超過此值。
cpurequests.cpu 相同
memoryrequests.memory 相同

擴充資源的資源配額

除了上述資源外,在 1.10 版本中,還新增了對 擴充資源的配額支援。

由於擴充資源不允許超量配置 (overcommit),因此在配額中為同一個擴充資源同時指定 requestslimits 是沒有意義的。因此,對於擴充資源,只允許使用帶有 requests. 前綴的配額項目。

以 GPU 資源為例,如果資源名稱是 nvidia.com/gpu,並且您想要將命名空間中請求的 GPU 總數限制為 4 個,您可以如下定義配額

  • requests.nvidia.com/gpu: 4

有關更多詳細資訊,請參閱 檢視和設定配額

儲存資源配額

您可以限制在給定命名空間中可以請求的 儲存資源總和。

此外,您可以根據相關聯的 storage-class 限制儲存資源的消耗。

資源名稱描述
requests.storage在所有持久卷聲明中,儲存請求的總和不能超過此值。
persistentvolumeclaims可以在命名空間中存在的 PersistentVolumeClaims 總數。
<storage-class-name>.storageclass.storage.k8s.io/requests.storage在所有與 <storage-class-name> 相關聯的持久卷聲明中,儲存請求的總和不能超過此值。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims在所有與 <storage-class-name> 相關聯的持久卷聲明中,可以在命名空間中存在的 持久卷聲明 總數。

例如,如果您想要將使用 gold StorageClass 的儲存空間與 bronze StorageClass 的儲存空間分開配額,您可以如下定義配額

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

在 1.8 版本中,本機臨時儲存空間的配額支援作為 alpha 功能新增。

資源名稱描述
requests.ephemeral-storage在命名空間中的所有 Pod 中,本機臨時儲存空間請求的總和不能超過此值。
limits.ephemeral-storage在命名空間中的所有 Pod 中,本機臨時儲存空間限制的總和不能超過此值。
ephemeral-storagerequests.ephemeral-storage 相同。

物件計數配額

您可以使用以下語法,在 Kubernetes API 中為*特定資源類型的總數*設定配額

  • count/<resource>.<group> 用於來自非核心群組的資源
  • count/<resource> 用於來自核心群組的資源

以下是用戶可能希望置於物件計數配額下的資源範例集

  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/replicationcontrollers
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch

如果您以這種方式定義配額,它將適用於 Kubernetes API 伺服器的一部分 API,以及任何由 CustomResourceDefinition 支援的自訂資源。如果您使用 API 聚合 來新增未定義為 CustomResourceDefinition 的其他自訂 API,則核心 Kubernetes 控制平面不會對聚合 API 強制執行配額。擴充 API 伺服器應在自訂 API 適用時提供配額強制執行。例如,若要在 example.com API 群組中的 widgets 自訂資源上建立配額,請使用 count/widgets.example.com

當使用此類資源配額(幾乎適用於所有物件類型)時,如果物件類型存在(已在控制平面中定義),則物件將計入配額。這些類型的配額可用於防止儲存資源耗盡。例如,您可能想要限制伺服器中 Secrets 的數量,因為它們的體積很大。叢集中過多的 Secrets 實際上可能會阻止伺服器和控制器啟動。您可以為 Jobs 設定配額,以防止配置不當的 CronJob。在命名空間中建立過多 Jobs 的 CronJob 可能會導致阻斷服務 (DoS)。

還有另一種語法僅用於為某些資源設定相同類型的配額。支援以下類型

資源名稱描述
configmaps可以在命名空間中存在的 ConfigMaps 總數。
persistentvolumeclaims可以在命名空間中存在的 PersistentVolumeClaims 總數。
pods可以在命名空間中存在的非終止狀態 Pod 的總數。如果 .status.phase in (Failed, Succeeded) 為 true,則 Pod 處於終止狀態。
replicationcontrollers可以在命名空間中存在的 ReplicationControllers 總數。
resourcequotas可以在命名空間中存在的 ResourceQuotas 總數。
services可以在命名空間中存在的 Services 總數。
services.loadbalancers可以在命名空間中存在的 LoadBalancer 類型 Services 的總數。
services.nodeports可以分配給 NodePortLoadBalancer 類型 Services 的 NodePorts 總數,且這些 Services 可以在命名空間中存在。
secrets可以在命名空間中存在的 Secrets 總數。

例如,pods 配額會計數並強制限制在單個命名空間中建立的非終止 pods 數量上限。您可能需要在命名空間上設定 pods 配額,以避免使用者建立過多小型 Pod 並耗盡叢集 Pod IP 供應的情況。

您可以在 檢視和設定配額 中找到更多範例。

配額範圍

每個配額都可以有一組相關聯的 scopes。只有當資源符合列舉範圍的交集時,配額才會衡量該資源的使用量。

當將範圍新增至配額時,它會將其支援的資源數量限制為與該範圍相關的資源。在配額上指定的資源如果超出允許的集合,將會導致驗證錯誤。

範圍描述
Terminating(終止中)匹配 .spec.activeDeadlineSeconds >= 0 的 Pod
NotTerminating(非終止中)匹配 .spec.activeDeadlineSeconds 為 nil 的 Pod
BestEffort(盡力而為)匹配具有盡力而為服務品質的 Pod。
NotBestEffort(非盡力而為)匹配不具有盡力而為服務品質的 Pod。
PriorityClass匹配參考指定 優先順序類別 的 Pod。
CrossNamespacePodAffinity(跨命名空間 Pod 親和性)匹配具有跨命名空間 Pod (反)親和性條款的 Pod。

BestEffort 範圍將配額限制為追蹤以下資源

  • pods

TerminatingNotTerminatingNotBestEffortPriorityClass 範圍將配額限制為追蹤以下資源

  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

請注意,您不能在同一個配額中同時指定 TerminatingNotTerminating 範圍,也不能在同一個配額中同時指定 BestEffortNotBestEffort 範圍。

scopeSelectoroperator 欄位中支援以下值

  • In(在其中)
  • NotIn(不在其中)
  • Exists(存在)
  • DoesNotExist(不存在)

當在定義 scopeSelector 時,使用以下其中一個值作為 scopeName 時,operator 必須是 Exists

  • Terminating(終止中)
  • NotTerminating(非終止中)
  • BestEffort(盡力而為)
  • NotBestEffort(非盡力而為)

如果 operatorInNotIn,則 values 欄位必須至少有一個值。例如

  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

如果 operatorExistsDoesNotExist,則 *不應* 指定 values 欄位。

每個 PriorityClass 的資源配額

功能狀態: Kubernetes v1.17 [穩定]

可以使用特定的 優先順序建立 Pod。您可以透過在配額規範中使用 scopeSelector 欄位,根據 Pod 的優先順序來控制 Pod 對系統資源的消耗。

只有當配額規範中的 scopeSelector 選取了 Pod 時,配額才會被匹配和消耗。

當使用 scopeSelector 欄位將配額範圍限定為優先順序類別時,配額物件會被限制為僅追蹤以下資源

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

此範例建立了一個配額物件,並將其與具有特定優先順序的 Pod 進行匹配。範例運作方式如下

  • 叢集中的 Pod 具有三個優先順序類別之一:「low」、「medium」、「high」。
  • 為每個優先順序建立一個配額物件。

將以下 YAML 儲存到檔案 quota.yml

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "1000"
      memory: 200Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "10"
      memory: 20Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["low"]

使用 kubectl create 套用 YAML。

kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

使用 kubectl describe quota 驗證 Used 配額為 0

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

建立一個優先順序為「high」的 Pod。將以下 YAML 儲存到檔案 high-priority-pod.yml

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

使用 kubectl create 套用它。

kubectl create -f ./high-priority-pod.yml

驗證「high」優先順序配額 pods-high 的「Used」統計資訊已變更,而其他兩個配額保持不變。

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

跨命名空間 Pod 親和性配額

功能狀態: Kubernetes v1.24 [穩定]

運算子可以使用 CrossNamespacePodAffinity 配額範圍來限制哪些命名空間允許擁有具有跨命名空間親和性條款的 Pod。具體來說,它控制哪些 Pod 允許在 Pod 親和性條款中設定 namespacesnamespaceSelector 欄位。

阻止使用者使用跨命名空間親和性條款可能是必要的,因為具有反親和性約束的 Pod 可能會阻止來自所有其他命名空間的 Pod 在故障域中進行排程。

透過使用此範圍,運算子可以透過在該命名空間中建立具有 CrossNamespacePodAffinity 範圍且硬性限制為 0 的資源配額物件,來防止某些命名空間(以下範例中的 foo-ns)擁有使用跨命名空間 Pod 親和性的 Pod。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0"
  scopeSelector:
    matchExpressions:
    - scopeName: CrossNamespacePodAffinity
      operator: Exists

如果運算子想要預設不允許使用 namespacesnamespaceSelector,而僅允許特定命名空間使用,他們可以透過將 kube-apiserver 標誌 --admission-control-config-file 設定為以下組態檔案的路徑,將 CrossNamespacePodAffinity 配置為受限資源。

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 只有在其建立所在的命名空間具有 CrossNamespacePodAffinity 範圍且硬性限制大於或等於使用這些欄位的 Pod 數量的資源配額物件時,才能在 Pod 親和性中使用 namespacesnamespaceSelector

請求與限制的比較

在分配計算資源時,每個容器可以為 CPU 或記憶體指定請求和限制值。配額可以配置為配額其中一個值。

如果配額為 requests.cpurequests.memory 指定了值,則它要求每個傳入的容器都必須對這些資源提出明確的請求。如果配額為 limits.cpulimits.memory 指定了值,則它要求每個傳入的容器都必須為這些資源指定明確的限制。

檢視和設定配額

kubectl 支援建立、更新和檢視配額

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

kubectl 也支援使用 count/<resource>.<group> 語法,針對所有標準命名空間資源的物件計數配額。

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

配額和叢集容量

ResourceQuotas 與叢集容量無關。它們以絕對單位表示。因此,如果您向叢集新增節點,這*不會*自動讓每個命名空間都能夠消耗更多資源。

有時可能需要更複雜的策略,例如

  • 在多個團隊之間按比例分配叢集總資源。
  • 允許每個租戶根據需要增加資源使用量,但設定寬裕的限制以防止意外的資源耗盡。
  • 偵測來自一個命名空間的需求,新增節點,並增加配額。

可以使用 ResourceQuotas 作為建構區塊來實作此類策略,方法是編寫一個「控制器」,該控制器監控配額使用情況,並根據其他訊號調整每個命名空間的配額硬性限制。

請注意,資源配額劃分了彙總的叢集資源,但它沒有對節點建立任何限制:來自多個命名空間的 Pod 可能會在同一個節點上執行。

預設限制 Priority Class 消耗量

可能會希望只有在存在匹配的配額物件時,才允許在命名空間中使用具有特定優先順序(例如「cluster-services」)的 Pod。

透過此機制,運算子可以限制特定高優先順序類別的使用範圍,使其僅限於少數命名空間,而非預設情況下每個命名空間都能取用這些優先順序類別。

為了強制執行此操作,應使用 kube-apiserver 旗標 --admission-control-config-file 傳遞至以下組態檔的路徑

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: PriorityClass
        operator: In
        values: ["cluster-services"]

接著,在 kube-system 命名空間中建立資源配額物件

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-cluster-services
spec:
  scopeSelector:
    matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created

在此情況下,Pod 建立將在以下情況允許:

  1. 未指定 Pod 的 priorityClassName
  2. Pod 的 priorityClassName 指定為 cluster-services 以外的值。
  3. Pod 的 priorityClassName 設定為 cluster-services,且將在 kube-system 命名空間中建立,並已通過資源配額檢查。

若 Pod 建立請求的 priorityClassName 設定為 cluster-services,且將在 kube-system 以外的命名空間中建立,則該請求會被拒絕。

下一步

上次修改時間:2024 年 12 月 04 日下午 3:45 PST:Clean up a policy concept page: resource-quotas (40e5543854)