kubernetes调度之资源配额

系列目录

当多个用户或者开发团队共享一个有固定节点的的kubernetes集群时,一个团队或者一个用户使用的资源超过他应当使用的资源是需要关注的问题,资源配额是管理员用来解决这个问题的一个工具.

资源配额,通过ResourceQuota定义,提供了对某一名称空间使用资源的总体约束.它即可以限制这个名称空间下有多少个对象可以被创建,也可以限制对计算机资源使用量的限制(前面说到过,计算机资源包括cpu,内存,磁盘空间等资源)

资源配额通过以下类似方式工作:

  • 不同的团队在不同的名称空间下工作.当前kubernetes并没有强制这样做,完全是自愿的,但是kubernetes团队计划通过acl授权来达到强制这样做.

  • 管理员对每一个名称空间创建一个ResourceQuota(资源配额)

  • 用户在一个名称空间下创建资源(例如pod,service等),配额系统跟踪资源使用量来保证资源的使用不超过ResourceQuota定义的量.

  • 如果对一个资源的创建或者更新违反了资源配额约束,则请求会返回失败,失败的http状态码是403 FORBIDDEN并且有一条消息来解释哪个约束被违反.

  • 如果一个名称空间下的计算机资源配额,比如CPU和内存被启用,则用户必须指定相应的资源申请或者限制的值,否则配额系统可能会阻止pod的创建.

资源配额在某一名称空间下创建策略示例:

  • 在一个有32G内存,16核cpu的集群,让团队A使用20G内存和10核cpu,让团队B使用10G内存和4核cpu,剩余的2G内存和2核cup预留以备进一步分配

  • 限制测试名称空间使用1核1G,让生产名称空间使用剩下的全部资源

当集群的容量小于所有名称空间下配额总和时,将会出现资源竞争,这种情况下kubernetes将会基于先到先分配的原则进行处理

不论是资源竞争或者是资源配额的修改都不会影响已经创建的资源

启用资源配额

很多kubernetes的发行版中资源配额支持默认是开启的,当ResourceQuota作为apiserver的--enable-admission-plugins=的其中一个值时,资源配额被开启.

当某一名称空间包含ResourceQuota对象时资源配额在这个名称空间下生效.

计算机资源配额

你可以限制一个名称空间下可以被申请的计算机资源的总和

kubernetes支持以下资源类型:

Resource Name Description
cpu Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value.
limits.cpu Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value.
limits.memory Across all pods in a non-terminal state, the sum of memory limits cannot exceed this value.
memory Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value.
requests.cpu Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value.
requests.memory Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value.

扩展资源的资源配额

除了上面提到的,在kubernetes 1.10里,添加了对扩展资源的配额支持

存储资源配额

你可以限制某一名称空间下的存储空间总量的申请

此外,你也可以你也可以根据关联的storage-class来限制存储空间资源的使用

Resource Name Description
requests.storage Across all persistent volume claims, the sum of storage requests cannot exceed this value.
persistentvolumeclaims The total number of persistent volume claims that can exist in the namespace.
.storageclass.storage.k8s.io/requests.storage Across all persistent volume claims associated with the storage-class-name, the sum of storage requests cannot exceed this value.
.storageclass.storage.k8s.io/persistentvolumeclaims Across all persistent volume claims associated with the storage-class-name, the total number of persistent volume claims that can exist in the namespace.

例如,一个operator想要想要使黄金和青铜单独申请存储空间,那么这个operator可以像如下一样申请配额:

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

在1.8版本里,对local ephemeral storage配额的的支持被添加到alpha特征里.

Resource Name Description
requests.ephemeral-storage Across all pods in the namespace, the sum of local ephemeral storage requests cannot exceed this value.
limits.ephemeral-storage Across all pods in the namespace, the sum of local ephemeral storage limits cannot exceed this value.

对象数量配额

1.9版本通过以下语法加入了对所有标准名称空间资源类型的配额支持

count/<resource>.<group>

以下是用户可能想要设置对象数量配额的例子:

  • 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

当使用count/*类型资源配额,服务器上存在的资源对象将都被控制.这将有助于防止服务器存储资源被耗尽.比如,如果存储在服务器上的secrets资源对象过大,你可能会想要限制它的数量.过多的secrets可能会导致服务器无法启动!你也可能会限制job的数量以防一些设计拙劣的定时任务会创建过多的job以导致服务被拒绝

以下资源类型的限额是支持的

Resource Name Description
configmaps The total number of config maps that can exist in the namespace.
persistentvolumeclaims The total number of persistent volume claims that can exist in the namespace.
pods The total number of pods in a non-terminal state that can exist in the namespace. A pod is in a terminal state if .status.phase in (Failed, Succeeded) is true.
replicationcontrollers The total number of replication controllers that can exist in the namespace.
resourcequotas The total number of resource quotas that can exist in the namespace.
services The total number of services that can exist in the namespace.
services.loadbalancers The total number of services of type load balancer that can exist in the namespace.
services.nodeports The total number of services of type node port that can exist in the namespace.
secrets The total number of secrets that can exist in the namespace.

例如,pod配额限制了一个名称空间下非terminal状态的pod总数量.这样可以防止一个用户创建太多小的pod以至于耗尽集群分配给pod的所有IP

配额范围

每一个配额都可以包含一系列相关的范围.配额只会在匹配列举出的范围的交集时才计算资源的使用.

当一个范围被添加到配额里,它将限制它支持的,属于范围的资源.指定的资源不在支持的集合里时,将会导致验证错误

Scope Description
Terminating Match pods where .spec.activeDeadlineSeconds >= 0
NotTerminating Match pods where .spec.activeDeadlineSeconds is nil
BestEffort Match pods that have best effort quality of service.
NotBestEffort Match pods that do not have best effort quality of service.

BestEffort范围限制配额只追踪pods资源

Terminating,NotTerminatingNotBestEffort范围限制配额追踪以下资源:

  • cpu

  • limits.cpu

  • limits.memory

  • memory

  • pods

  • requests.cpu

  • requests.memory

每一个PriorityClass的资源配额

此特征在1.12片本中为beta

pod可以以指定的优先级创建.你可以通过pod的优先级来控制pod对系统资源的使用,它是通过配额的spec下的scopeSelector字段产生效果的.

只有当配额spec的scopeSelector选择了一个pod,配额才会被匹配和消费

你在使用PriorityClass的配额的之前,需要启用ResourceQuotaScopeSelectors

以下示例创建一个配额对象,并且一定优先级的pod会匹配它.

  • 集群中的pod有以下三个优先级类之一:low,medium,high

  • 每个优先级类都创建了一个资源配额

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来用户以上yml文件

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

使用kubectl describe quota来查看

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,把以下内容保存在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

这时候再用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支持operator字段的以下值:

  • In

  • NotIn

  • Exist

  • DoesNotExist

配额资源的申请与限制

当分配计算机资源时,每一个容器可能会指定对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:
    pods: "4"
    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"
    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
pods                     0     4
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
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

配额和集群容量

ResourceQuotas独立于集群的容量,它们通过绝对的单位表示.因此,如果你向集群添加了节点,这并不会给集群中的每个名称空间赋予消费更多资源的能力.

有时候需要更为复杂的策略,比如:

  • 把集群中所有的资源按照比例分配给不同团队

  • 允许每个租户根据需求增加资源使用,但是有一个总体的限制以防资源被耗尽

  • 检测名称空间的需求,添加节点,增加配额

这些策略可以通过实现ResourceQuotas来写一个controller用于监视配额的使用,并且通过其它信号来调整每个名称空间的配额

默认限制优先类消费

有时候我们可能希望一定优先级别的pod,例如cluster-services应当被允许在一个名称空间里,当且仅当匹配的配额存在.

通过这种机制,operators可以限制一些高优先级的类只能用于有限数量的名称空间里,并且不是所有的名称空间都可以默认消费它们.

为了使以上生效,kube-apiserver标签--admission-control-config-file应当传入以下配置文件的路径

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类型的pod仅被允许运行在有匹配scopeSelector的配额资源对象的名称空间里,例如

 scopeSelector:
      matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]
``
posted @ 2019-06-16 09:07  周国通  阅读(1780)  评论(0编辑  收藏  举报