【kubernetes系列】Kubernetes之资源限制ResourceQuota

概述

当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量。我们可以通过ResourceQuota来解决这个问题,对每个namespace的资源消耗总量提供限制。它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命名空间中的 Pod 可以使用的计算资源的总上限。

资源配额的工作方式如下:

  • 不同的团队在不同的namespace下工作,这可以通过 RBAC 强制执行。
  • 管理员为每个namespace创建一个或多个ResourceQuota对象。
  • 用户在相应的namespace中创建资源时,quota 配额系统跟踪使用情况,来确保其不超过资源配额中定义的硬性资源限额
  • 如果资源的创建或更新违反了配额约束,则请求会失败,并返回 HTTP状态码 403 FORBIDDEN ,以及说明违反配额约束的信息。
  • 如果namespace下的计算资源 (如 cpu 和 memory)的配额被启用,则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝Pod的创建。
  • 资源配额的更改不会影响到已经创建的pod。

提示: 可使用 LimitRange 准入控制器来为没有设置计算资源需求的Pod设置默认值。
ResourceQuota官方文档地址https://kubernetes.io/zh-cn/docs/concepts/policy/resource-quotas/

启用资源配额

资源配额的支持在很多Kubernetes版本中是默认开启的。当 apiserver 的 --admission-control= 参数中包含 ResourceQuota 时,资源配额会被启用。当namespace中存在一个 ResourceQuota 对象时,该namespace即开始实施资源配额管理。

配额分类

计算资源配额

用户可以对指定namespace下的计算资源可使用总量进行限制。目前配额机制所支持的资源类型如下:

资源名称 描述
cpu 所有非终止状态的Pod中,其CPU需求(request)总量上限
limits.cpu 所有非终止状态的Pod中,其CPU限额(limit)总量上限
limits.memory 所有非终止状态的Pod中,其内存限额总量上限
memory 所有非终止状态的Pod中,其内存需求总量上限
requests.cpu 所有非终止状态的Pod中,其CPU需求总量上限
requests.memory 所有非终止状态的Pod中,其内存需求总量上限
requests.nvidia.com/gpu 所有非终止状态的Pod中,其GPU资源总数使请求上限

存储资源配额

用户可以对指定namespace下的存储资源总量进行限制。此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。

资源名称 描述
requests.storage 所有的PVC中,存储资源的需求声明总上限
persistentvolumeclaims namespace中所允许的 PVC个数上限
.storageclass.storage.k8s.io/requests.storage 所有该storage-class-name相关的PVC中, 存储资源的需求声明总上限
.storageclass.storage.k8s.io/persistentvolumeclaims namespace中所允许的 PVC个数上限

对象数量配额

资源名称 描述
configmaps 在该命名空间中允许存在的 ConfigMap 总数上限
persistentvolumeclaims 在该命名空间中允许存在的 PVC 的总数上限
pods 在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真
replicationcontrollers 在该命名空间中允许存在的 ReplicationController 总数上限
resourcequotas 在该命名空间中允许存在的 ResourceQuota 总数上限
services 在该命名空间中允许存在的 Service 总数上限
services.loadbalancers 在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限
services.nodeports 在该命名空间中允许存在的 NodePort 类型的 Service 总数上限
secrets 在该命名空间中允许存在的 Secret 总数上限

配额作用域

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

范围 描述
Terminating 匹配 spec.activeDeadlineSeconds >= 0 的pod。
NotTerminating 匹配 spec.activeDeadlineSeconds is nil 的pod。
BestEffort 匹配"尽力而为(best effort)"服务类型的pod。
NotBestEffort 匹配非"尽力而为(best effort)"服务类型的pod。
PriorityClass 匹配所有引用了所指定的优先级类的 Pods。
CrossNamespacePodAffinity 匹配那些设置了跨名字空间 (反)亲和性条件的 Pod。

BestEffort 作用域限制配额跟踪以下资源: pods
而Terminating、 NotTerminating 和 NotBestEffort 限制配额跟踪以下资源:

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

需要注意的是,不可以在同一个配额对象中同时设置 Terminating 和 NotTerminating 作用域,也不可以在同一个配额中同时设置 BestEffort 和 NotBestEffort 作用域。
scopeSelector 支持在 operator 字段中使用以下值:

  • In
  • NotIn
  • Exists
  • DoesNotExist
    定义 scopeSelector 时,如果使用以下值之一作为 scopeName 的值,则对应的 operator 只能是 Exists。
  • Terminating
  • NotTerminating
  • BestEffort
  • NotBestEffort

如果 operator 是 In 或 NotIn 之一,则 values 字段必须至少包含一个值。 例如:

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

而如果 operator 为 Exists 或 DoesNotExist,则不可以设置 values 字段。

示例

[root@k8s-m1 k8s-resource]# cat resource-list.yaml 
apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "100"
      memory: 50Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "60"
      memory: 30Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "40"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["low"]

[root@k8s-m1 k8s-resource]# kubectl apply -f resource-list.yaml 
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created
[root@k8s-m1 k8s-resource]# kubectl get resourcequotas 
NAME          AGE   REQUEST                                  LIMIT
pods-high     5s    cpu: 0/100, memory: 0/50Gi, pods: 0/10   
pods-low      5s    cpu: 0/40, memory: 0/10Gi, pods: 0/10    
pods-medium   5s    cpu: 0/60, memory: 0/30Gi, pods: 0/10    
[root@k8s-m1 k8s-resource]# kubectl describe quota 
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     100
memory      0     50Gi
pods        0     10


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


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

#priorityclass也需要自己手动创建,系统默认自带了两个
[root@k8s-m1 k8s-resource]# cat priorityclass.yaml 
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
  name: high
value: 10000   
globalDefault: true  
description: "high priority"

[root@k8s-m1 k8s-resource]# kubectl apply  -f priorityclass.yaml 
Warning: scheduling.k8s.io/v1beta1 PriorityClass is deprecated in v1.14+, unavailable in v1.22+; use scheduling.k8s.io/v1 PriorityClass
priorityclass.scheduling.k8s.io/high created

[root@k8s-m1 k8s-resource]# kubectl get priorityclasses.scheduling.k8s.io 
NAME                      VALUE        GLOBAL-DEFAULT   AGE
high                      10000        true             14s
system-cluster-critical   2000000000   false            431d
system-node-critical      2000001000   false            431d

[root@k8s-m1 k8s-resource]# cat higi-priority-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: high-priority-pod
spec:
  containers:
  - name: high-priority
    image: centos:7
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "2Gi"
        cpu: "500m"
      limits:
        memory: "4Gi"
        cpu: "500m"
  priorityClassName: high

[root@k8s-m1 k8s-resource]# kubectl apply -f higi-priority-pod.yaml 
pod/high-priority created

[root@k8s-m1 k8s-resource]# kubectl describe resourcequotas pods-high
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  100
memory      2Gi   50Gi
pods        1     10

请求/约束

分配计算资源时,可以为每个容器指定CPU或内存请求和约束,也可以设置两者中的任何一个。
如果配额中指定了 requests.cpu 或 requests.memory 的值,那么它要求每个进来的容器针对这些资源有明确的请求。 如果配额中指定了 limits.cpu 或 limits.memory的值,那么它要求每个进来的容器针对这些资源指定明确的约束。

示例

[root@k8s-m1 k8s-resource]# kubectl create namespace myspace
[root@k8s-m1 k8s-resource]# cat compute-resources.yaml 
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
  namespace: myspace
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.nvidia.com/gpu: 4

[root@k8s-m1 k8s-resource]# cat object-counts.yaml 
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
  namespace: myspace
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"

[root@k8s-m1 k8s-resource]# kubectl create -f compute-resources.yaml 
resourcequota/compute-resources created 
[root@k8s-m1 k8s-resource]# kubectl create -f object-counts.yaml 
resourcequota/object-counts created

[root@k8s-m1 k8s-resource]# kubectl get quota --namespace=myspace
NAME                AGE   REQUEST                                                                                                                                              LIMIT
compute-resources   15s   requests.cpu: 0/1, requests.memory: 0/1Gi, requests.nvidia.com/gpu: 0/4                                                                              limits.cpu: 0/2, limits.memory: 0/2Gi
object-counts       3s    configmaps: 0/10, persistentvolumeclaims: 0/4, pods: 0/4, replicationcontrollers: 0/20, secrets: 1/10, services: 0/10, services.loadbalancers: 0/2   

创建nginx测试,如果pod所需资源超过resourcequota设置的pod将不能正常创建出来

[root@k8s-m1 k8s-resource]# kubectl apply  -f nginx-resourcequota-deployment.yml 
deployment.apps/my-nginx created
[root@k8s-m1 k8s-resource]# cat nginx-resourcequota-deployment.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  namespace: myspace
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      tier: frontend
  replicas: 2
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
        - name: nginx-gateway
          image: nginx
          resources:
            requests:
              cpu: 1
              memory: 1Gi
            limits:
              cpu: 1
              memory: 1Gi
          ports:
            - containerPort: 80

[root@k8s-m1 k8s-resource]# kubectl apply  -f nginx-resourcequota-deployment.yml 
deployment.apps/my-nginx created

[root@k8s-m1 k8s-resource]# kubectl get pod -n myspace 
NAME                        READY   STATUS    RESTARTS   AGE
my-nginx-5c45dd8bc5-7zhw5   1/1     Running   0          83s
#只创建出来一个pod,而预期是2个

[root@k8s-m1 k8s-resource]# kubectl describe replicasets.apps  -n myspace my-nginx-5c45dd8bc5 
Name:           my-nginx-5c45dd8bc5
Namespace:      myspace
Selector:       pod-template-hash=5c45dd8bc5,tier=frontend
Labels:         pod-template-hash=5c45dd8bc5
                tier=frontend
Annotations:    deployment.kubernetes.io/desired-replicas: 2
                deployment.kubernetes.io/max-replicas: 3
                deployment.kubernetes.io/revision: 1
Controlled By:  Deployment/my-nginx
Replicas:       1 current / 2 desired
Pods Status:    1 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  pod-template-hash=5c45dd8bc5
           tier=frontend
  Containers:
   nginx-gateway:
    Image:      nginx
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     1
      memory:  1Gi
    Requests:
      cpu:        1
      memory:     1Gi
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type             Status  Reason
  ----             ------  ------
  ReplicaFailure   True    FailedCreate
Events:
  Type     Reason            Age                From                   Message
  ----     ------            ----               ----                   -------
  Normal   SuccessfulCreate  98s                replicaset-controller  Created pod: my-nginx-5c45dd8bc5-7zhw5
  Warning  FailedCreate      98s                replicaset-controller  Error creating: pods "my-nginx-5c45dd8bc5-5hjqq" is forbidden: exceeded quota: compute-resources, requested: requests.cpu=1,requests.memory=1Gi, used: requests.cpu=1,requests.memory=1Gi, limited: requests.cpu=1,requests.memory=1Gi

可以看到,由于所需资源已经超出当前命名空间设置的resourcequota,pod将不能正常创建。

更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出

你可能感兴趣的:(Kubernetes,kubernetes)