Kubernetes资源管理

资源简介

Kubernetes中有Node和Pod两种基础概念。一个Node就是Kubernetes集群中的一个节点,这个节点为Kubernetes提供计算资源(CPU、Memory、GPU等),并且由很多 Pod组成,一个Pod是Kubernetes中部署的最小的单位,由多个容器组成,这些容器会占用Node提供的资源来保持运行。当Kubernetes集群中计算资源不足时(如 Pod 占用资源过多),为了避免Kubernetes某个Node瘫痪,Kubernetes会清理已经存在的资源,比如杀死Pod来完成资源释放。为了避免或降低由于资源不足时导致Pod被杀死这种情况发生导致的风险,Kubernetes提供了资源限制的机制,其核心如下:

  • 限制Pod资源占用率
  • 为Pod划分等级(QoS),当资源不足时先杀死等级低的Pod来释放资源

在Kubernetes中资源又可分为API资源与计算资源,API资源主要包括Pod、Service、Deployment 等,计算资源主要包括CPU、GPU及Memory等,在计算资源中资源可分成 可压缩资源与不可压缩资源,因为在资源不足的时候,它们的表现不一样,对于不可压缩资源,资源不足也就无法继续申请资源(例如内存不足是无法再分配的),并且会导致Pod的运行产生无法预测的错误。对于可压缩资源,如CPU时间片,即使Pod使用的 CPU资源很多,CPU使用也可以按照权重分配给所有Pod使用,虽然当某个Pod分配 CPU过多时使其它Pod能够使用CPU的时间片减少,但不会导致程序直接不可用

Pod资源配置参数

Pod中可以配置CPU与Memory参数,如下:

  • spec.container[].resources.requests.cpu
  • spec.container[].resources.requests.memory
  • spec.container[].resources.limits.cpu
  • spec.container[].resources.limits.memory

其中requests是设置容器使用CPU与Memory资源占用预估值,它将作为容器调度分资源分配时的判断依据,只有当前节点上可分配的资源量大于requests中设置的值时才允许容器调度到该节点。limits是对容器使用CPU与Memory资源的上限,由于CPU是属于可压缩资源,Pod是无法突破其设置值的上限的,而Memory是不可压缩资源,当Pod突破内存上限时Kubernetes会杀死该Pod,所以设置内存限制时得慎重考虑

CPU与Memory

CPU

CPU的Requests和Limites能设置的值跟CPU的核心数息息相关,CPU的资源值是个绝对值,0.1CPU(100m)不论在单核心CPU的服务器上还是多核心CPU的服务器上,都是等于0.1CPU

Memory

Memory可以设置的Requests和Limits单位是字节数,需要整数加国际单位来表示内存值
国际单位制:

  • 十进制:E、P、T、G、M、K、m
  • 二进制:Ei、Pi、Ti、Gi、Mi、Ki、MiB

比如:

  • 1 KB = 1000 Bytes = 8000 Bits;
  • 1 KiB = 1024 Bytes = 8192 Bits;

一般推荐CPU是用 m,例如0.1个CPU使用100m表示,1个CPU用1000m表示;对于内存推荐使用Mi,例如256Mi、512Mi、1Gi

限额参数原理简介

requests.cpu

上面参数 spec.container[].resources.requests.cpu 设置的值会转换为CPU核心数,这个值乘以1024得到的结果,会作为docker run命令执行时–cpu-shares的参数。–cpu-shares参数是一个相对权重值,这个相对权重值会决定Docker在资源竞争时调整分配给容器可用资源的比例,默认对所有的容器的CPU资源可利用的占比都一致,即1024。这个值可以设置为2个CPU(值为 2048)或者 3个CPU(值为 3072)依次类推。启动两个容器且设置 --cpu-shares 参数分别为1024和2048,在主机CPU资源产生竞争时Docker 会按照1:2 的比例将CPU资源分配刚给这两个容器使用
由上可知requests.cpu参数对kubernetes来说这个值是一个绝对值,用于Kubernetes调度与管理,对于Docker来说是相对值,用于CPU资源比例分配

limits.cpu

Docker中有两个参数 --cpu-quota和–cpu-period,这两个参数一般配合使用,其中 --cpu-period是指使用CPU调度程序的周期,单位 μs (微秒),而 --cpu-quota 为在周期内使用CPU的配额,单位也是μs(微秒)。如何运行一个Docker容器并且设置它可用CPU为 1.5 个,那么久可用设置参数 --cpu-period=100000(100ms) 和 --cpu-quota=150000(150ms),这说明在100ms配置150ms的CPU量来完成任务,150/100 = 1.5,所以即完成1.5 个CPU的分配,关于Docker如何实现的这个限制功能,具体可用看内核的CPU分配策略
Kubernetes中的参数spec.container[].resources.limits.cpu如何限制CPU的,其实也是利用上面两个参数进行限制CPU使用的,利用公式 (limits.cpu * 100000)/1000得出结果的值作为–cpu-quota参数值,且将–cpu-period参数值设置为100000,然后这两个参数进行计算得出可用的CPU时间分片。Kubernetes中想限制只使用 0.1 个 CPU,就可以设置 limits.cpu=100m,然后可以按照上面公式计算一下,(100 * 100000)/1000 = 10000,将这个值赋给–cpu-quota参数,然后Kubernetes设置–cpu-period参数默认为100000,计算Docker分配容器最终CPU使用量为–cpu-quota/–cpu-period即10000/100000 = 0.1,符合Kubernetes中对CPU的限制

requests.memory

内存占用值,标识一般正常情况下镜像使用的内存量,这个参数值不会传递给Docker,它一般供Kubernetes调度器调度和作为资源使用指标的依据

limits.memory

容器内存限制,一般这个设置的值会传递给Docker启动时的–memory参数,用于限制启动的容器的内存使用大小,如果容器在运行过程中内存使用飙升超过这个限制的值,那么该容器可能会被杀掉,所以一般设置此值需要提前测试一下程序占用内存可能的值,进而考虑设置它的值的大小

limits与requests示例

Deployment对象来加上resources配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: "100m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"

Requests和Limits对调度器调度的影响

Kubernetes中每次创建Pod都是将其启动到Kubernetes Node节点上面,每个节点的计算资源(例如 CPU、Memory)是有限制的,该节点上所以的Pod占用资源大小综合不能超过节点所拥有的真实的资源大小。例如一个节点上拥有10GB内存,在这个节点上启动 limits.memory=2GB,requests.memory=1GB 的 Pod,考虑到系统占用资源情况,所以该节点最多启 9 个一样的 Pod。一般 Pod中可设置的requests参数值会影响到调度器对 Pod的调度,而limits参数主要是对容器可用资源的限制,对资源的调度影响比较小

QoS资源服务质量控制

QoS 等级分类

Kubernetes中如果一个Node节点上的Pod 占用资源过多并且不断飙升导致Node节点资源不足,可能会导致为了保证节点可用,将容器被杀掉。在遇见这种情况时候希望先杀掉那些不太重要的容器,确保核心容器不会首先被杀掉。为了衡量先杀掉哪个程序,所以推出了优先级机制 QoS (Quality of Service)来做判断,Kubernetes将容器划分为三种QoS等级:

  • Guaranteed: 完全可靠的
  • Burstable: 较可靠的。
  • BestEffort: 不太可靠的

Pod QoS特点

Kubernetes中资源不足时,根据QoS等级杀死Pod会有以下特点:

  • BestEffort Pod: 优先级最低,在Kubernetes资源不足时候会将此Pod先杀掉
  • Burstable Pod: 优先级居中,如果整个系统内存资源不足,且已经杀完全部 BestEffort 等级的Pod时可能被杀掉
  • Guaranteed Pod: 优先级最高,一般情况下不会被杀掉,除非资源不足且系统 BestEffort和Burstable的Pod都不存在的情况下,才可能被杀掉

Pod QoS等级

Kubernetes中Qos等级是根据Limits和Requests这两个参数设置的值息息相关,Kubernetes会根据这两个值的不同而给Pod设置不同的QoS等级

Guaranteed (等级-最高)
如果Pod中所有容器都定义了 Limits和Requests,并且全部容器的Limits值 = Requests值(值不能为0),那么该Pod的QoS级别就是 Guaranteed

注意:这种情况下容器可以只设置Limits值即可,引入在只设置Limits不设置Requests 情况下,Requests值默认等于Limits的值

BestEffort(等级-最低)
如果Pod中所有容器都未定义Requests和Limits值,该Pod的Qos即为BestEffort

Burstable(等级-中等)
当一个Pod既不是Guaranteed 级别,也不是BestEffort级别时候,该Pod的QoS级别就是Burstable。例如Pod中全部或者部分容器Requests和Limits都定义,且Requests小于 Limits值,或者Pod中一部分容器未定义Requests和Limits资源时

三种Qos示例

Guaranteed

每个容器都设置Limits而不设置Requests

containers:
  - name: example-container1
    resources:
      limits:
        cpu: 10m
        memory: 512Mi
  - name: example-container2
    resources:
      limits:
        cpu: 100m
        memory: 100Mi

每个容器都设置Limits值和Requests值都相等

containers:
  - name: example-container1
    resources:
      limits:
        cpu: 100m
        memory: 512Mi
      requests:
        cpu: 100
        memory: 512Mi
  - name: example-container2
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
      requests:
        cpu: 200
        memory: 256Mi

BestEffort

Pod中的所有容器都未定义Requests和Limits

containers:
  - name: example-container1
    resources:
  - name: example-container2
    resources:

Burstable
Pod中只要有一个容器的Requests和Limits的设置的值不相同

containers:
  - name: example-container1
    resources:
      limits:
        cpu: 100m
        memory: 512Mi
      requests:
        cpu: 100
        memory: 512Mi
  - name: example-container2
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
      requests:
        cpu: 100
        memory: 128Mi

Pod都存在Limits,但是Limits中限制的类型不同

containers:
  - name: example-container1
    resources:
      limits:
        memory: 512Mi
  - name: example-container2
    resources:
      limits:
        cpu: 200m

Pod中两个容器只有一个Limits ,另一个都没有设置

containers:
  - name: example-container1
    resources:
      limits:
        cpu: 100m
        memory: 512Mi
      requests:
        cpu: 100
        memory: 512Mi
  - name: example-container2
    resources:

资源范围管理对象LimitRange

默认情况下如果创建一个Pod没有设置Limits和Requests对其加以限制,那么这个Pod可能能够使用Kubernetes集群中全部资源, 但是每创建Pod资源时都加上这个动作是繁琐的,考虑到这点Kubernetes提供了LimitRange对象,它能够对一个Namespace下的全部 Pod使用资源设置默认值、并且设置上限大小和下限大小等操作

创建测试Namespace

[root@k8s01 ~]# kubectl create namespace limit-namespace

创建LimitRange对Namespace资源限制

创建一个LimitRange对象限制Namespace下的资源使用,其中limit的类型有两种:

  • 对 Container 使用资源进行限制,在 Pod 中的每一个容器都受此限制
  • 对 Pod 进行限制,即 Pod 中全部 Container 使用资源总和来进行限制
    资源对象limit-range.yaml内容如下
[root@k8s01 ~]# vim limit-range.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: limit-test
spec:
  limits:
    - type: Pod        #对Pod中所有容器资源总和进行限制
      max:
        cpu: 4000m
        memory: 2048Mi 
      min:
        cpu: 10m
        memory: 128Mi 
      maxLimitRequestRatio:
        cpu: 5
        memory: 5
    - type: Container  #对Pod中所有容器资源进行限制
      max:
        cpu: 2000m
        memory: 1024Mi
      min:
        cpu: 10m
        memory: 128Mi 
      maxLimitRequestRatio:
        cpu: 5
        memory: 5
      default:
        cpu: 1000m
        memory: 512Mi
      defaultRequest:
        cpu: 500m
        memory: 256Mi

注意:LimitRange 类型为 Pod 中,不能设置 Default

[root@k8s01 ~]# kubectl apply -f limit-range.yaml -n limit-namespace

查看LimitRange

[root@k8s01 ~]# kubectl describe limitrange limit-test -n limit-namespace
Name:       limit-test
Namespace:  limit-namespace
Type        Resource  Min    Max  Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---    ---  ---------------  -------------  -----------------------
Pod         cpu       10m    4    -                -              5
Pod         memory    128Mi  2Gi  -                -              5
Container   cpu       10m    2    500m             1              5
Container   memory    128Mi  1Gi  256Mi            512Mi          5

可以看到上面LimitRange对象中的配置可以了解到,如果容器使用默认值,则容器的 Request和Limits 一致

LimitRange 对象参数介绍

Container 参数

  • max: Pod中所有容器的Limits值上限
  • min: Pod中所有容器的Requests值下限
  • default: Pod中容器未指定Limits时,将此值设置为默认值
  • defaultRequest: Pod中容器未指定Request时,将此值设置为默认值
  • maxLimitRequestRatio: Pod中的容器设置Limits与Requests的比例的值不能超过maxLimitRequestRatio参数设置的值,即Limits/Requests ≤ maxLimitRequestRatio

Pod 参数

  • max: Pod中所有容器资源总和值上限
  • min: Pod中所有容器资源总和值下限
  • maxLimitRequestRatio: Pod中全部容器设置Limits总和与Requests总和的比例的值不能超过
  • maxLimitRequestRatio参数设置的值,即(All Container Limits)/(All Container Requests) ≤ maxLimitRequestRatio

创建Pod测试

Container默认值测试

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: nginx1
    image: nginx:latest

查看Pod的描述信息,可以看到Limits和Requests的值和上面LimitRange中配置的默认值一致

Containers:
  nginx1:
    Limits:
      cpu:     1000m
      memory:  512Mi
    Requests:
      cpu:     500m
      memory:  256Mi

Container Max限制测试

上面设置Max中CPU和Memory的值分别为2000m与1024Mi,下面创建一个Pod并设置其中某一个容器limits值超过LimitRange中设置的值,查看是否能起到限制作用

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: nginx1
    image: nginx:latest
  - name: nginx2
    image: nginx:latest
    resources:
      limits:
        cpu: "3000m"
        memory: "512Mi"

执行Kubectl命令创建Pod时并没有通过验证,并且已经提示CPU不能超过 2 个

[root@k8s01 ~]# kubectl apply -f test.yaml -n limit-namespace
Error from server (Forbidden): error when creating "test.yaml": 
pods "test" is forbidden: maximum cpu usage per Container is 2, but limit is 3.

Container Min限制测试

上面设置Min中CPU和Memory的值分别为10m与128Mi,下面创建一个Pod并设置其中某一个容器Requests值小于LimitRange中设置的值,是否能起到限制作用

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: nginx1
    image: nginx:latest
  - name: nginx2
    image: nginx:latest
    resources:
      requests:
        cpu: "100m"
        memory: "64Mi"

执行Kubectl命令创建Pod时并没有通过验证,并且已经提示Memory不能低于128Mi大小

[root@k8s01 ~]# kubectl apply -f test.yaml -n limit-namespace
Error from server (Forbidden): error when creating "test.yaml": pods "test" is forbidden: 
[minimum memory usage per Container is 128Mi, but request is 64Mi.
, cpu max limit to request ratio per Container is 5, but provided ratio is 10.000000.
, memory max limit to request ratio per Container is 5, but provided ratio is 8.000000.]

Container MaxLimitRequestRatio限制测试

上面LimitRange中设置maxLimitRequestRatio值为5,就是限制Pod中容器CPU和 Memory的limit/request的值小于5,测试一下设置内存limit/request中值超过5创建Pod是否会报错

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: nginx1
    image: nginx:latest
  - name: nginx2
    image: nginx:latest
    resources:
      requests:
        cpu: "100m"
        memory: "128Mi"
      limits:
        cpu: "200m"
        memory: "1024Mi"

Kubectl命令创建Pod时并没有通过验证,并且已经提示limit/request ratio为8,超过了限制的值5

[root@k8s01 ~]# kubectl apply -f test.yaml -n limit-namespace
Error from server (Forbidden): error when creating "test.yaml": 
pods "test" is forbidden: memory max limit to request ratio per Container is 5, but provided ratio is 8.000000.

资源配额管理对象ResourcesQuota

Kubernetes是一个多租户平台,更是一个镜像集群管理工具。一个Kubernetes集群中的资源一般是由多个团队共享的,经常要考虑的是如何对这个整体资源进行分配。在 kubernetes中提供了Namespace来讲应用隔离,那么是不是也能将资源的大小跟 Namespace挂钩进行一起隔离,Kubernetes提供了Resources Quotas工具,让集群管理员可以创建ResourcesQuota对象管理这个集群整体资源的配额,它可以限制某个Namespace下计算资源的使用量,也可以设置某个Namespace下某种类型对象的上限等
通过设置不同的Namespace与对应的 RBAC权限将各个团队隔离,然后通过 ResourcesQuota对象来现在该Namespace能够拥有的资源的多少

开启资源配额ResourceQuota

ResourceQuota对象一般在Kubernetes中是默认开启的,如果未开启且不能创建该对象,那么可以进入Master的Kubernetes配置目录修改Apiserver配置文件kube-apiserver.yaml,在添加参数 --admission-control=ResourceQuota 来开启

...
     - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --admission-control=ResourceQuota    #开启ResourceQuota
...

注意:一个Namespace中可以拥有多个ResourceQuota对象

配额资源类型

  • 计算资源配额: 限制一个 Namespace 中所有 Pod 的计算资源(CPU、Memory)的总和
  • 存储资源配额: 限制一个 Namespace 中所有存储资源的总量
  • 对象数量配额: 限制一个 Namespace 中指定类型对象的数量

ResourcesQuota支持的计算资源

  • cpu: 所有非终止状态的Pod中,其CPU需求总量不能超过该值
  • limits.cpu: 所有非终止状态的Pod中,其CPU限额总量不能超过该值
  • limits.memory: 所有非终止状态的Pod中,其内存限额总量不能超过该值
  • memory: 所有非终止状态的Pod中,其内存需求总量不能超过该值
  • requests.cpu: 所有非终止状态的Pod中,其CPU需求总量不能超过该值
  • requests.memory: 所有非终止状态的Pod中,其内存需求总量不能超过该值

ResourcesQuota支持限制的存储资源

  • requests.storage:所有 PVC 请求的存储大小总量不能超过此值
  • Persistentvolumeclaims: PVC 对象存在的最大数目
  • .storageclass.storage.k8s.io/requests.storage: 和 StorageClass 关联的 PVC 的请求存储的量大小不能超过此设置的值
  • .storageclass.storage.k8s.io/persistentvolumeclaims: 和 StorageClass 关联的 PVC 的总量

ResourcesQuota支持限制的对象资源

  • Configmaps: 允许存在的 ConfigMap 的数量
  • Pods: 允许存在的非终止状态的 Pod 数量,如果 Pod 的 status.phase 为 Failed 或 Succeeded , 那么其处于终止状态
  • Replicationcontrollers: 允许存在的 Replication Controllers 的数量
  • Resourcequotas: 允许存在的 Resource Quotas 的数量
  • Services: 允许存在的 Service 的数量
  • services.loadbalancers: 允许存在的 LoadBalancer 类型的 Service 的数量
  • services.nodeports: 允许存在的 NodePort 类型的 Service 的数量
  • Secrets: 允许存在的 Secret 的数量

配额作用域

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

  • Terminating: 匹配 spec.activeDeadlineSeconds ≥ 0 的 Pod
  • NotTerminating: 匹配 spec.activeDeadlineSeconds 是 nil(空) 的 Pod
  • BestEffort: 匹配所有 QoS 等级是 BestEffort 级别的 Pod
  • NotBestEffort: 匹配所有 QoS 等级不是 BestEffort 级别的 Pod

BestEffort作用域限制配额跟踪以下资源

  • pods

Terminating、 NotTerminating和NotBestEffort限制配额跟踪以下资源

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

ResourceQuota使用示例

设置Namespace计算资源的配额

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@k8s01 ~]# kubectl apply -f resources-test1.yaml -n limit-namespace  

查看创建后的ResourcesQuota配额的详细信息

[root@k8s01 ~]# kubectl describe quota compute-resources -n limit-namespace
Name:            compute-resources
Namespace:       limit-namespace
Resource         Used   Hard
--------         ----   ----
limits.cpu       2      2
limits.memory    1Gi    2Gi
pods             2      4
requests.cpu     2      1
requests.memory  512Mi  1Gi

设置Namespace对象数量的配额

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
    
[root@k8s01 ~]# kubectl apply -f resources-test2.yaml -n limit-namespace    

查看创建后的ResourcesQuota配额的详细信息

[root@k8s01 ~]# kubectl describe quota object-counts -n limit-namespace

Name:                   object-counts
Namespace:              limit-namespace
Resource                Used  Hard
--------                ----  ----
configmaps              0     10
persistentvolumeclaims  0     4
replicationcontrollers  0     20
secrets                 1     10
services                0     10
services.loadbalancers  0     2

限制Namespace下Pod数量并只作用域BestEffort

apiVersion: v1
kind: ResourceQuota
metadata:
  name: besteffort
spec:
  hard:
    pods: "5"
  scopes:
  - BestEffort
  
[root@k8s01 ~]# kubectl apply -f resources-test3.yaml -n limit-namespace

查看创建后的ResourcesQuota配额的详细信息

[root@k8s01 ~]# kubectl describe quota besteffort -n limit-namespace
Name:       besteffort
Namespace:  limit-namespace
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     5

配额对象创建完成后,可以创建几个Pod对其配额规则进行验证

文章作者: 鲜花的主人
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 爱吃可爱多
Kubernetes Kubernetes
喜欢就支持一下吧
打赏
微信 微信
支付宝 支付宝