Kubernetes的资源管理ResouceQuota、LimitRange、Qos

        通过前面的学习,我们已经从多个维度保障了服务的可用性,比如调度到不同的机器和机房、配置可靠的健康检査等。但是上述措施都是基于应用级别去做的,如果我们的 Kubernetes 集群用来运行容器的节点有了故障,带来的影响是很大的,所以在保证应用本身的前提下,也要通过一些措施保障节点的可用性。
        节点故障大部分都是由于资源分配不合理、超额分配引起的,因此需要用某个技术手段保证节点的资源不会过大地超额分配,Kubernetes 为我们提供了开箱即用的资源管理,可以通过 ResourceQuota 和LimitRange 的配合防止节点资源超额分配。

一、ResouceQuota

        首先看一下 ResourceQuota(资源配额)的使用,资源配额是限制某个命名空间对资源使用的一个总量限制,比如内存、CPU、Pod 数量等。

1.什么是资源配额

        在生产环境中,可能会有多个 Kubernetes 集群,面向开发环境、测试环境、预生产环境和生产环境等。身为 Kubernetes 管理员,必然知道每个环境的规模有多大、可调度资源有多少,并且知道如何合理地为容器分配内存和 CPU,所以一个管理员去管理整个 Kubernetes 集群时,很少会有资源分配超出集群可调度范围的情况。
        但在实际使用时,Kubernetes 集群并非只有一个管理员在使用,也并非只有管理员在使用。公司可能存在多个项目组,每个项目组都有属于自己的命名空间,每个项目组可以在其所在的命名空间创建资源而他们并不知道 Kubernetes 集群是多大规模,也不知道有多少可调度的资源,这样就很容易造成集群资源过量分配,引起集群不可用。在这种情况下,需要对每个项目组合理地分配资源用以避免超出集群的承载能力,也可以减少废弃资源没有及时清理带来的资源浪费。
        为了解决上述问题,Kubernetes 引入了 ResourceQuota 的概念,以方便 Kubernetes 管理员进行资源分配,比如给A项目组分配 16 核 64GB 的资源,并且最多只能部署 20 个 Pod、30个service 等,这样来对 Kubernetes 的各类资源进行限制。

2.定义一个ResourceQuota

        和其他资源配置方法一样,资源配额也可以通过一个 YAML 文件进行创建,比如定义一个比较常用的ResourceQuota如下:

[root@master ~]# vim resourcequota.yaml 

apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-test
  labels:
    app: resourcequota
spec:
  hard:
    pods: 50
    requests.cpu: 0.5
    requests.memory: 512Mi
    limits.cpu: 5
    limits.memory: 16Gi
    configmaps: 20
    requests.storage: 40Gi
    persistentvolumeclaims: 20
    replicationcontrollers: 20
    secrets: 20
    services: 50
    services.loadbalancers: "2"
    services.nodeports: "10"

3.ResourceQuota的使用

接下来演示 ResourceQuota 的具体使用方法。

(1)创建一个用于测试的 namespace
[root@master ~]# ku create ns quota-example
namespace/quota-example created
(2)创建一个测试 ResourceQuota

该 demo 可以限制 namespace 的 PVC 不能超过 1个

[root@master ~]# vim quota-objects.yaml 

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
spec:
  hard:
    persistentvolumeclaims: "1"
(3)创建该ResourceQuota
[root@master ~]# ku create -f quota-objects.yaml -n quota-example
resourcequota/object-quota-demo created
 (4)查看创建的资源限制状态
[root@master ~]# ku get quota object-quota-demo -n quota-example
NAME                AGE     REQUEST                       LIMIT
object-quota-demo   2m45s   persistentvolumeclaims: 0/1
[root@master ~]# ku get quota object-quota-demo -n quota-example -oyaml
apiVersion: v1
kind: ResourceQuota
metadata:
  creationTimestamp: "2024-08-30T01:00:32Z"
  name: object-quota-demo
  namespace: quota-example
  resourceVersion: "11063"
  uid: 6ab9339b-4001-41b5-ae2e-0645c1b50ff9
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "0"

备注:
从 status 字段的 userd 看出,当前的资源显示的使用量,并且namespace 只有在创建了ResourceQuota才会启用资源使用的配额,没有创建 ResourceQuota 的 namespace 不限制资源的使用。 

(5)创建一个PVC
[root@master ~]# vim pvc.yaml 

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
[root@master ~]# ku create -f pvc.yaml -n quota-example
persistentvolumeclaim/pvc-quota-demo created
 (6)查看当前资源使用情况
[root@master ~]# ku get quota object-quota-demo -n quota-example -oyaml
apiVersion: v1
kind: ResourceQuota
metadata:
  creationTimestamp: "2024-08-30T01:00:32Z"
  name: object-quota-demo
  namespace: quota-example
  resourceVersion: "11424"
  uid: 6ab9339b-4001-41b5-ae2e-0645c1b50ff9
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "1"
 (7)再次创建一个PVC
[root@master ~]# vim pvc2.yaml 

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo2
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
[root@master ~]# ku 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

 此时已经无法在这个namespace中创建PVC,其他资源的限制与之类似。

(8)环境清理
[root@master ~]# ku delete -f pvc.yaml -n quota-example
persistentvolumeclaim "pvc-quota-demo" deleted
[root@master ~]# ku delete ns quota-example
namespace "quota-example" deleted

二、LimitRange

        和 ResourceQuota 不同的是,LimitRange 用来配置默认值,也就是一个Pod 如果没有配置要用多少内存、CPU、LimitRange 会在创建 Pod 时添加一个默认值。

1.LimitRange 的用途

        上一节讲解了每个命名空间的最大资源使用量,细心的话我们可能会发现,如果创建了一个 Pod 或Deployment 没有指定 requests 和 limits 字段,是不是就意味着资源配额对内存和 CPU 的限制变成了一个摆设?答案是肯定的,这时 CPU 和内存永远不会被限制。还有另一种情况,假如一个Namespace 分配了 16 核、64GB 的空间,之后创建一个申请了requests.cpu为16、requests.memory为64GB 的容器,那么单个 Pod 就能把整个 Namespace 的资源全部占用。

        为了防止这类情况发生,Kubernetes 又引出了另一个概念:LimitRanger,用于针对没有配置requests 和 limits 的资源设置一个默认值,同时配置单个资源最大的requests 和 Limits,这样就能解决上述问题(注意:LimitRanger 不会影响已经创建的资源)。

2.示例1:配置默认的 requests 和 limits

        可以通过 LimitRanger 配置默认的 requests 和 limits 值,用来解决创建的资源没有配置或配置过小 的 requests 和 Limits 带来的问题,比如创建一个requests.cpu 默认为0.5(0.5为半颗 CPU,]个CPU等于 1000m)、requests.memory为256MB、limits.cpu为1、limits.memory 为512MB 的LimitRanger:

(1)创建一个 limitRange
[root@master ~]# vim limitrange01.yaml 

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-mem-limit-range
spec:
  limits:
  - default:
      cpu: 1
      memory: 512Mi
    defaultRequest:
      cpu: 0.5
      memory: 256Mi
    type: Container
---
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx:1.7.9
[root@master ~]# ku create -f limitrange01.yaml 
limitrange/cpu-mem-limit-range created
pod/default-cpu-demo created
 (2)查看pod信息
[root@master ~]# ku get pod default-cpu-demo -oyaml
......
spec:
  containers:
  - image: nginx:1.7.9
    imagePullPolicy: IfNotPresent
    name: default-cpu-demo-ctr
    resources:
      limits:
        cpu: "1"
        memory: 512Mi
      requests:
        cpu: 500m
        memory: 256Mi
 (3)清除资源
[root@master ~]# ku delete -f limitrange01.yaml 
limitrange "cpu-mem-limit-range" deleted
pod "default-cpu-demo" deleted

 3.示例2:配置 requests和 limits 的范围

        上述针对没有设置 requests 和 limits 字段的资源添加了默认值,但是并没有限制 requests 和1imits 的最大值和最小值,这样同样会给集群带来风险,所以在管理资源分配时,对 requests 和 limits的最大值和最小值也需要进行管控。
        requests 和 limits 的最大值和最小值的配置方式和默认值的配置差别不大,比如创建一个内存最小值为 500MB、最大值为 1GB 和 CPU 最小值为 288MB、最大值为 800MB 的 LimitRanger。

(1)创建LimitRanger 的yaml文件
[root@master ~]# vim limitrange02.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
---
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx:1.7.9
    resources:
      limits:
        memory: "500Mi"
      requests:
        memory: "500Mi"

备注:
内存最小值为 500M,最大为1G
CPU 最小为 200m,最大为 800m
limits:最高分配给 pod 的资源上限requests:最高分配给 pod 的请求 

(2)创建这个 LimitRange 和 Pod 
[root@master ~]# ku create -f limitrange02.yaml 
limitrange/cpu-min-max-demo-lr created
pod/constraints-mem-demo-2 created
(3)测试
[root@master ~]# ku get pod
NAME                     READY   STATUS    RESTARTS   AGE
constraints-mem-demo-2   1/1     Running   0          27s
[root@master ~]# ku get pod constraints-mem-demo-2 -oyaml

可以调整 Pod 中的内存大小,重新创建这个资源,观察效果 

(4)清除资源 

[root@master ~]# ku delete -f limitrange02.yaml 
limitrange "cpu-min-max-demo-lr" deleted
pod "constraints-mem-demo-2" deleted

3.示例3:限制申请存储空间的大小

        上述讲解的是对 CPU 和内存的限制,同样 LimitRanger 也可以对存储申请的大小进行限制,比如限制 PVC 申请空间的最小值为 1GB、最大值为 2GB(结合 Resource0uota 可以同时限制最多存储使用量和最大 PVC 创建的数量):

[root@master ~]# vim limitrange03.yaml 

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:
      storage: 2Gi
    min:
      storage: 1Gi
[root@master ~]# vim hostpath-pv.yaml 

kind: PersistentVolume
apiVersion: v1
metadata:
  name: mypv-hostpath
  labels:
    type: local
spec:
  storageClassName: pv-hostpath
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
[root@master ~]# ku create -f hostpath-pv.yaml 
persistentvolume/mypv-hostpath created
[root@master ~]# ku create -f limitrange03.yaml 
limitrange/storagelimits created
[root@master ~]# ku 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

三、Qos

        虽然我们进行了资源限制,但是实际使用时依旧会造成节点资源不足,针对资源不足 Kubernetes 会通过重启或驱逐 Pod 释放资源,再重启时,难免会造成一些很重要的服务不可用。但实际情况可能是,如果重启或驱逐一些不重要的 Pod 可能会更好,而这种决策是通过 0os(ouality of service,服务质量)决定的,所以在生产环境中,Qos是一个非常重要的环节。

1.什么是服务质量保证

        假设我们有一个 java 进程,需要的 CPU 最低为1核,内存最低为 2G 才能正常启动,并且需要 10 个副本才能支撑业务量。此时我们需要创建一个 Deployment 并设置副本为 10、requests.cpu为1和requests.memory为 2GB。创建资源时,Scheduler 会根据一系列算法将该 Pod 部署到合适的节点上,当然需要被调度节点最少有1核、2GB 的空间资源。看似一个再简单不过的部署,如果不设置requests字段会如何呢?
        如果未设置 requests 字段,那么 Scheduler 在调度时将不再检査 Node 节点上的资源是否满足该应用程序的最低需求,因为未配置 requests 字段,Scheduler 将随机部署至其他节点,包括已经没有资源的机器。如果部署到了一个没有资源的宿主机上,在该应用程序启动时可能就会引起容器一直无法启用,从而进入恶性循环。而且如果此时 10个副本中有3个副本都部署到了一个没有多少资源的宿主机上,3个容器就会引起资源竞争,到最后一个也无法启动,并且在启动过程中程序会占用非常高的 CPU,可能会导致上面正在运行的 Pod 和宿主机不可用,此时很大程度上会引起雪崩,所以设置合理的 requests 和limits 是非常重要的事情。

        我们设置了 requests 和 limits 就能万无一失吗?答案当然是不可能的。在使用 Kubernetes 部署时,应用的部署和更新都会经过一系列的调度策略将应用部署在最合适的节点上,但是随着时间的推移,当时“最优”的节点可能已经不再是最佳选择,因为在该服务器上别的应用或者其他管理员部署的应用可能忘记了配置资源限制,所以在日积月累的消耗中,宿主机一些不可压缩的资源(比如内存、磁盘)的使用率将达到最高峰。假如内存达到最高峰时会引起 00MKilled 故障(00MKi1led 即为内存杀手),此时Kubelet 会根据某些策略重启上面的容器用来避免宿主机宕机引来的风险,但是重启容器难免会带来服务中断的现象,如果重启的是比较重要的业务应用,这将是一个非常不好的体验。那么我们如何在系统资源不够的情况下尽量保证一些比较重要的 Pod 不被杀死呢?
        前面讲了这么多主要是为了引出本节的主角:QoS;我们可以用 QoS 来提高某些应用的服务质
量,以保证宿主机资源不够时尽可能保证某些应用不被杀死。

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

  • Guaranteed:最高服务质量,当宿主机内存不够时,会先杀死 0os为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 正常运行。

        实现不同级别的服务质量是根据requests 和 ljmits 的配置决定的」在宿主机资源不够时会先杀死服务质呈为 BestEffort 的 Pod,然后杀死服务质量为 Burstable 的 Pod,最后杀死服务质量头Guaranteed 的 Pod。所以在生产环境中比较重要的应用最好设置为 Guaranteed,当然如果集群资源足够使用,可以都设置为 Guaranteed。接下来看一下如何实现这 3种 0oS。

2.首先创建一个用于测试的 Namespace

[root@master ~]# ku create ns qos-example
namespace/qos-example created
(1)实现 Qos 为Guaranteed 的 Pod 

创建此类型的 Pod 需要满足的条件如下:

  • Pod 中的每个容器必须指定 limits.memory 和 requests.memory,并且两者需要相等
  • Pod 中的每个容器必须指定 1imits.cpu和 requests.cpu,并且两者需要相等
[root@master ~]# vim qos-pod.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"
[root@master ~]# ku create -f qos-pod.yaml -n qos-example
pod/qos-demo created
[root@master ~]# ku get pod qos-demo -n qos-example -oyaml
......
 podIPs:
  - ip: 10.244.104.9
  qosClass: Guaranteed
  startTime: "2024-08-30T02:07:35Z"
(2)实现0os 为Burstable 的Pod 

创建一个 Qos 为 Burstable 的 Pod 需要满足以下条件:

  • Pod 不符合 Guaranteed 的配置要求
  • Pod 中至少有一个容器配置了 requests.cpu 或者 requests.memory。
[root@master ~]# vim qos-pod-2.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"
[root@master ~]# ku create -f qos-pod-2.yaml 
pod/qos-demo-2 created
[root@master ~]# ku get pod qos-demo -n qos-example -oyaml
......
 podIPs:
  - ip: 10.244.166.135
  qosClass: Burstable
  startTime: "2024-08-30T02:11:38Z"
(3)实现Qos为BestEffort 的 Pod 

创建一个 Qos 为 Burstable 的 Pod 更为简单,Pod 中的所有容器都没有设置 requests 和 limits 字段即可。

[root@master ~]# vim qos-pod-3.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
[root@master ~]# ku create -f qos-pod-3.yaml -n qos-example
pod/qos-demo-3 created
[root@master ~]# ku get pod qos-demo-3 -n qos-example -oyaml
......
 podIPs:
  - ip: 10.244.166.136
  qosClass: BestEffort
  startTime: "2024-08-30T02:13:59Z"

四、小结

        提升服务可用性只从应用程序级别考虑是远远不够的,也需要考虑服务器的稳定性。在使用Kubernetes 部署程序时,很容易造成资源的过量分配,作为 Kubernetes 管理员,必须要去考虑服务器的可用性,防止服务器岩机带来的雪崩。
        通常情况下,依赖的底层中间件的 QoS 会配置为 Guaranteed,其他服务可能并不需要那么高的QoS。在实际使用时。最有可能超额分配的是内存,而CPU通常使用率不高,只有在高频工作时,CPU 才会处于忙碌状态。所以在设置 resources 参数时,内存的 request 需要按需配置,比如一个程序的运行内存最少为 2GB,那么内存的 request 就要设置为 2GB,而不能低于 2GB。对于 CPU,Limit 参数很重要,防止 Pod 的 CPU 过高,从而引起宿主机的问题。
        所以在生产环境部署一个程序时,需要从应用的健康检査、平滑退出、亲和力、QoS 和 ResourceQuota等多个维度去考虑应用的健壮性,这些都是不可省略的配置。

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值