Kubernetes 资源管理全解析

Kubernetes 资源管理全解析:从配额到服务质量的全方位管控

前言

在当今的云计算时代,Kubernetes 作为容器编排的事实标准,被广泛应用于各类企业的集群管理中。服务的高可用性是企业关注的核心,而这不仅仅依赖于应用级别的配置和调度策略,节点的稳定性和资源管理的合理性同样起着至关重要的作用。节点的故障,尤其是由资源分配不当或超额分配引发的故障,往往会给整个集群带来严重的影响,甚至导致业务中断,造成巨大损失。

为了确保 Kubernetes 集群的稳定运行,深入理解和运用 Kubernetes 提供的资源管理功能就显得尤为重要。ResourceQuota(资源配额)和 LimitRange(资源限制)是 Kubernetes 资源管理的两大核心工具。ResourceQuota 为命名空间内的资源使用设定了总量限制,防止因过量分配资源而导致集群不可用;而 LimitRange 则为未配置资源请求的 Pod 提供了默认的资源限制,并允许管理员设定单个资源的最大和最小请求及限制值,从而避免了资源分配不合理的问题。

此外,QoS(服务质量)的概念在 Kubernetes 资源管理中也占据着举足轻重的地位。通过合理配置 Pod 的 QoS 等级,我们可以在系统资源紧张时,优先保障关键业务的正常运行,降低服务中断的风险。

综上所述,本文将详细探讨 Kubernetes 的资源管理功能,包括 ResourceQuota 和 LimitRange 的使用、QoS 的配置方法等,以期为构建稳定、高效的 Kubernetes 集群环境提供有力的支持。

一、ResourceQuota:命名空间的资源总量管控

在 Kubernetes 集群中,当多个项目组共用一个集群时,资源分配的合理性就成了一大挑战。ResourceQuota(资源配额)作为一种有效的资源管控手段,能够为每个命名空间设定资源使用的总量限制,保障集群的稳定运行。

1.1 资源配额的概念

在生产环境中,Kubernetes 集群往往需要面向开发环境、测试环境、预生产环境和生产环境等多个场景。对于 Kubernetes 管理员而言,他们清楚每个环境的规模、可调度资源数量以及如何合理地为容器分配内存和 CPU,因此在单一管理员管理整个集群时,资源分配超出集群可调度范围的情况较少出现。

但实际情况是,Kubernetes 集群并非仅由一个管理员使用,公司内部可能存在多个项目组,每个项目组都有属于自己的命名空间,并且可以在其命名空间内自主创建资源。这些项目组往往不了解集群的整体规模和可调度资源情况,这就很容易造成集群资源的过量分配,进而导致集群不可用。同时,废弃资源未及时清理也会带来资源浪费的问题。

为了解决上述问题,Kubernetes 引入了 ResourceQuota 的概念。通过 ResourceQuota,Kubernetes 管理员可以为每个项目组合理分配资源,例如给 A 项目组分配 16 核 64GB 的资源,并且限制其最多只能部署 20 个 Pod、30 个 Service 等,从而实现对 Kubernetes 各类资源的有效限制。

1.2 定义 ResourceQuota

与 Kubernetes 中其他资源的配置方式相同,资源配额也可以通过 YAML 文件进行创建。以下是一个常用的 ResourceQuota 定义示例:

yaml

复制代码
apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-test
  labels:
    app: resourcequota
spec:
  hard:
    pods: 50  # 限制最多启动Pod的个数
    requests.cpu: 0.5  # 初始要求的cpu数量
    requests.memory: 512Mi  # 初始要求的内存量
    limits.cpu: 5  # 所能使用的最大cpu数量
    limits.memory: 16Gi  # 所能使用的最大内存量
    configmaps: 20
    requests.storage: 40Gi
    persistentvolumeclaims: 20
    replicationcontrollers: 20
    secrets: 20
    services: 50
    services.loadbalancers: "2"
    services.nodeports: "10"

在这个 YAML 文件中,spec.hard字段下定义了各种资源的限制额度。pods表示该命名空间内最多可创建的 Pod 数量;requests.cpurequests.memory分别表示该命名空间内所有 Pod 初始请求的 CPU 和内存总量限制;limits.cpulimits.memory则是该命名空间内所有 Pod 所能使用的 CPU 和内存总量上限。此外,还对 configmaps、persistentvolumeclaims 等其他资源的数量或存储请求进行了限制。

1.3 ResourceQuota 的使用示例

接下来,通过一个具体的示例来演示 ResourceQuota 的使用方法。

(1)创建测试命名空间

首先,创建一个用于测试的命名空间quota-example

bash

复制代码
[root@k8s-master ~]# kubectl create ns quota-example
(2)创建测试用的 ResourceQuota

创建一个限制该命名空间内 PersistentVolumeClaims(PVC)数量不超过 1 个的 ResourceQuota,编辑quota-objects.yaml文件:

yaml

复制代码
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
spec:
  hard:
    persistentvolumeclaims: "1"
(3)创建 ResourceQuota

执行以下命令创建该 ResourceQuota:

bash

复制代码
[root@k8s-master ~]# kubectl create -f quota-objects.yaml -n quota-example
(4)查看资源限制状态

使用以下命令查看创建的 ResourceQuota 状态:

bash

复制代码
[root@k8s-master ~]# kubectl get quota object-quota-demo -n quota-example
NAME              AGE   REQUEST                                                                   LIMIT
object-quota-demo 114s  persistentvolumeclaims: 0/1

也可以通过-o yaml参数查看更详细的信息:

bash

复制代码
[root@k8s-master ~]# kubectl get quota object-quota-demo -n quota-example -o yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  creationTimestamp: "2023-08-19T08:10:19Z"
  name: object-quota-demo
  namespace: quota-example
  resourceVersion: "8831"
  uid: 47d2980c-bf9c-407f-a146-3f47e34372d2
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "0"

status.used字段可以看出当前资源的使用量。需要注意的是,只有当命名空间中创建了 ResourceQuota 后,该命名空间才会启用资源使用配额限制,未创建 ResourceQuota 的命名空间则不受资源配额限制。

(5)创建一个 PVC

编辑pvc.yaml文件,定义一个 PVC:

yaml

复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

执行以下命令创建该 PVC:

bash

复制代码
[root@k8s-master ~]# kubectl create -f pvc.yaml -n quota-example
(6)查看资源使用情况

再次查看 ResourceQuota 的状态:

bash

复制代码
[root@k8s-master ~]# kubectl get quota object-quota-demo -n quota-example -o yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  creationTimestamp: "2023-08-19T08:10:19Z"
  name: object-quota-demo
  namespace: quota-example
  resourceVersion: "9423"
  uid: 47d2980c-bf9c-407f-a146-3f47e34372d2
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "1"

可以看到,status.used.persistentvolumeclaims的值变为 1,表明已使用了该配额。

(7)再次创建 PVC

尝试再创建一个 PVC,编辑pvc2.yaml文件:

yaml

复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo2
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

执行创建命令时,会出现错误:

bash

复制代码
[root@k8s-master ~]# kubectl create -f pvc2.yaml -n quota-example
Error from server (Forbidden): error when creating "pvc2.yaml": persistentvolumeclaims "pvc-quota-demo2" is forbidden: exceeded quota: object-quota-demo, requested: persistentvolumeclaims=1, used: persistentvolumeclaims=1, limited: persistentvolumeclaims=1

这表明在该命名空间中已无法再创建 PVC,其他资源的限制效果与此类似。

(8)环境清理

测试完成后,清理环境:

bash

复制代码
[root@k8s-master ~]# kubectl delete -f pvc.yaml -n quota-example
[root@k8s-master ~]# kubectl delete ns quota-example

二、LimitRange:资源的默认值与范围限制

与 ResourceQuota 不同,LimitRange 的主要作用是为未配置资源请求(requests)和资源限制(limits)的 Pod 设置默认值,同时还可以限制单个资源的最大和最小请求及限制值,从而进一步优化资源管理。

2.1 LimitRange 的用途

在上一节中,我们了解了如何限制每个命名空间的最大资源使用量。但细心的读者可能会发现,如果创建的 Pod 或 Deployment 没有指定requestslimits字段,那么资源配额中对 CPU 和内存的限制就会形同虚设,因为此时 CPU 和内存的使用不会受到任何限制。另外,还有一种情况,假设一个 Namespace 分配了 16 核、64GB 的资源,若在该命名空间中创建一个申请requests.cpu为 16、requests.memory为 64GB 的容器,那么单个 Pod 就会占用整个 Namespace 的资源。

为了防止这类情况的发生,Kubernetes 引入了 LimitRange(也称为 LimitRanger)的概念。LimitRange 可以为未配置requestslimits的资源设置默认值,同时配置单个资源的最大requestslimits,从而有效解决上述问题。需要注意的是,LimitRange 不会影响已经创建的资源。

2.2 配置默认的 requests 和 limits

通过 LimitRange 可以配置默认的requestslimits值,以解决创建的资源未配置或配置过小的requestslimits带来的问题。

(1)创建 LimitRange

创建一个默认requests.cpu为 0.5(0.5 核 CPU,1 核 CPU 等于 1000m)、requests.memory为 256MB,默认limits.cpu为 1、limits.memory为 512MB 的 LimitRange,编辑limitrange01.yaml文件:

yaml

复制代码
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-mem-limit-range
spec:
  limits:
    - default:  # 当没有显式指定资源限制(limit最大值)时,为新创建的对象设置的默认资源限制
        cpu: "1"  # 默认的CPU限制为1个单位
        memory: 512Mi  # 默认的内存限制为512MiB
      defaultRequest:  # 当没有明确指定资源请求时,为新创建的对象设置的默认资源请求值
        cpu: "0.5"  # 默认的CPU请求为0.5个单位
        memory: 256Mi  # 默认的内存请求为256MiB
      type: Container

同时,创建一个未指定requestslimits的 Pod,编辑对应的 YAML 文件:

yaml

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
    - name: default-cpu-demo-ctr
      image: nginx

执行以下命令创建 LimitRange:

bash

复制代码
[root@k8s-master ~]# kubectl create -f limitrange01.yaml
(2)查看 Pod 信息

创建 Pod 后,查看其详细信息:

bash

复制代码
[root@k8s-master ~]# kubectl get pod default-cpu-demo -o yaml

在输出的信息中,可以看到容器的资源配置自动添加了默认值:

yaml

复制代码
containers:
  - image: nginx
    imagePullPolicy: Always
    name: default-cpu-demo-ctr
    resources:
      limits:
        cpu: "1"
        memory: 512Mi
      requests:
        cpu: 500m
        memory: 256Mi
(3)清除资源

测试完成后,清除资源:

bash

复制代码
[root@k8s-master ~]# kubectl delete -f limitrange01.yaml

2.3 配置 requests 和 limits 的范围

上述示例为未设置requestslimits的资源添加了默认值,但并未限制requestslimits的最大值和最小值,这仍然可能给集群带来风险。因此,在管理资源分配时,还需要对requestslimits的最大值和最小值进行管控。

(1)创建 LimitRange 的 YAML 文件

创建一个限制内存最小值为 500MB、最大值为 1GB,CPU 最小值为 200m、最大值为 800m 的 LimitRange,编辑limitrange02.yaml文件:

yaml

复制代码
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
    - max:
        cpu: "800m"
        memory: 1Gi
      min:
        cpu: "200m"
        memory: "500Mi"
      type: Container

同时,创建一个符合该范围的 Pod,编辑对应的 YAML 文件:

yaml

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
    - name: constraints-mem-demo-2-ctr
      image: nginx
      resources:
        limits:
          memory: "500Mi"
        requests:
          memory: "500Mi"

其中,limits表示分配给 Pod 的资源上限,requests表示分配给 Pod 的初始量。

(2)创建 LimitRange 和 Pod

执行以下命令创建 LimitRange 和 Pod:

bash

复制代码
[root@k8s-master ~]# kubectl create -f limitrange02.yaml
(3)测试

可以通过调整 Pod 中的内存大小,重新创建资源,观察效果。例如,若将 Pod 的内存请求设置为 200Mi,创建时会出现错误,因为其小于 LimitRange 中设置的内存最小值 500Mi。

(4)清除资源

测试完成后,清除资源:

bash

复制代码
[root@k8s-master ~]# kubectl delete -f limitrange02.yaml

2.4 限制申请存储空间的大小

除了对 CPU 和内存进行限制外,LimitRange 还可以对存储申请的大小进行限制。例如,限制 PVC 申请空间的最小值为 1GB、最大值为 2GB(结合 ResourceQuota 可以同时限制最多存储使用量和最大 PVC 创建数量)。

(1)创建 LimitRange

编辑limitrange03.yaml文件:

yaml

复制代码
apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
    - type: PersistentVolumeClaim
      max:
        storage: 2Gi
      min:
        storage: 1Gi

执行以下命令创建该 LimitRange:

bash

复制代码
[root@k8s-master ~]# kubectl create -f limitrange03.yaml
(2)测试存储限制

创建一个申请 3Gi 存储空间的 PVC,执行创建命令时会出现错误:

bash

复制代码
[root@k8s-master ~]# kubectl create -f pvc-hostpath.yaml
Error from server (Forbidden): error when creating "pvc-hostpath.yaml": persistentvolumeclaims "mypvc-hostpath" is forbidden: maximum storage usage per PersistentVolumeClaim is 2Gi, but request is 3Gi

这表明 PVC 申请的存储空间超出了 LimitRange 设置的最大值,因此创建失败。

三、QoS:服务质量等级与资源紧张时的调度策略

尽管我们进行了资源限制,但在实际使用过程中,节点资源不足的情况仍可能发生。当资源不足时,Kubernetes 会通过重启或驱逐 Pod 来释放资源。为了在这种情况下尽量保证关键业务的正常运行,Kubernetes 引入了 QoS(Quality of Service,服务质量)的概念。

3.1 服务质量保证的概念

在 Kubernetes 集群中,当应用部署和更新时,调度策略会将应用部署在最合适的节点上。但随着时间的推移,当初 "最优" 的节点可能不再是最佳选择,因为其他应用或管理员部署的应用可能未配置资源限制,导致宿主机的不可压缩资源(如内存、磁盘)使用率达到峰值。当内存达到峰值时,可能会引发 OOMKilled 故障,此时 Kubelet 会根据某些策略重启容器以避免宿主机宕机,但这难免会导致服务中断。如果重启的是关键业务应用,将带来非常不好的体验。

为了解决在系统资源不够的情况下尽量保证重要 Pod 不被杀死的问题,Kubernetes 引入了 QoS。通过配置 Pod 的 QoS 等级,可以提高某些应用的服务质量,确保在宿主机资源不足时,尽可能保证重要应用不被杀死。

Kubernetes 提供了 3 种级别的服务质量,分别是:

  • Guaranteed :最高服务质量。当宿主机内存不足时,会先杀死 QoS 为 BestEffort 和 Burstable 的 Pod,若内存仍不够,才会杀死 QoS 为 Guaranteed 的 Pod。该级别 Pod 的资源占用量通常比较明确,即requests字段的 CPU 和 memory 与limits字段的 CPU 和 memory 配置一致。

  • Burstable :服务质量低于 Guaranteed。当宿主机内存不足时,会先杀死 QoS 为 BestEffort 的 Pod,若内存仍不够,就会杀死 QoS 为 Burstable 的 Pod,以保证 QoS 为 Guaranteed 的 Pod 正常运行。该级别的 Pod 通常知道最小资源使用量,但在机器资源充足时,希望尽可能使用更多资源,即limits字段的 CPU 和 memory 大于requests字段的 CPU 和 memory 配置。

  • BestEffort:尽力而为。当宿主机内存不足时,首先被杀死的就是该 QoS 级别的 Pod,以保证 Burstable 和 Guaranteed 级别的 Pod 正常运行。

不同级别的服务质量由requestslimits的配置决定。在宿主机资源不足时,Kubernetes 会按照 BestEffort→Burstable→Guaranteed 的顺序杀死 Pod。因此,在生产环境中,比较重要的应用最好设置为 Guaranteed 级别;如果集群资源充足,所有应用都可以设置为 Guaranteed 级别。

3.2 QoS 级别的配置方法

下面通过具体的示例来演示如何配置这 3 种 QoS 级别。

(1)创建测试命名空间

首先,创建一个用于测试的命名空间qos-example

bash

复制代码
[root@k8s-master ~]# kubectl create namespace qos-example
(2)配置 QoS 为 Guaranteed 的 Pod

创建 Guaranteed 级别的 Pod 需要满足以下条件:

  • Pod 中的每个容器必须指定limits.memoryrequests.memory,且两者相等。
  • Pod 中的每个容器必须指定limits.cpurequests.cpu,且两者相等。

编辑qos-pod.yaml文件:

yaml

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
    - name: qos-demo-ctr
      image: nginx:1.7.9
      resources:
        limits:
          memory: "200Mi"
          cpu: "700m"
        requests:
          memory: "200Mi"
          cpu: "700m"

执行以下命令创建该 Pod:

bash

复制代码
[root@k8s-master ~]# kubectl create -f qos-pod.yaml -n qos-example

查看 Pod 的 QoS 级别:

bash

复制代码
[root@k8s-master ~]# kubectl get pod qos-demo -n qos-example -o yaml

在输出信息中可以看到:

yaml

复制代码
podIPs:
  - ip: 10.244.85.203
qosClass: Guaranteed
startTime: "2023-08-26T03:31:48Z"
(3)配置 QoS 为 Burstable 的 Pod

创建 Burstable 级别的 Pod 需要满足以下条件:

  • Pod 不符合 Guaranteed 的配置要求。
  • Pod 中至少有一个容器配置了requests.cpu或者requests.memory

编辑qos-pod-2.yaml文件:

yaml

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
    - name: qos-demo-2-ctr
      image: nginx:1.7.9
      resources:
        limits:
          memory: "200Mi"
        requests:
          memory: "100Mi"

执行以下命令创建该 Pod:

bash

复制代码
[root@k8s-master ~]# kubectl create -f qos-pod-2.yaml -n qos-example

查看 Pod 的 QoS 级别:

bash

复制代码
[root@k8s-master ~]# kubectl get pod qos-demo-2 -n qos-example -o yaml

在输出信息中可以看到:

yaml

复制代码
podIPs:
  - ip: 10.244.58.195
qosClass: Burstable
startTime: "2023-08-26T04:12:39Z"
(4)配置 QoS 为 BestEffort 的 Pod

创建 BestEffort 级别的 Pod 非常简单,只需保证 Pod 中的所有容器都没有设置requestslimits字段即可。

编辑qos-pod-3.yaml文件:

yaml

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
    - name: qos-demo-3-ctr
      image: nginx:1.7.9

执行以下命令创建该 Pod:

bash

复制代码
[root@k8s-master ~]# kubectl create -f qos-pod-3.yaml -n qos-example

查看 Pod 的 QoS 级别:

bash

复制代码
[root@k8s-master ~]# kubectl get pod qos-demo-3 -n qos-example -o yaml

在输出信息中可以看到:

yaml

复制代码
podIPs:
  - ip: 10.244.58.196
qosClass: BestEffort
startTime: "2023-08-26T04:24:13Z"

四、小结与总结

4.1 小结

提升服务可用性不能仅从应用程序级别考虑,还需要关注服务器的稳定性。在使用 Kubernetes 部署程序时,很容易出现资源过量分配的情况,因此作为 Kubernetes 管理员,必须考虑服务器的可用性,防止服务器宕机引发雪崩效应。

通常情况下,底层中间件的 QoS 会配置为 Guaranteed,其他服务可能不需要那么高的 QoS 级别。在实际使用中,最有可能超额分配的是内存,而 CPU 的使用率通常不高,只有在高频工作时才会处于忙碌状态。因此,在设置resources参数时,内存的request需要按需配置,例如一个程序的运行内存最少为 2GB,那么内存的request就应设置为 2GB,且不能低于该值。对于 CPU,limit参数非常重要,可防止 Pod 的 CPU 使用率过高而引发宿主机问题。

因此,在生产环境部署程序时,需要从应用的健康检查、平滑退出、亲和力、QoS 和 ResourceQuota 等多个维度考虑应用的健壮性,这些都是不可省略的配置。

4.2 总结

通过本章的学习,我们深入了解了 Kubernetes 资源管理的重要性和具体实现方式。

ResourceQuota 作为资源配额的核心组件,为限制命名空间资源使用提供了有效手段,避免了资源的过度分配和浪费。它能够根据不同项目组的需求,合理分配各类资源的总量,确保集群资源的有序使用。

LimitRange 则进一步增强了资源管理的灵活性和可控性。它为未设置资源请求和限制的资源设定了合理的默认值,同时限制了单个资源的最大请求和限制值,有效解决了未配置资源参数的 Pod 可能带来的资源滥用问题。

在此基础上,QoS 服务质量的概念与配置方法为资源紧张时的调度决策提供了依据。通过合理配置 Pod 的 QoS 等级,能够在系统资源紧张时优先保障关键业务的正常运行,提高了整个集群的稳定性和可用性。

总之,Kubernetes 资源管理是一个复杂而细致的过程,需要我们深入理解相关概念和原理,并结合实际需求进行合理配置。只有这样,才能充分发挥 Kubernetes 集群的性能优势,为业务的稳定运行提供有力保障。在实际应用中,应根据具体的业务场景和资源状况,灵活运用 ResourceQuota、LimitRange 和 QoS,构建稳定、高效的 Kubernetes 集群环境。

相关推荐
共享家95278 分钟前
排序算法实战(上)
数据结构·算法·排序算法
千楼22 分钟前
LeetCode 1888. 使二进制字符串字符交替的最少反转次数
算法·leetcode·职场和发展
Shan120533 分钟前
经典排序算法之希尔排序
java·算法·排序算法
PixelMind2 小时前
【LLIE专题】通过通道选择归一化提升模型光照泛化能力
图像处理·python·算法·llie·暗光增强
abigale032 小时前
JavaScript数据结构&算法
javascript·数据结构·算法
MuYiLuck2 小时前
【jvm|基本原理】第四天
java·开发语言·算法
今天背单词了吗9803 小时前
算法学习笔记:23.贪心算法之活动选择问题 ——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·考研·算法·贪心算法·活动选择问题
科大饭桶3 小时前
数据结构自学Day7-- 二叉树
数据结构·算法·leetcode·链表·c
秋说3 小时前
【PTA数据结构 | C语言版】根据前序序列重构二叉树
c语言·数据结构·算法
我想静静wwwwww3 小时前
74.搜索二维矩阵
数据结构·算法·矩阵