Kubernetes的Pod的资源配额

一.为什要进行资源配额

        我们可以使用一些方法从多个维度保障了服务的可用性,比如调度到不同的机器和机房、配置可靠的健康检查等。但是上述措施都是基于应用级别去做的,如果我们的Kubernetes集群用来运行容器的节点有了故障,带来的影响是很大的,所以在保证应用本身的前提下,也要通过一些措施保障节点的可用性

        节点故障大部分都是由于资源分配不合理、超额分配引起的,因此需要用某个技术手段保证节点的资源不会过大地超额分配,Kubernetes为我们提供了开箱即用的资源管理,可以通过ResourceQuota和LimitRange的配合防止节点资源超额分配。

二.ResourceQuota(资源配额)

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

1.什么是资源配额

        在生产环境中,可能会有多个Kubernetes集群,面向开发环境、测试环境、预生产环境和生产环境等。身为Kubernetes管理员,必然知道每个环境的规模有多大、可调度资源有多少,并且知道如何合理地为容器分配内存和CPU,所以一个管理员去管理整个Kubernetes集群时,很少会有资源分配超出集群可调度范围的情况。        

        但在实际使用时,Kubernetes集群并非只有一个管理员在使用,也并非只有管理员在使用。公司可能存在多个项目组,每个项目组都有属于自己的命名空间,每个项目组可以在其所在的命名空间创建资源而他们并不知道Kubernetes集群是多大规模,也不知道有多少可调度的资源,这样就很容易造成集群资源过量分配,引起集群不可用。在这种情况下,需要对每个项目组合理地分配资源用以避免超出集群的承能力,也可以减少废弃资源没有及时清理带来的资源浪费

        为了解决上述问题,Kubernetes引入了ResourceQuota的概念,以方便Kubernetes管理员进行资源分配,比如给A项目组分配16核64GB的资源,并且最多只能部署20个Pod、30个Service等,这羊来对Kubernetes的各类资源进行限制

        如果在一个命名空间内配置了ResourceQuota这个命名空间内的所有Pod都会受其影响

2.定义一个ResourceQuota

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

apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-test
  labels:
    app: resourcequota
spec:
  hard:
    pods: 50                        #限制最多启动50个Pod
    requests.cpu: 0.5               #初始化要求的最低CPU数量
    requests.memory: 512Mi          #初始化要求的最低内存数量
    limits.cpu: 5                   #最高可用的CPU数量
    limits.memory: 16Gi             #最高可用的内存数量
    configmaps: 20                  #限制最多20个ConfigMap
    requests.storage: 40Gi          #请求 40 GiB 的存储空间,用于持久化存储
    persistentvolumeclaims: 20      #集群允许最多 20 个持久化卷声明
    replicationcontrollers: 20      #集群允许最多 20 个副本控制器
    secrets: 20                     #集群允许最多 20 个机密
    services: 50                    #集群允许最多 50 个服务(Services)。
    services.loadbalancers: "2"     #集群允许最多 2 个负载均衡服务(LoadBalancers)
    services.nodeports: "10"        #集群允许最多 10 个 NodePort 服务

 3.ResourceQuota的使用

(1)创建命名空间

[root@k8s-master ~]# ku create ns quota-example
namespace/quota-example created

(2)编写ResourceQuota文件

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

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

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
spec:
  hard:
    persistentvolumeclaims: "1"

(3)创建该ResourceQuota

[root@k8s-master ~]# ku create -f quota-objects.yaml -n quota-example
resourcequota/object-quota-demo created

(4)查看创建的资源限制状态

[root@k8s-master ~]# ku get quota object-quota-demo -n quota-example
NAME                AGE   REQUEST                       LIMIT
object-quota-demo   69s   persistentvolumeclaims: 0/1   
[root@k8s-master ~]# ku get quota object-quota-demo -n quota-example -oyaml
apiVersion: v1
kind: ResourceQuota
metadata:
  creationTimestamp: "2024-08-30T03:18:00Z"
  name: object-quota-demo
  namespace: quota-example
  resourceVersion: "11752"
  uid: 2176d59b-b777-4745-8a0e-1fc08462986e
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "0"

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

(5)创建一个PVC

[root@k8s-master ~]# vim pvc.yaml 

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
root@k8s-master ~]# ku create -f pvc.yaml -n quota-example
persistentvolumeclaim/pvc-quota-demo created

(6)查看当前资源的使用情况

[root@k8s-master ~]# ku get quota object-quota-demo -n quota-example 
NAME                AGE     REQUEST                       LIMIT
object-quota-demo   8m35s   persistentvolumeclaims: 1/1 
[root@k8s-master ~]# ku get quota object-quota-demo -n quota-example -oyaml
apiVersion: v1
kind: ResourceQuota
metadata:
  creationTimestamp: "2024-08-30T03:18:00Z"
  name: object-quota-demo
  namespace: quota-example
  resourceVersion: "12412"
  uid: 2176d59b-b777-4745-8a0e-1fc08462986e
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "1"

 (7)在创建一个PVC

[root@k8s-master ~]# vim pvc2.yaml 

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

因为ResourceQuota配置了此命名空间内只能创建一个PVC,在前面已经创建了一个,used的值已经变为:1;那一个PVC已经被使用过了,所以在创建PVC的时候会报上述错误。

(8)清理 

[root@k8s-master ~]# ku delete -f pvc.yaml -n quota-example
persistentvolumeclaim "pvc-quota-demo" deleted
[root@k8s-master ~]# ku delete ns quota-example
namespace "quota-example" deleted

三.LimiRange

1.为什么使用LimitRange

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

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

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

(1)创建一个LimitRange

[root@k8s-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@k8s-master ~]# ku create -f limitrange01.yaml 
limitrange/cpu-mem-limit-range created
pod/default-cpu-demo created

(2)查看pod信息

[root@k8s-master ~]# ku get pod default-cpu-demo -oyaml 
··········
  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@k8s-master ~]# ku delete -f limitrange01.yaml 
limitrange "cpu-mem-limit-range" deleted
pod "default-cpu-demo" deleted
[root@k8s-master ~]# ku get pod 
No resources found in default namespace.

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

        上述针对没有设置requests和limits字段的资源添加了默认值,但是并没有限制requests和limits的最大值和最小值,这样同样会给集群带来风险,所以在管理资源分配时,对request和limits的最大值和最小值也需要进行管控。

        requests和limits的最大值和最小值的配置方式和默认值的配置差别不大。比如创建一个内存最大为1G,最小为500M和CPU最大为800m,最小为200m的LimiRange

(1)创建LimitRange的yaml文件

[root@k8s-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"                       

(2)创建这个LimitRange和pod

[root@k8s-master ~]# ku create -f limitrange02.yaml 
limitrange/cpu-min-max-demo-lr created
pod/constraints-mem-demo-2 created

(3)测试

[root@k8s-master ~]# ku get pod constraints-mem-demo-2 -oyaml
·······
containers:
  - image: nginx:1.7.9
    imagePullPolicy: IfNotPresent
    name: constraints-mem-demo-2-ctr
    resources:
      limits:
        cpu: 800m
        memory: 500Mi
      requests:
        cpu: 800m
        memory: 500Mi
·······

将containers的内存参数memory调整为大于800。

[root@k8s-master ~]# ku create -f limitrange02.yaml 
limitrange/cpu-min-max-demo-lr created
Error from server (Forbidden): error when creating "limitrange02.yaml": pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1200Mi

(4)清除

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

        LimitRange也可以对存储申请的大小进行限制,比如限制PVC申请空间的最小值为1GB,最大值为2GB(结合ResourceQuota可以同时限制最多存储使用量和最大PVC创建的数量)

(1) 编写限制请存储(PVC)的的配置文件

[root@k8s-master ~]# vim limitrange03.yaml 

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

(2)创建该LimitRange

[root@k8s-master ~]# ku create -f limitrange03.yaml 
limitrange/storagelimits created

(3)创建测试用的pv yaml文件,并创建pv

[root@k8s-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@k8s-master ~]# ku create -f hostpath-pv.yaml 
persistentvolume/mypv-hostpath created

(4)创建测试用的请求存储PVC yaml文件

[root@k8s-master ~]# vim pvc-hostpath.yaml 

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mypvc-hostpath
spec:
  storageClassName: pv-hostpath
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

创建pvc

[root@k8s-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

备注:可以看出pvc请求pv的存储空间为3Gi,但是LimitRange只让pvc请求1-2Gi的存储空间所以PVC请求失败。

四.服务质量(Qos)

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

1. 什么是服务质量

服务质量(QoS)是指当宿主机的资源不足时,按照级别驱逐或杀死Pod,以确保重要的服务资源(高级别)能够正常运行。

2.三种级别的服务质量

  • Guaranteed:最高服务质量,当宿主机内存或cpu不够时,会先杀死BestEffort和Burstable的Pod,如果内存或cpu还是不够,才会杀死Qos为Guaranteed的pod,该级别pod的资源占用量一般比较明确,即request字段的cpu和memory与limits字段的cpu和memory配置的一致。
  • Burstable:服务质量低于Guaranteed,当宿主机内存不够时,会先杀死QoS为BestEffort的Pod,如果内存还是不够,就会杀死QoS级别为Burstable的Pod,用未保证QoS质量为Guaranteed的Pod,该级别的Pod一般知道最小资源使用量,但是当机器资源充足时,还是想尽可能使用更多的资源,即1imits字段的cpu和memory大于requests字段的cpu和memory的配置。
  • BestEffort:尽力而为,当宿主机内存不够时,首先杀死的就是该QoS的Pod,用以保证Burstable和Guaranteed级别的Pod正常运行。

实现不同级别的服务质量是根据requests和limits的配置决定的在宿主机资源不够时会先杀死服务质量为BestEffort的Pod,然后杀死服务质量为Burstable的Pod,最后杀死服务质量为Guaranteed的Pod。所以在生产环境中比较重要的应用最好设置为Guaranteed,当然如果集群资源足够使用,可以都设置为Guaranteed.。

3.三种Qos服务质量的实现

(1)创建一个用于测试的Namespace命名空间

[root@k8s-master ~]# ku create ns qos-example
namespace/qos-example created

 (2)实现Qos为Guaranteed的Pod

满足条件:Pod中每个容器必须指定的limits.memory和request.memory必须相等;且limits.cpu和request.cpu也必须相等。

创建此类pod的yaml文件

[root@k8s-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@k8s-master ~]# ku create -f qos-pod.yaml -n qos-example
pod/qos-demo created

[root@k8s-master ~]# ku get pod qos-demo -n qos-example -oyaml
·········
 podIPs:
  - ip: 10.244.140.72
  qosClass: Guaranteed
  startTime: "2024-08-30T08:29:35Z"
···

(2)实现Qos为Burstable的Pod

满足条件:

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

编写服务质量为Burstable的pod的yaml文件

[root@k8s-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@k8s-master ~]# ku create -f qos-pod-2.yaml -n qos-example
pod/qos-demo-2 created

[root@k8s-master ~]# ku get pod qos-demo-2 -n qos-example -oyaml
········
podIP: 10.244.196.136
  podIPs:
  - ip: 10.244.196.136
  qosClass: Burstable
  startTime: "2024-08-30T08:35:57Z"

(3)实现Qos为BestEffort的Pod

条件:Pod中所有容器都没有设置requests和limits字段即可。

创建服务质量为BestEffort的Pod的yaml文件

[root@k8s-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

创建该Pod,并查看服务质量

[root@k8s-master ~]# ku create -f qos-pod-3.yaml -n qos-example
pod/qos-demo-3 created

[root@k8s-master ~]# ku get pod qos-demo-3 -n qos-example -oyaml
··········
podIP: 10.244.196.137
  podIPs:
  - ip: 10.244.196.137
  qosClass: BestEffort
  startTime: "2024-08-30T08:39:20Z"

4.总结

        提升服务可用性只从应用程序级别考虑是远远不够的,也需要考虑服务器的稳定性。在使用Kubernetes部署程序时,很容易造成资源的过量分配,作为Kubernetes管理员,必须要去考虑服务器的可用性,防止服务器岩机带来的雪。

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

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

你可能感兴趣的:(kubernetes,容器,云原生)