根据集群用户数量来调整集群配置,以达到这个目的:能控制特定命名空间中的资源使用量,最终实现集群的公平使用和成本控制。

需要实现的功能如下:

  • 限制运行状态的 Pod 的计算资源用量。
  • 限制持久卷的数量以控制对存储的访问。
  • 限制负载均衡器的数量以控制成本。
  • 防止滥用网络端口这类稀缺资源。
  • 提供默认的计算资源 Requests 以便系统做出更优化的调度。

1. 创建命名空间

创建名为 quota-example 的命名空间,namespace.yaml 文件内容如下:

apiVersion: v1
kind: Namespace
metadata:
  name: quota-example

创建命名空间:

[root@master1 ~]# kubectl create -f namespace.yaml 
namespace/quota-example created

查看命名空间:

[root@master1 ~]# kubectl get namespaces | grep -v 'kube'
NAME              STATUS   AGE
default           Active   17d
quota-example     Active   2m13s

2. 设置限定对象数量的资源配额

通过设置限定对数量的资源配额,可以控制持久存储卷、负载均衡器、NodePort 这些资源的数量。
创建名为 object-counts 的 ResourceQuota,object-counts.yaml 文件内容如下:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    persistentvolumeclaims: "2"
    services.loadbalancers: "2"
    services.nodeports: "0"

创建资源:

[root@master1 ~]# kubectl create -f object-counts.yaml --namespace=quota-example
resourcequota/object-counts created

配额系统会检测到资源项配额的创建,统计和限制该命名空间中的资源消耗。

查看该配置是否生效:

[root@master1 ~]# kubectl describe quota object-counts --namespace=quota-example 
Name:                   object-counts
Namespace:              quota-example
Resource                Used  Hard
--------                ----  ----
persistentvolumeclaims  0     2
services.loadbalancers  0     2
services.nodeports      0     0

至此,配额系统会自动阻止那些使资源用量超过资源配额限定值的请求。

3. 设置限定计算资源的资源配额

创建一项限定计算资源的资源配额,以限制该命名空间中计算资源的使用总量。

创建名为 compute-resources 的 ResourceQuota,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

创建资源:

[root@master1 ~]# kubectl create -f compute-resources.yaml --namespace=quota-example 
resourcequota/compute-resources created

查看该配额是否生效:

[root@master1 ~]# kubectl describe quota compute-resources --namespace=quota-example 
Name:            compute-resources
Namespace:       quota-example
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     2Gi
pods             0     4
requests.cpu     0     1
requests.memory  0     1Gi

配额系统会自动防止在该命名空间中同时拥有超过4个非“终止态”的 Pod。此外,由于该项资源配额限制了 CPU 和 内存的 Limits 和 Requests 总量,因此会强制要求该命名空间中所有的容器都显式定义 CPU 和内存的 Limits、Requests(可使用默认值,Requests 默认等于 Limits)。

4. 配置默认的 Requests 和 Limits

在命名空间已经配置了限定计算资源配额的情况下,如果尝试在该命名空间中创建一个不指定 Requests 和 Limits 的 Pod,那么 Pod 的创建可能会失败。 下面是一个失败的例子。

创建一个 Nginx 的 Deployment:

[root@master1 ~]# kubectl create deployment nginx --image=nginx --replicas=1 --namespace=quota-example
deployment.apps/nginx created

查看创建的 Pod,会发现 Pod 没有创建成功:

[root@master1 ~]# kubectl get pods --namespace=quota-example 
No resources found in quota-example namespace.
[root@master1 ~]# kubectl get deployments --namespace=quota-example 
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   0/1     0            0           2m38s

再查看 Deployment 的详细信息:

[root@master1 ~]# kubectl describe deployments nginx --namespace=quota-example 
Name:                   nginx
Namespace:              quota-example
CreationTimestamp:      Tue, 02 Aug 2022 15:31:30 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=nginx
Replicas:               1 desired | 0 updated | 0 total | 0 available | 1 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type             Status  Reason
  ----             ------  ------
  Progressing      True    NewReplicaSetCreated
  Available        False   MinimumReplicasUnavailable
  ReplicaFailure   True    FailedCreate
OldReplicaSets:    <none>
NewReplicaSet:     nginx-6799fc88d8 (0/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  104s  deployment-controller  Scaled up replica set nginx-6799fc88d8 to 1

该 Deployment 会尝试创建一个 Pod,但是失败,查看其中 ReplicaSet 的详细信息:

[root@master1 ~]# kubectl describe replicasets nginx-6799fc88d8  --namespace=quota-example 
Name:           nginx-6799fc88d8
Namespace:      quota-example
Selector:       app=nginx,pod-template-hash=6799fc88d8
Labels:         app=nginx
                pod-template-hash=6799fc88d8
Annotations:    deployment.kubernetes.io/desired-replicas: 1
                deployment.kubernetes.io/max-replicas: 2
                deployment.kubernetes.io/revision: 1
Controlled By:  Deployment/nginx
Replicas:       0 current / 1 desired
Pods Status:    0 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=nginx
           pod-template-hash=6799fc88d8
  Containers:
   nginx:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type             Status  Reason
  ----             ------  ------
  ReplicaFailure   True    FailedCreate
Events:
  Type     Reason        Age                 From                   Message
  ----     ------        ----                ----                   -------
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-vt4t2" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-xrj66" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-scqxt" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-m672m" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-62pcx" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-j7p8m" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m5s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-5tv47" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m4s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-4dfch" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  4m4s                replicaset-controller  Error creating: pods "nginx-6799fc88d8-hffdc" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
  Warning  FailedCreate  81s (x7 over 4m3s)  replicaset-controller  (combined from similar events): Error creating: pods "nginx-6799fc88d8-p2rmx" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory

提示创建失败,Master 拒绝这个 ReplicaSet 创建 Pod,因为在这个 Pod 中没有指定 CPU 和 内存的 Requests、Limits。

为了避免这种失败,可以使用 LimitRange 为这个命名空间中所有 Pod 都提供一个资源配置的默认值。

创建一个名为 limits 的 LimitRange,limits.yaml 文件内容如下:

apiVersion: v1
kind: LimitRange
metadata:
  name: limits
spec:
  limits:
    - default:
        cpu: 200m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 256Mi
      type: Container

创建资源:

[root@master1 ~]# kubectl create -f limits.yaml --namespace=quota-example 
limitrange/limits created

查看 LimitsRange 资源配置:

[root@master1 ~]# kubectl describe limitranges --namespace=quota-example 
Name:       limits
Namespace:  quota-example
Type        Resource  Min  Max  Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---  ---  ---------------  -------------  -----------------------
Container   cpu       -    -    100m             200m           -
Container   memory    -    -    256Mi            512Mi          -

在 LimitRange 创建成功后,若用户在该命名空间中创建了未指定资源限制的 Pod,系统就会自动为该 Pod 设置默认的资源限制。

例如,每个新建的未指定资源限制的 Pod 都等价于使用下面的资源限制:

[root@master1 ~]# kubectl run nginx --image=nginx --requests=cpu=100m,memory=256Mi --limits=cpu=200m,memory=512Mi --namespace=quota-example
pod/nginx created

至此,已经为该命名空间配置好默认的计算资源,ReplicaSet 就能够创建 Pod了:

[root@master1 10.4]# kubectl get pods --namespace=quota-example
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6799fc88d8-kvbpm   1/1     Running   0          39s

接下来查看资源配额的使用情况:

[root@master1 ~]# kubectl  describe quota --namespace=quota-example 
Name:            compute-resources
Namespace:       quota-example
Resource         Used   Hard
--------         ----   ----
limits.cpu       200m   2
limits.memory    512Mi  2Gi
pods             1      4
requests.cpu     100m   1
requests.memory  256Mi  1Gi


Name:                   object-counts
Namespace:              quota-example
Resource                Used  Hard
--------                ----  ----
persistentvolumeclaims  0     2
services.loadbalancers  0     2
services.nodeports      0     0

看到每个 Pod 在创建时都会消耗指定的资源量。

5. 指定资源配额的作用域

假设并不想为某个命名空间配置默认的计算资源配额,而是希望限定在命名空间中运行的 QoS 为 BestEffort(Pod 中所有容器 Requests 和 Limits 都未定义,优先级最低) 的 Pod 总数。

例如让集群中的部分资源运行 QoS 为非 BestEffort 的服务,并让闲置的资源运行 QoS 为 BestEffort 的服务,即可避免集群的所有资源仅被大量的 BestEffort Pod 耗尽。这可以通过创建两个资源配额来实现:

创建一个名为 quota-scopes 的命名空间:

[root@master1 ~]# kubectl create namespace quota-scopes
namespace/quota-scopes created

创建一个名为 best-effort 的 ResourceQuota,指定 Scope 为 BestEffort,best-effort.yaml 文件内容如下:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: best-effort
spec:
  hard:
    pods: "10"
  scopes:
    - BestEffort

创建资源:

[root@master1 ~]# kubectl create -f best-effort.yaml --namespace=quota-scopes 
resourcequota/best-effort created

再创建一个名为 not-best-effort 的 ResourceQuota,指定 Scope 为 NotBestEffort,not-best-effort.yaml 文件内容如下:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: not-best-effort
spec:
  hard:
    pods: "4"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
  scopes:
    - NotBestEffort

创建资源:

[root@master1 ~]# kubectl create -f not-best-effort.yaml --namespace=quota-scopes 
resourcequota/not-best-effort created

查看创建成功的 ResourceQuota:

[root@master1 ~]# kubectl describe quota --namespace=quota-scopes 
Name:       best-effort
Namespace:  quota-scopes
Scopes:     BestEffort
 * Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
Resource  Used  Hard
--------  ----  ----
pods      0     10


Name:       not-best-effort
Namespace:  quota-scopes
Scopes:     NotBestEffort
 * Matches all pods that have at least one resource requirement set. These pods have a burstable or guaranteed quality of service.
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     2Gi
pods             0     4
requests.cpu     0     1
requests.memory  0     1Gi

之后没有配置 Requests 的 Pod 将被名为 best-effort 的 ResourceQuota 限制;而配置了 Requests 的 Pod 会被名为 not-best-effort 的 ResourceQuota 限制。

创建 bestEffort 的 Deployment , best-effort-nginx.yaml 文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: best-effort-nginx
spec:
  replicas: 8
  selector:
    matchLabels:
      app: best-effort-nginx
  template:
    metadata:
      labels:
        app: best-effort-nginx
    spec:
      containers:
        - name: nginx
          image: nginx

创建 NotbestEffort 的 Deployment,not-best-effort-nginx.yaml 文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: not-best-effort-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: not-best-effort-nginx
  template:
    metadata:
      labels:
        app: not-best-effort-nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 200m
              memory: 512Mi

创建两个资源:

[root@master1 ~]# kubectl create -f best-effort-nginx.yaml -f not-best-effort-nginx.yaml  --namespace=quota-scopes 
deployment.apps/best-effort-nginx created
deployment.apps/not-best-effort-nginx created

名为 best-effort-nginx 的 Deployment 因为没有配置 Requests 和 Limits,所以它的 QoS 级别为 BestEffort,因此它的创建过程由 best-effort 资源配额项来限制,而 not-best-effort 资源配额项不会对它进行限制。best-effort 资源配置项没有限制 Requests 和 Limits,因此 best-effort-nginx Deployment 可以成功创建 8 个 Pod。

名为 not-best-effort-nginx 的 Deployment 因为配置了 Requests 和 Limits,且二者不相等,所以它的 QoS 级别为 Burstable,因此所以它的创建过程由 not-best-effort 资源配额项限制,而 best-effort资源配额项不会对它进行限制。not-best-effort 资源配额项限制了 Pod 的 Requests 和 Limits 的总上限,not-best-effort-nginx Deployment 并没有超过这个上限,所以可以成功创建两个 Pod。

查看已经创建的 Pod,可以看到 10 个 Pod 都创建成功:

[root@master1 ~]# kubectl get pods --namespace=quota-scopes 
NAME                                    READY   STATUS    RESTARTS   AGE
best-effort-nginx-78bb6d5d9d-6cxg7      1/1     Running   0          45s
best-effort-nginx-78bb6d5d9d-ckrdx      1/1     Running   0          44s
best-effort-nginx-78bb6d5d9d-jw6cq      1/1     Running   0          44s
best-effort-nginx-78bb6d5d9d-llm42      1/1     Running   0          44s
best-effort-nginx-78bb6d5d9d-pzkkk      1/1     Running   0          45s
best-effort-nginx-78bb6d5d9d-rhjps      1/1     Running   0          45s
best-effort-nginx-78bb6d5d9d-tl6g5      1/1     Running   0          44s
best-effort-nginx-78bb6d5d9d-zghvp      1/1     Running   0          40s
not-best-effort-nginx-54cfd5bd8-v68dr   1/1     Running   0          45s
not-best-effort-nginx-54cfd5bd8-v7mt8   1/1     Running   0          45s

再查看两个资源配额项的使用情况,看到 best-effort 资源配额项已经统计了在 best-effort-nginx Deployment 中创建的 8 个 Pod 的资源使用信息。not-best-effort 资源配额项也已经统计了在 not-best-effort-nginx Deployment 中创建的两个 Pod 的资源使用信息。

[root@master1 ~]# kubectl describe quota --namespace=quota-scopes 
Name:       best-effort
Namespace:  quota-scopes
Scopes:     BestEffort
 * Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
Resource  Used  Hard
--------  ----  ----
pods      8     10


Name:       not-best-effort
Namespace:  quota-scopes
Scopes:     NotBestEffort
 * Matches all pods that have at least one resource requirement set. These pods have a burstable or guaranteed quality of service.
Resource         Used   Hard
--------         ----   ----
limits.cpu       400m   2
limits.memory    1Gi    2Gi
pods             2      4
requests.cpu     200m   1
requests.memory  512Mi  1Gi

通过这个例子可以发现:资源配额的作用域(Scopes)提供了一种将资源集合分割的机制,可以使集群管理员更加方便地监控和限制不同类型的对象对各类资源的使用情况,同时为资源分配和限制提供更好的灵活性和便利性。

6.资源管理小结

Kubernetes 中资源管理的基础是容器和 Pod 的资源配置(Requests 和 Limits)。容器的资源配置指定了容器请求的资源和容器能使用的资源上限,Pod 的资源配置则是 Pod 中所有容器的资源配置总和上限。

通过资源配额机制,我们可以对命名空间中所有 Pod 使用资源的总量进行限制,也可以对这个命名空间中指定类型的对象的数量进行限制。使用作用域可以让资源配额只对符合特定范围的对象加以限制,因此作用域机制可以使资源配额的策略更加丰富、灵活。

如果需要对用户的 Pod 或容器的资源配置做更多的限制,则可以使用资源配置范围(LimitRange)来达到这个目的。LimitRange 可以有效限制 Pod 和容器的资源配置的最大、最小范围,也可以限制 Pod 和容器的 Limits 与 Requests 的最大比例上限,LimitRange 还可以为 Pod 中的容器提供默认的资源配置。

Kubernetes 基于 Pod 的资源配置实现了资源服务质量(QoS)。不同 QoS 级别的 Pod 在系统中拥有不同的优先级:高优先级的 Pod 有更高的可靠性,可以用于运行对可靠性要求较高的服务;低优先级的 Pod 可以实现集群资源的超售,有效提高集群资源利用率。

参考:《Kubernetes权威指南(第五版)》