k8s中 資源配額 ResourceQuota


文章轉載自:https://www.kuboard.cn/learning/k8s-advanced/policy/lr.html

當多個用戶(團隊)共享一個節點數量有限的集群時,如何在多個用戶(團隊)之間分配集群的資源就會變得非常重要。Resource quota 的用途便在於此。

資源配額

資源配額(Resource quota)通過 ResourceQuota 對象定義,可以限定單個名稱空間中可使用的計算資源的總量。限定的方式有:

  • 按對象類型限定名稱空間中可創建的對象的總數
  • 按對象類型限定名稱空間中可消耗的計算資源

資源配額(Resource quota)的工作方式描述如下:

  • 不同的用戶(團隊)使用不同的名稱空間。如果通過 ACL(權限控制),可以強制用戶只能訪問特定的名稱空間
  • 集群管理員為每個名稱空間創建一個 ResourceQuota 對象
  • 用戶在名稱空間中創建對象(Pod、Service等),ResourceQuota 系統跟蹤對象的資源使用情況,並確保不會超過 ResourceQuota 對象中定義的配額
  • 如果創建或更新對象時與 ResourceQuota 沖突,則 apiserver 會返回 HTTP 狀態碼 403,以及對應的錯誤提示信息
  • 如果在名稱空間中為計算資源 CPU 和 內存 激活 ResourceQuota,用戶在創建對象(Pod、Service等)時,必須指定 requests 和 limits。

使用 LimitRange 可以為沒有定義 requests、limits 的對象強制添加默認值

下面是一些使用 ResourceQuota 的場景描述:

  • 在一個總容量為 32GiB 內存、16核CPU 的集群里,允許 teamA 使用 20GiB內存、10核CPU,允許 teamB 使用 10GiB 內存、4核CPU,保留 2GiB 內存和 2核CPU 待將來分配
  • 限定 “Testing” 名稱空間使用 1核CPU、1GiB內存,允許 “Production” 名稱空間使用任意數量的計算資源

當集群中總的容量小於名稱空間資源配額的總和時,可能會發生資源爭奪。此時 Kubernetes 集群將按照先到先得的方式分配資源。

無論是資源爭奪還是修改名稱空間的資源配額(ResourceQuota),都不會影響到已經創建的對象。

啟用ResourceQuota

Kubernetes集群中默認啟用 ResourceQuota。如果沒有,可在啟動 apiserver 時為參數 --enable-admission-plugins 添加 ResourceQuota 配置項。

在名稱空間中定義一個 ResourceQuota 對象,就可以激活該名稱空間的資源配額檢查。

資源類型

當多個用戶(團隊)共享一個節點數量有限的集群時,如何在多個用戶(團隊)之間分配集群的資源就會變得非常重要。Resource quota 的用途便在於此。本文主要探索通過 ResourceQuota 限定名稱空間的計算資源配額、存儲資源配額、對象數量配額。

計算資源配額

通過 ResourceQuota 可以限定名稱空間中可以使用的 計算資源 的總量。支持的計算資源定義類型如下:

資源名稱 描述
limits.cpu 名稱空間中,所有非終止狀態(non-terminal)的 Pod 的 CPU限制 resources.limits.cpu 之和不能超過此值
limits.memory 名稱空間中,所有非終止狀態(non-terminal)的 Pod 的內存限制 resources.limits.memory 之和不能超過此值
requests.cpu 名稱空間中,所有非終止狀態(non-terminal)的 Pod 的 CPU請求 resources.requrest.cpu 之和不能超過此值
requests.memory 名稱空間中,所有非終止狀態(non-terminal)的 Pod 的 CPU請求 resources.requests.memory 之和不能超過此值

存儲資源配額

通過 ResourceQuota 可以:

  • 限定名稱空間中可以使用的 存儲資源 的總量
  • 限定名稱空間中可以使用的某個 存儲類 存儲資源的總量
資源名稱 描述
requests.storage 名稱空間中,所有存儲卷聲明(PersistentVolumeClaim)請求的存儲總量不能超過此值
persistentvolumeclaims 名稱空間中,可以創建的 存儲卷聲明(PersistentVolumeClaim)的總數不能超過此值
.storageclass
.storage.k8s.io/requests.storage 名稱空間中,所有與指定存儲類(StorageClass)關聯的存儲卷聲明(PersistentVolumeClaim)請求的存儲總量不能超過此值
.storageclass
.storage.k8s.io/persistentvolumeclaims 名稱空間中,所有與指定存儲類(StorageClass)關聯的存儲卷聲明(PersistentVolumeClaim)的總數不能超過此值

例如,假設管理員想要對存儲類 gold 和存儲類 bronze 做不同的配額限定,那么,可以按如下方式定義 ResourceQuota:

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

在 Kubernetes v1.8 中,引入了本地短時存儲(local ephemeral storage)的資源配額設置 (Alpha)

資源名稱 描述
requests.ephemeral-storage 名稱空間中,所有 Pod 的本地短時存儲(local ephemeral storage)請求的總和不能超過此值
limits.ephemeral-storage 名稱空間中,所有 Pod 的本地短時存儲(local ephemeral storage)限定的總和不能超過此值

對象數量配額

從 Kubernetes v1.9 開始,支持使用如下格式的限定名稱空間中標准類型對象的總數量:

  • count/ .

下面是一些例子:

  • 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
  • count/deployments.extensions

Kubernetes v1.15 開始,支持使用相同的格式限定名稱空間中自定義資源(CustomResource)對象的總量。例如,為 example.com API group 中的自定義資源(CustomResource) widgets 限定對象數量總數的配額,可以使用 count/widgets.example.com。

當使用 count/* 的方式限定對象總數的配額時,只要對象存儲在 apiserver 中,無論其狀態如何,該對象就被計數。 此類配額限定可以保護 apiserver 的存儲空間不被過度消耗。例如,

  • 您可能需要限定名稱空間中 Secret 的總數,因為他們通常占用的存儲空間比較大。集群中如果存在大量的 Secret 對象,可能會導致 apiserver 或者控制器(Controller)啟動失敗
  • 您可能也需要限定名稱空間中 Job 對象的個數,以避免某個配置錯誤的 cronjob 創建了太多的 Job,造成了拒絕服務(denial of service)的情況

作用域

每個 ResourceQuota 對象都可以綁定一組作用域,當 Kubernetes 對象與此 ResourceQuota 的作用域匹配(在作用域中)時,ResourceQuota 的限定才對該對象生效。

Scope(作用域) 描述
Terminating 包含所有 .spec.activeDeadlineSeconds >= 0 的 Pod
NotTerminating 包含所有 .spec.activeDeadlineSeconds is nil 的Pod
BestEffort 包含所有服務等級(quality of service)為 BestEffort 的 Pod
NotBestEffort 包含所有服務等級(quality of service)為 NotBestEffort 的 Pod
  • 帶有 BestEffort 作用域的 ResourceQuota 關注點為: Pod
  • 帶有 Terminating、NotTerminating、 NotBestEffort 的作用域關注點為:
    • cpu
    • limits.cpu
    • limits.memory
    • memory
    • pods
    • requests.cpu
    • requests.memory

按PriorityClass設定ResourceQuota

創建 Pod 時,可以指定 priority

(opens new window)。使用 ResourceQuota 的 .spec.scopeSelector 字段將 ResourceQuota 和 Pod 的 priority 關聯,進而限定 Pod 的資源消耗。

只有當 ResourceQuota 的 .spec.scopeSelector 字段與 Pod 的 priorty 字段匹配時,ResourceQuota 才生效。

下面的例子創建了一個通過 priority 限定特定 Pod 的 ResourceQuota 對象,該例子的工作方式如下:

  • 假設集群中的 Pod 可以被指定三種 priority class: low、medium、high
  • 集群中為每個 Priority 都創建了一個 ResourceQuota 對象

定義 ResourceQuota 對象的文件如下所示:

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"]

執行命令以創建 ResourceQuota:

kubectl create -f https://kuboard.cn/statics/learning/policy/rq-scope-quota.yaml

resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

執行如下命令驗證 quota 的使用為 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” priority Pod,YAML 文件如下所示:

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 -f https://kuboard.cn/statics/learning/policy/rq-scope-high-priority-pod.yaml

驗證 "high" priority 對應的 ResourceQuota pods-high 的 Used 統計結果,可以發現 pods-heigh 的配額已經被使用,而其他兩個的配額則沒有被使用。

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

scopeSelector.matchExpressions.operator 字段中,可以使用如下幾種取值:

  • In
  • NotIn
  • Exist
  • DoesNotExist

Requests vs Limits

Kubernetes 中,在為容器分配計算資源時,每一個容器都可以指定 resources.limits.cpu、resources.limits.memory、resources.requests.cpu、resources.requests.memory。
ResourceQuota可以為 limits 和 requests 各自設定資源配額。

  • 如果 ResourceQuota 指定了 requests.cpu 或者 requests.memory,此時任何新建的容器都必須明確指定自己的 requests.cpu、requests.memory。
  • 如果 ResourceQuota 指定了 limits.cpu 或者 limits.memory,此時任何新建的容器都必須明確指定自己的 limits.cpu、limits.memory。

查看和設定ResourceQuota

使用 kubectl 可以查看和設定 ResourceQuota:

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.extensions=2,count/replicasets.extensions=4,count/pods=3,count/secrets=4 --namespace=myspace
  
kubectl run nginx --image=nginx --replicas=2 --namespace=myspace

kubectl describe quota --namespace=myspace

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

ResourceQuota和Cluster Capacity

ResourceQuota 與 Cluster Capacity 相互獨立,都使用絕對值來標識其大小(而不是百分比)。如果您想集群中添加節點,並不會自動使其中任何一個名稱空間的可用資源配額發生變化。

某些情況下,需要更加復雜的策略配置,例如:

  • 在多個團隊之間按比例切分集群的資源
  • 允許每一個租戶按需增加資源使用,但是又有合適的限定以避免資源耗盡的情況發生
  • 根據某個名稱空間的實際需要,增加節點,並提高為其提高資源配額

要實現這些策略,可以使用 ResourceQuota 作為基礎,編寫自己的控制器來監聽資源配額的使用情況,並根據具體的情況動態調整名稱空間的 ResourceQuota。

盡管 ResourceQuota 可以將集群中的資源配額分配到名稱空間,但是它並不對節點做任何限定,不同名稱空間的 Pod 可以運行在同一個節點上。

限定Priority Class的默認資源消耗

某些情況下我們可能需要做如下設定:某個特定 priority 的 Pod(例如,cluster-services)當且僅當名稱空間中存在匹配的 ResourceQuota 時才可以創建。

使用這樣的機制,集群管理員可以限定某些特別的 priority class 只在指定的名稱空間中使用。

如果要激活此特性,您需要將如下配置文件的路徑通過 --admission-control-config-file 參數指定到 kube-apiserver 的啟動參數中:

apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: resourcequota.admission.k8s.io/v1beta1
    kind: Configuration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass 
        operator: In
        values: ["cluster-services"]

完成此配置后,cluster-services priority 的 Pod 將只能在帶有對應 scopeSelector 的 ResourceQuota 的名稱空間中創建,例如:

    scopeSelector:
      matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

CPU/內存資源限額

本文通過實例演示了如何通過ResourceQuota為名稱空間配置CPU和內存的資源限額。

創建名稱空間

執行如下命令,創建名稱空間:

kubectl create namespace quota-mem-cpu-example

創建ResourceQuota

下面是 ResourceQuota 的YAML文件:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

執行命令以創建該 ResourceQuota:

kubectl apply -f https://kuboard.cn/statics/learning/policy/rq-mem-cpu-quota.yaml --namespace=quota-mem-cpu-example

執行命令查看剛創建的 ResourceQuota:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

ResourceQuota 為 quota-mem-cpu-example 名稱空間設定了如下資源配額:

  • 每一個容器必須有 內存請求(request)、內存限制(limit)、CPU請求(request)、CPU限制(limit)
  • 所有容器的內存請求總和不超過 1 GiB
  • 所有容器的內存限定總和不超過 2 GiB
  • 所有容器的CPU請求總和不超過 1 cpu
  • 所有容器的CPU限定總和不超過 2 cpu

創建Pod

下面是一個 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m" 
      requests:
        memory: "600Mi"
        cpu: "400m"

執行命令以創建該 Pod

kubectl apply -f https://kuboard.cn/statics/learning/policy/rq-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

執行命令驗證 Pod 已運行:

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

此時執行命令再次查看名稱空間的資源配額消耗情況:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

輸出結果中除了顯示名稱空間的資源配額之外,同時還顯示了該配額的使用情況。結果如下所示:

status:
  hard:
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.cpu: "1"
    requests.memory: 1Gi
  used:
    limits.cpu: 800m
    limits.memory: 800Mi
    requests.cpu: 400m
    requests.memory: 600Mi

嘗試創建第二個Pod

下面是另外一個 Pod 的 YAML 文件:

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"      
      requests:
        memory: "700Mi"
        cpu: "400m"

在此配置文件中,Pod 請求了 700MiB 的內存,如果加上第一個 Pod 所請求的內存,其結果已經超出了名稱空間的資源配額中對內存請求的限制:600MiB + 600MiB > 1GiB

執行如下命令嘗試創建該 Pod:

kubectl apply -f https://kuboard.cn/statics/learning/policy/rq-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

第二個 Pod 將不能創建成功,該命令的輸出結果將提示創建 Pod 失敗的原因是內存請求之和超過了內存請求的資源配額,錯誤信息如下所示:

Error from server (Forbidden): error when creating "examples/admin/resource/quota-mem-cpu-pod-2.yaml":
pods "quota-mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo,
requested: requests.memory=700Mi,used: requests.memory=600Mi, limited: requests.memory=1Gi

總結

在本文的例子中,您可以使用 ResourceQuota 來限定名稱空間中所有容器的內存請求(request)之和不超過指定的配額。同時也可以設置內存限定(limit)、CPU請求(request)、CPU限定(limit)的資源配額。

如果需要限定單個Pod、容器的資源使用情況,請參考 LimitRange

清理

刪除名稱空間可清理本文所創建的所有內容:

kubectl delete namespace quota-mem-cpu-example

Pod數量限額

本文通過實例演示了如何通過ResourceQuota為名稱空間配置最多可以運行多少個Pod。

創建名稱空間

為本次演示創建名稱空間:

kubectl create namespace quota-pod-example

創建ResourceQuota

為本次演示創建 ResourceQuota 對象,yaml文件如下所示:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-demo
spec:
  hard:
    pods: "2"

執行命令創建該 ResourceQuota

kubectl apply -f https://kuboard.cn/statics/learning/policy/rq-pod-quota.yaml --namespace=quota-pod-example

執行如下命令查看已創建的 ResourceQuota

kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml

輸出結果中顯示了該名稱空間的配額限定了只能創建兩個 Pod,當前沒有任何 Pod 被創建:

spec:
  hard:
    pods: "2"
status:
  hard:
    pods: "2"
  used:
    pods: "0"

創建Pod

創建如下 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-quota-demo
spec:
  selector:
    matchLabels:
      purpose: quota-demo
  replicas: 3
  template:
    metadata:
      labels:
        purpose: quota-demo
    spec:
      containers:
      - name: pod-quota-demo
        image: nginx

該 Deployment 的副本數為 3 replicas: 3,執行命令以創建該 Deployment:

kubectl apply -f https://kuboard.cn/statics/learning/policy/rq-pod-deployment.yaml --namespace=quota-pod-example

執行命令以查看 Deployment 的詳細信息

kubectl get deployment pod-quota-demo --namespace=quota-pod-example --output=yaml

盡管 Deployment 期望的副本數是 3,但是由於名稱空間通過 ResourceQuota 限定了最大的 Pod 數量,因此,最終只有兩個 Pod 被創建成功。輸出結果如下所示:

spec:
  ...
  replicas: 3
...
status:
  availableReplicas: 2
...
lastUpdateTime: 2017-07-07T20:57:05Z
    message: 'unable to create pods: pods "pod-quota-demo-1650323038-" is forbidden:
      exceeded quota: pod-demo, requested: pods=1, used: pods=2, limited: pods=2'

清理

刪除名稱空間可清理本次演示創建的對象:

kubectl delete namespace quota-pod-example


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM