Skip to content

文档,https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/

1.Pod 资源限制

1.1 什么是资源限制

在 Kubernetes 集群中,为了使系统能够稳定的运⾏,通常会对 Pod 的资源使⽤量进⾏限制

在 Kubernetes 集群中,如果有⼀个程序出现异常,并占⽤⼤量的系统资源。如果未对该 Pod 进⾏资源限制的话,可能会影响其他的 Pod 正常运⾏,从⽽造成业务的不稳定性

1.2 如何实现资源限制

Kubernetes 通过 RequestsLimits 字段来实现对 Pod 的资源进⾏限制

  • Requests:启动 Pod 时申请分配的资源⼤⼩;(Pod在调度的时候requests⽐较重要)
  • Limits:限制 Pod 运⾏时最⼤可⽤的资源⼤⼩;(Pod在运⾏时limits⽐较重要)
yaml
spec.containers[].resources.requests.cpu		//定义创建容器时预分配的CPU资源

spec.containers[].resources.requests.memory		//定义创建容器时预分配的内存资源

spec.containers[].resources.limits.cpu			//定义 cpu 的资源上限 

spec.containers[].resources.limits.memory		//定义内存的资源上限
spec.containers[].resources.requests.cpu		//定义创建容器时预分配的CPU资源

spec.containers[].resources.requests.memory		//定义创建容器时预分配的内存资源

spec.containers[].resources.limits.cpu			//定义 cpu 的资源上限 

spec.containers[].resources.limits.memory		//定义内存的资源上限

1.3 资源限制的⽬的与意义

CPU:为集群中运⾏的容器配置CPU请求和限制,可以有效利⽤集群上可⽤的 CPU 资源

  • 设置 Pod CPU 请求 设定在较低的数值,可以使 Pod 更有机会被调度
  • 设置 CPU 限制⼤于 CPU 请求,可以完成如下两件事
    • 1、当 Pod 碰到⼀些突发负载时,它可以合理利⽤可⽤的 CPU 资源
    • 2、当 Pod 在突发流量期间 可使⽤的 CPU 被限制为合理的数值,从⽽可以避免影响其他 Pod 的正常运⾏;

内存:为集群中运⾏的容器配置内存请求和限制,可以有效利⽤集群节点上可⽤的内存资源

  • 通过将 Pod 的内存请求设定在较低的数值,可以使 Pod 更有机会被调度
  • 通过让 内存限制⼤于内存请求,可以完成如下两件事:
    • 当 Pod 碰到⼀些突发负载时,可以更好的利⽤其主机上的可⽤内存
    • 当 Pod 在突发负载期间 可使⽤的内存被限制为合理的数值,从⽽可以避免影响其他 Pod 的运⾏

2.资源限制单位换算

2.1 CPU 限制单位

1 核 CPU 等于 1000 毫核,当定义容器为 0.5 时,所能⽤到的 CPU 资源时 1 核⼼ CPU 的⼀半,对于 CPU 资源单位,表达式 0.1 等价于表达式 100m,可以看作 100 millicpu

在 Kubernetes 系统上,1 个单位的 CPU 相当于虚拟机上的 1 颗虚拟 CPU(vCPU)或物理机上的一个超线程(Hyperthread,或称为一个逻辑 CPU),它支持分数计量方式,一个核心(1core)相当于 1000 个微核心(millicores),因此 500m 相当于是 0.5 个核心,即二分之一个核心

shell
1 核⼼ = 1000 millicpu (1 Core = 1000m)
0.5  = 500 millicpu (0.5 Core = 500m)
1 核⼼ = 1000 millicpu (1 Core = 1000m)
0.5  = 500 millicpu (0.5 Core = 500m)

💡 说明

m 毫核,cpu 单位;Kubernetes 集群中的每一个节点可以通过操作系统的命令来确认本节点的 CPU 内核数量,然后将这个数量乘以 1000,得到的就是节点总 CPU 总毫数。比如一个节点有四核,那么该节点的 CPU 总毫量为 4000m,如果你要使用 0.5 core,则你要求的是 4000*0.5 = 2000m :::

举例:当我们有 1 个物理 CPU,16 核⼼,如果某个 Pod 最多使⽤⼀半的核⼼数,则表达式可以写⼊如下两种:

yaml
limits.cpu: 8

limits.cpu: 8000m 计算公式:(16000*0.5=8000m)
limits.cpu: 8

limits.cpu: 8000m 计算公式:(16000*0.5=8000m)

❌ 注意

Kubernetes 不允许设置精度⼩于 1m 的 CPU 资源。因此当 CPU 单位⼩于 1 时,只能使⽤毫核来表示。例如:期望使⽤ 1 个 CPU 的 0.5%,应该写 5m ⽽不是 0.005

2.2 内存分配单位

内存的基本单位是字节数(Bytes),也可以加上国际单位,⼗进制的 E、P、T、G、M,K、m,或⼆进制的 Ei、Pi、Ti、Gi、Mi、Ki,内存的计量方式与日常使用方式相同

MiB ≠ MB,MB 是十进制单位,MiB 是二进制,平时我们以为 MB 等于 1024KB,其实 1MB=1000KB,1MiB 才等于 1024KiB。中间带字母 i 的是国际电工协会(IEC)定的,走 1024 乘积;KB、MB、GB 是国际单位制,走 1000 乘积

shell
1MB = 1000 KB = 1000000 Bytes
1Mi = 1024 KB = 1048576 bytes
1MB = 1000 KB = 1000000 Bytes
1Mi = 1024 KB = 1048576 bytes

❌ 注意

如果你为某个资源指定了限制,但不指定请求, 并且没有应用准入时机制为该资源设置默认请求, 然后 Kubernetes 复制你所指定的限制值,将其用作资源的请求值

内存的计量方式与日常使用方式相同

2.3 计算

cpu

shell
##查看CPU的限制##
[root@k8s-node1 ~]# docker exec -it 6933185dbe4b /bin/bash

root@pod-limits:/# cd /sys/fs/cgroup/cpu/

root@pod-limits:/sys/fs/cgroup/cpu# cat cpu.cfs_period_us
100000
root@pod-limits:/sys/fs/cgroup/cpu# cat cpu.cfs_quota_us
200000

#反向计算出--cpus参数
#cpu.cfs_quota_us / cpu.cfs_period_us = cpu的限制
root@pod-limits:/sys/fs/cgroup/cpu# expr `cat cpu.cfs_quota_us` / `cat cpu.cfs_period_us`
2
##查看CPU的限制##
[root@k8s-node1 ~]# docker exec -it 6933185dbe4b /bin/bash

root@pod-limits:/# cd /sys/fs/cgroup/cpu/

root@pod-limits:/sys/fs/cgroup/cpu# cat cpu.cfs_period_us
100000
root@pod-limits:/sys/fs/cgroup/cpu# cat cpu.cfs_quota_us
200000

#反向计算出--cpus参数
#cpu.cfs_quota_us / cpu.cfs_period_us = cpu的限制
root@pod-limits:/sys/fs/cgroup/cpu# expr `cat cpu.cfs_quota_us` / `cat cpu.cfs_period_us`
2

cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的所有进程在单位时间里可以使用的 cpu 时间。 cpu.cfs_period_us:时间周期(微秒); cpu.cfs_quota_us:指的是在 cpu.cfs_period_us 周期内可使用的 cpu 的时间(微秒);多核场景下,如配置 cpu.cfs_period_us=10000,而 cfs_quota_us=20000,表示该 cgroup 可以完全使用 2 个 cpu。所以 cpu 的计算方法为:cfs_quota_us / cpu.cfs_period_us

mem

shell
##查看内存的限制##
[root@k8s-node1 ~]# docker exec -it 6933185dbe4b /bin/bash

root@pod-limits:~# cd /sys/fs/cgroup/memory/

root@pod-limits:/sys/fs/cgroup/memory# cat memory.limit_in_bytes
2147483648 (Byte字节)

内存的计算方法为:2147483648÷1024÷1024÷1024 = 2(G)
##查看内存的限制##
[root@k8s-node1 ~]# docker exec -it 6933185dbe4b /bin/bash

root@pod-limits:~# cd /sys/fs/cgroup/memory/

root@pod-limits:/sys/fs/cgroup/memory# cat memory.limit_in_bytes
2147483648 (Byte字节)

内存的计算方法为:2147483648÷1024÷1024÷1024 = 2(G)

3.实践

安装 metrics-server

官方文档,https://github.com/kubernetes-sigs/metrics-server

requests

shell
kubectl explain pod.spec.containers.resources
kubectl explain pod.spec.containers.resources
yaml
[root@kube-master yaml]# cat requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: requests-pod
spec:
  containers:
  - image: busybox
    name: busybox
    args:
      - "/bin/sh"
      - "-c"
      - "sleep 60000"
    resources:
      requests:			#资源申请
        cpu: 500m		#容器申请500毫核(一个CPU核心时间的1/2)
        memory: 500Mi	#容器申请500M内存
[root@kube-master yaml]# cat requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: requests-pod
spec:
  containers:
  - image: busybox
    name: busybox
    args:
      - "/bin/sh"
      - "-c"
      - "sleep 60000"
    resources:
      requests:			#资源申请
        cpu: 500m		#容器申请500毫核(一个CPU核心时间的1/2)
        memory: 500Mi	#容器申请500M内存
  • 超出内存
shell
[root@kube-master yaml]# cat requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: requests-pod
spec:
  containers:
  - image: busybox
    name: busybox
    args:
      - "/bin/sh"
      - "-c"
      - "sleep 60000"
    resources:
      requests:
        cpu: 500m
        memory: 2048Mi
  nodeName: kube-node01
[root@kube-master yaml]# cat requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: requests-pod
spec:
  containers:
  - image: busybox
    name: busybox
    args:
      - "/bin/sh"
      - "-c"
      - "sleep 60000"
    resources:
      requests:
        cpu: 500m
        memory: 2048Mi
  nodeName: kube-node01
shell
[root@kube-master yaml]# kubectl get pod
NAME                              READY   STATUS        RESTARTS        AGE
requests-pod                      0/1     OutOfmemory   0               4s
[root@kube-master yaml]# kubectl get pod
NAME                              READY   STATUS        RESTARTS        AGE
requests-pod                      0/1     OutOfmemory   0               4s
shell
[root@kube-master yaml]# kubectl describe pod requests-pod
...
Status:       Failed
Reason:       OutOfmemory
Message:      Pod Node didn't have enough resource: memory, requested: 2147483648, used: 0, capacity: 1934282752
...
Events:
  Type     Reason       Age   From     Message
  ----     ------       ----  ----     -------
  Warning  OutOfmemory  10s   kubelet  Node didn't have enough resource: memory, requested: 2147483648, used: 0, capacity: 1934282752
[root@kube-master yaml]# kubectl describe pod requests-pod
...
Status:       Failed
Reason:       OutOfmemory
Message:      Pod Node didn't have enough resource: memory, requested: 2147483648, used: 0, capacity: 1934282752
...
Events:
  Type     Reason       Age   From     Message
  ----     ------       ----  ----     -------
  Warning  OutOfmemory  10s   kubelet  Node didn't have enough resource: memory, requested: 2147483648, used: 0, capacity: 1934282752

limits

yaml
[root@kube-master yaml]# cat limit-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: limited-pod
spec:
  containers:
  - image: busybox
    command: ["sleep","600000"]
    name: busybox
    resources:
      requests:         #资源申请
        cpu: 200m       #容器申请200毫核(一个CPU核心时间的1/5)
        memory: 80Mi    #容器申请80M内存
      limits:           #资源限制
        cpu: 2          #容器最大允许使用2核CPU
        memory: 2Gi     #容器最大允许使用2GB内存
  nodeName: kube-node01
[root@kube-master yaml]# cat limit-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: limited-pod
spec:
  containers:
  - image: busybox
    command: ["sleep","600000"]
    name: busybox
    resources:
      requests:         #资源申请
        cpu: 200m       #容器申请200毫核(一个CPU核心时间的1/5)
        memory: 80Mi    #容器申请80M内存
      limits:           #资源限制
        cpu: 2          #容器最大允许使用2核CPU
        memory: 2Gi     #容器最大允许使用2GB内存
  nodeName: kube-node01
shell
kubectl describe nodes kube-node01
kubectl describe nodes kube-node01

image-20240426141514746

❌ 注意

节点上所有 pod 的资源 limits 之和可以超过节点资源总量的 100%

requests 不同的是,limits 并不会影响 pod 的调度结果

如果只设置了 limit,则 request 默认会根据 limit 自动设定

运⾏超过容器内存限制的应⽤

当节点拥有足够的可用内存时,容器可以使用其请求的内存。但是,容器不允许使用超过其限制的内存。如果容器分配的内存超过其限制,该容器会成为被终止的候选容器。如果容器继续消耗超出其限制的内存,则终止容器。如果终止的容器可以被重启,则 kubelet 会重新启动它。

yaml
[root@kube-master yaml]# cat memory-request-limit-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memory-request-limit-2
spec:
  containers:
  - name: memory-request
    image: polinux/stress
    command: ["stress", "--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"] ## 容器会尝试分配 250 MiB 内
存,这远⾼于 100 MiB 的限制,模拟1个进程产⽣250M内存
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"
[root@kube-master yaml]# cat memory-request-limit-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memory-request-limit-2
spec:
  containers:
  - name: memory-request
    image: polinux/stress
    command: ["stress", "--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"] ## 容器会尝试分配 250 MiB 内
存,这远⾼于 100 MiB 的限制,模拟1个进程产⽣250M内存
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"

4.资源配置范围管理 LimitRange

4.1 概念

默认情况下,Kubernetes 中所有容器都没有任何 CPU 和内存限制。LimitRange 用来给 Namespace 增加一个资源限制,包括最小、最大和默认资源。

4.2 为什么需要 LimitRange

为单个容器设置资源 requests 和 limits 很有必要性:

1.提升 QoS 等级,防止在 OOM 时被首先 kill;

2.默认情况下 Pod 会以无限制的 CPU 和内存运行,很有可能因故吞掉所在工作节点上的所有可用计算资源。

通过配置 Pod 的计算资源 Requests 和 Limits,我们可以限制 Pod 的资源使用,但对于 Kubemetes 集群管理员而言,配置每一个 Pod 的 Requests 和 Limits 是烦琐且限制性过强的。更多时,我们需要的是对集群内 Requests 和 Limits 的配置做一个全局的统一的限制。

💡 说明

LimitRange 资源支持限制 Container、Pod 和 PersistentVolumeClaim 三种资源对象的系统资源用量

创建

yaml
# more limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: limitrange
spec:
  limits:
    - type: Pod #指定整个pod的资源limits
      min: #pod中所有容器的Requests值的总和的下限
        cpu: 50m
        memory: 5Mi
      max: #pod中所有容器的Limits值的总和的上限
        cpu: 1
        memory: 1Gi
    - type: Container #指定容器的资源限制
      defaultRequest: #容器Requests默认值
        cpu: 100m
        memory: 10Mi
      default: #容器Limits默认值
        cpu: 200m
        memory: 100Mi
      min: #pod中所有容器的Requests值的下限
        cpu: 50m
        memory: 5Mi
      max: #pod中所有容器的Limits值的上限
        cpu: 1
        memory: 1Gi
      maxLimitRequestRatio: #每种资源Requests与Limits的最大比值
        cpu: 4
        memory: 10
    - type: PersistentVolumeClaim #指定请求PVC存储容量的最小值和最大值
      min:
        storage: 1Gi
      max:
        storage: 10Gi
# more limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: limitrange
spec:
  limits:
    - type: Pod #指定整个pod的资源limits
      min: #pod中所有容器的Requests值的总和的下限
        cpu: 50m
        memory: 5Mi
      max: #pod中所有容器的Limits值的总和的上限
        cpu: 1
        memory: 1Gi
    - type: Container #指定容器的资源限制
      defaultRequest: #容器Requests默认值
        cpu: 100m
        memory: 10Mi
      default: #容器Limits默认值
        cpu: 200m
        memory: 100Mi
      min: #pod中所有容器的Requests值的下限
        cpu: 50m
        memory: 5Mi
      max: #pod中所有容器的Limits值的上限
        cpu: 1
        memory: 1Gi
      maxLimitRequestRatio: #每种资源Requests与Limits的最大比值
        cpu: 4
        memory: 10
    - type: PersistentVolumeClaim #指定请求PVC存储容量的最小值和最大值
      min:
        storage: 1Gi
      max:
        storage: 10Gi

查看

shell
#kubectl describe limitranges limitrange

Name:                  limitrange
Namespace:             default
Type                   Resource  Min  Max   Default Request  Default Limit  Max Limit/Request Ratio
----                   --------  ---  ---   ---------------  -------------  -----------------------
Pod                    cpu       50m  1     -                -              -
Pod                    memory    5Mi  1Gi   -                -              -
Container              memory    5Mi  1Gi   10Mi             100Mi          10
Container              cpu       50m  1     100m             200m           4
PersistentVolumeClaim  storage   1Gi  10Gi  -                -              -
#kubectl describe limitranges limitrange

Name:                  limitrange
Namespace:             default
Type                   Resource  Min  Max   Default Request  Default Limit  Max Limit/Request Ratio
----                   --------  ---  ---   ---------------  -------------  -----------------------
Pod                    cpu       50m  1     -                -              -
Pod                    memory    5Mi  1Gi   -                -              -
Container              memory    5Mi  1Gi   10Mi             100Mi          10
Container              cpu       50m  1     100m             200m           4
PersistentVolumeClaim  storage   1Gi  10Gi  -                -              -

LimitRange 中各项配置的意义和特点如下:

  1. 不论是 CPU 还是内存,在 LimitRange 中,Pod 和 Container 都可以设置 Min、Max 和 Max Limit / Requests Ratio 参数。Container 还可以设置 Default Request 和 Default Limit 参数,而 Pod 不能设置 Default Request 和Default Limit 参数。
  2. 对 Pod 和 Container 的参数解释如下。
    • Container:
      • Container 的 Min(上面的 100m 和 3Mi)是 Pod 中所有容器的 Requests 值下限;
      • Container 的 Max(上面的 2 和 1Gi)是 Pod 中所有容器的 Limits 值上限;
      • Container 的 Default Request(上面的 200m 和 100Mi)是 Pod 中所有未指定 Requests 值的容器的默认 Requests 值;
      • Container 的 Default Limit(上面的300m和200Mi)是 Pod 中所有未指定 Limits 值的容器的默认 Limits 值。
      • 对于同一资源类型,这 4 个参数必须满足以下关系:Min ≤ Default Request ≤ Default Limit ≤ Max。
    • Pod:
      • Pod 的 Min(上面的 200m 和 6Mi)是 Pod 中所有容器的 Requests 值的总和下限;
      • Pod 的 Max(上面的 4 和 2Gi)是 Pod 中所有容器的 Limits 值的总和上限。
      • 容器未指定 Requests 值或者 Limits 值时,将使用 Container 的 Default Request 值或者 Default Limit 值。
    • Container 的 Max Limit/Requests Ratio(上面的 5 和 4)限制了 Pod 中所有容器的 Limits 值与 Requests 值的比例上限;而 Pod 的 Max Limit / Requests Ratio(上面的 3 和 2)限制了 Pod 中所有容器的 Limits 值总和与 Requests 值总和的比例上限。
  3. 如果设置了 Container 的 Max,那么对于该类资源而言,整个集群中的所有容器都必须设置 Limits,否则无法成功创建。Pod 内的容器未配置 Limits 时,将使用 Default Limit 的值(本例中的 300m CPU 和 200MiB 内存),如果也未配置 Default,则无法成功创建。
  4. 如果设置了 Container 的 Min,那么对于该类资源而言,整个集群中的所有容器都必须设置 Requests。如果创建 Pod 的容器时未配置该类资源的 Requests,那么在创建过程中会报验证错误。Pod 里容器的 Requests 在未配置时,可以使用默认值 defaultRequest(本例中的 200m CPU 和 100MiB 内存);如果未配置而且没有使用默认值 defaultRequest,那么默认等于该容器的 Limits;如果容器的 Limits 也未定义,就会报错。
  5. 对于任意一个 Pod 而言,该 Pod 中所有容器的 Requests 总和都必须大于或等于 6MiB,而且所有容器的 Limits 总和都必须小于或等于 1GiB;同样,所有容器的 CPU Requests 总和都必须大于或等于 200m,而且所有容器的 CPU Limits 总和都必须小于或等于 2。
  6. Pod里任何容器的 Limits 与 Requests 的比例都不能超过 Container 的Max Limit / Requests Ratio;Pod 里所有容器的 Limits 总和与 Requests 总和的比例都不能超过 Pod 的 Max Limit / Requests Ratio。

❌ 注意

需要注意的是,CPU Limits 强制配置这个选项,在 Kubernetes 集群中默认是开启的;除非在部署kubelet服务时通过设置--cpu-cfs-quota=false来关闭该限制。

测试

yaml
#cat default.yaml
apiVersion: v1
kind: Pod
metadata:
  name: default-pod
spec:
  containers:
    - image: busybox
      name: busybox
      args:
        - /bin/sh
        - -c
        - sleep 60000
#cat default.yaml
apiVersion: v1
kind: Pod
metadata:
  name: default-pod
spec:
  containers:
    - image: busybox
      name: busybox
      args:
        - /bin/sh
        - -c
        - sleep 60000
  • 查看
[root@kube-master yaml]# kubectl describe pod default-pod
[root@kube-master yaml]# kubectl describe pod default-pod

容器的 requests 和 limits 与我们在 LimitRange 对象中设置的一致

cpu 超限制

shell
# kubectl run  cpu-over --image=busybox --restart=Never --requests='cpu=1200m,memory=30Mi' sleep 6000
The Pod "cpu-over" is invalid: spec.containers[0].resources.requests: Invalid value: "1200m": must be less than or equal to cpu limit
# kubectl run  cpu-over --image=busybox --restart=Never --requests='cpu=1200m,memory=30Mi' sleep 6000
The Pod "cpu-over" is invalid: spec.containers[0].resources.requests: Invalid value: "1200m": must be less than or equal to cpu limit

内存超限制

shell
# kubectl run  cpu-over --image=busybox --restart=Never --requests='cpu=200m,memory=300Mi' sleep 6000
The Pod "cpu-over" is invalid: spec.containers[0].resources.requests: Invalid value: "300Mi": must be less than or equal to memory limit
# kubectl run  cpu-over --image=busybox --restart=Never --requests='cpu=200m,memory=300Mi' sleep 6000
The Pod "cpu-over" is invalid: spec.containers[0].resources.requests: Invalid value: "300Mi": must be less than or equal to memory limit

不指定 CPU 和 mem Limits

如果没有为容器指定 CPU 限制,那么容器在可以使用的 CPU 资源是没有上限。因而可以使用所在节点上所有的可用 CPU 资源,这样可能会造成某一个 Pod 占用了大量的 CPU 时间,可能会影响其他的 Pod 正常运行,从而造成业务的不稳定性。

这个也不用担心,在 Kubernetes 中,可以通过LimitRange自动为容器设定,所使用的 CPU 资源和内存资源最大最小值

5.资源配额管理(ResourceQuota)

资源配额管理(Resource Quotas)通过 ResourceQuota 对象,可以定义资源配额,这个资源配额可以为每个命名空间都提供一个总体的资源限制:它可以限制命名空间中某种类型的对象的总数量上限,也可以设置命名空间中 Pod 可以使用的计算资源的总上限。

在使用资源配额时,需要注意以下两点:

  1. 如果集群中总的可用资源小于各命名空间中资源配额的总和,那么可能会导致资源竞争。在发生资源竞争时,Kubernetes 系统会遵循先到先得的原则。
  2. 不管是资源竞争还是配额修改,都不会影响已创建的资源使用对象。

5.1 概念

Kubemetes 可以通过存活探针(liveness probe)检查容器是否还在运行。可以为 pod 中的每个容器单独指定存活探针。如果探测失败,Kubemetes 将定期执行探针并重新启动容器。

资源配额(Resource Quotas)是用来限制用户资源用量的一种机制,限制 Pod 的请求不会超过配额,需要在 namespace 中创建一个 ResourceQuota 对象

资源配额类型:

  • 计算资源。包括 cpu 和 memory
  • 存储资源。包括存储资源的总量以及指定 storage class 的总量
  • 对象数。即可创建的对象的个数

5.2 ResourceQuota 作用

尽管 LimitRange 资源能限制单个容器、Pod 及 PVC 等相关计算资源或存储资源的用量,但用户依然可以创建数量众多的此类资源对象进而侵占所有的系统资源。于是,Kubernetes 提供了 ResourceQuota 资源用于定义名称空间的对象数量或系统资源配额

❌ 注意

LimitRange 应用于单独的 pod,ResourceQuota 应用于命名空间中所有的 pod

image-20240426160154222

5.3 在 Master 中开启资源配额选型

资源配额可以通过在 kube-apiserver 的 --admission-control参数值中添加 ResourceQuota参数进行开启。如果在某个命名空间的定义中存在 ResourceQuota,那么对于该命名空间而言,资源配额就是默认开启的。一个命名空间可以有多个 ResourceQuota 配置项。(重启服务的时候会提示:–admission-control has been deprecated, Use --enable-admission-plugins or --disable-admission-plugins instead. Will be removed in a future version.)更换使用 --enable-admission-plugins即可。

5.3.1 计算资源配额(Compute Resource Quota)

资源配额可以限制一个命名空间中所有 Pod 的计算资源的总和。ResourceQuota 目前支持闲置的计算资源类型如表:

资源名称描述
cpu所有非终止状态的 Pod,CPU Requests 的总和不能超过该值
requests.cpu所有非终止状态的 Pod,CPU Requests 的总和不能超过该值
limits.cpu所有非终止状态的 Pod,CPU Limits 的总和不能超过该值
memory所有非终止状态的 Pod,内存 Requests 的总和不能超过该值
requests.memory所有非终止状态的 Pod,内存 Requests 的总和不能超过该值
limits.memory所有非终止状态的 Pod,内存 Limits 的总和不能超过该值

5.3.2 存储资源配额(Volume Count Quota)

可以在给定的命名空间中限制所使用的存储资源(Storage Resources)的总量,目前支持的存储资源名称如表:

资源名称描述
requests.storage所有 PVC,存储资源的需求总量不能超过该值
persistentvolumeclaims在该命名空间中所允许的 PVC 总量
storage-class-name.storageclass.storage.k8s.io/requests.storage在所有与<storage-class-name> 相关的 PVC 请求的存储总量都不能超过该值,例如 gold.storageclass.storage.k8s.io/requests.storage: 500Gi表示类型为 gold 的 storageClass 对应的 PVC 的申请存储总量最多可达 500Gi
storage-class-name.storageclass.storage.k8s.io/persistentvolumeclaims所有与<storage-class-name> 相关的 PVC 总数都不超过该值。
ephemeral-storage、requests.ephemeral-storage、limits.ephemeral-storage本地临时存储(ephemeral-storage)的总量限制

5.3.3 对象数量配额(Object Count Quota)

指定类型的对象数量可以被限制,例如,可以通过资源配额来限制在命名空间中创建的 Pod 的最大数量。这种配置可以防止某些用户大量创建 Pod 而迅速耗尽整个集群的 Pod IP 和计算资源。下表列出 ResourceQuota 支持限制的对象类型。

资源名称描述
configmaps在该命名空间中允许存在的 ConfigMap 总数上限。
pods在该命名空间中能存在的非终止状态 Pod 的总数上限。Pod 的终止状态等价于 Pod 的 status.phase in (Failed, Successded) = true
replicationcontrollers在该命名空间中能存在的 RC 的总数上限。
resourcequotas在该命名空间中能存在的资源配额的总数上限。
services在该命名空间中能存在的 Service 的总数上限。
services.loadbalancers在该命名空间中能存在的 LoadBalancer 类型的 Service 总数上限。
services.nodeports在该命名空间中能存在的 NodePort 类型的 Service 总数上限。
secrets在该命名空间中能存在的 Secret 的总数上限。

具体表示如下:

  • count/<resource>.<group>: 用于非核心(core)组成的资源,例如 count/deployments.appscount/cronjobs.batch
  • count/resource: 用于核心组的资源,例如 count/servicescount/pods

5.4 配额的作用域(Quota Scopes)

对每项资源配额都可以单独配置一组作用域,配置了作用域的资源配额只会对符合其作用域的资源使用情况进行计量和限制,作用域范围超出了资源配额的请求都会被报验证错误。下表列出了 ResourceQuota 的 4 种作用域。

作用域说明
Terminating匹配所有 spec.activeDeadlineSeconds 不小于 0 的 Pod
NotTerminating匹配所有 spec.activeDeadlineSeconds 是 nil 的 Pod
BestEffort匹配所有 Qos 是 BestEffort 的 Pod
NotBestEffort匹配所有 Qos 不是 BestEffort 的 Pod
PriorityClass匹配所有引用了所指定的优先级类的 Pods

其中:

  • BestEffort 作用域可以限定资源配额来追踪 Pod 资源的使用。
  • 而 Terminating、NotTerminating、NotBestEffort、PriorityClass 除了可以追踪 Pod,还可以追踪 CPU、Limits.cpu、Limits.memory、memory、requests.cpu、 requests.memory 等资源的使用情况。

5.5 资源配额的定义

与 LimitRange 相似,ResourceQuota 也被设置在命名空间中。创建名为 myspace 的命名空间:

kubectl create namespace myspace
kubectl create namespace myspace

创建 ResourceQuota 配置文件 compute-resource.yaml

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
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
 kubectl create -f compute-resource.yaml --namespace=myspace
 kubectl create -f compute-resource.yaml --namespace=myspace

创建另一个名为 object-counts.yaml 的文件,用于设置对象数量的配额

yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"

创建该 ResourceQuota:

kubectl create -f object-counts.yaml --namespace=myspace
kubectl create -f object-counts.yaml --namespace=myspace

查看各 ResourceQuota 的详细信息

 kubectl describe quota compute-resources --namespace=myspace
 
 Name:            compute-resources
Namespace:       myspace
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     2Gi
pods             0     4
requests.cpu     0     1
requests.memory  0     1Gi
[root@master1 ]# kubectl describe quota object-counts --namespace=myspace 
Name:                   object-counts
Namespace:              myspace
Resource                Used  Hard
--------                ----  ----
configmaps              0     10
persistentvolumeclaims  0     4
replicationcontrollers  0     20
secrets                 1     10
services                0     10
services.loadbalancers  0     2
 kubectl describe quota compute-resources --namespace=myspace
 
 Name:            compute-resources
Namespace:       myspace
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     2Gi
pods             0     4
requests.cpu     0     1
requests.memory  0     1Gi
[root@master1 ]# kubectl describe quota object-counts --namespace=myspace 
Name:                   object-counts
Namespace:              myspace
Resource                Used  Hard
--------                ----  ----
configmaps              0     10
persistentvolumeclaims  0     4
replicationcontrollers  0     20
secrets                 1     10
services                0     10
services.loadbalancers  0     2

❌ 注意

资源配额与集群资源总量是完全独立的。资源配额是通过绝对的单位来配置的,这也就意味着如果在集群中新添加了节点,那么资源配额不会自动更新,而该资源配额所对应的命名空间中的对象也不能自动增加资源上限.

5.3 创建

shell
#cat quota-cpu-memory.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: cpu-and-mem
spec:
  hard:
    requests.cpu: 1
    requests.memory: 1Gi
    limits.cpu: 1500m
    limits.memory: 1500Mi

# kubectl apply -f quota-cpu-memory.yaml
resourcequota/cpu-and-mem created
#cat quota-cpu-memory.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: cpu-and-mem
spec:
  hard:
    requests.cpu: 1
    requests.memory: 1Gi
    limits.cpu: 1500m
    limits.memory: 1500Mi

# kubectl apply -f quota-cpu-memory.yaml
resourcequota/cpu-and-mem created

查看

yaml
#kubectl describe resourcequotas cpu-and-mem
#kubectl describe resourcequotas cpu-and-mem

测试 resourcequota

  • 创建
[root@kube-master yaml]# cat replicas-quota-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: replicas-quota-test
spec:
  replicas: 10
  selector:
    matchLabels:
      app: replicas-quota-test
  template:
    metadata:
      labels:
        app: replicas-quota-test
    spec:
      containers:
      - name: replicas-quota-test
        image: busybox
        command: ["/bin/sh","-c","sleep 60000"]
        resources:
          limits:
            cpu: "200m"
            memory: "90Mi"
[root@kube-master yaml]# cat replicas-quota-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: replicas-quota-test
spec:
  replicas: 10
  selector:
    matchLabels:
      app: replicas-quota-test
  template:
    metadata:
      labels:
        app: replicas-quota-test
    spec:
      containers:
      - name: replicas-quota-test
        image: busybox
        command: ["/bin/sh","-c","sleep 60000"]
        resources:
          limits:
            cpu: "200m"
            memory: "90Mi"
  • 查看
[root@kube-master yaml]# kubectl get pod
NAME                                   READY   STATUS    RESTARTS         AGE
replicas-quota-test-84bd8d9fb4-bnbm9   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-ctjdz   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-n2kv9   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-n445x   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-s8jt2   1/1     Running   0                7m6s
[root@kube-master yaml]# kubectl get pod
NAME                                   READY   STATUS    RESTARTS         AGE
replicas-quota-test-84bd8d9fb4-bnbm9   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-ctjdz   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-n2kv9   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-n445x   1/1     Running   0                7m6s
replicas-quota-test-84bd8d9fb4-s8jt2   1/1     Running   0                7m6s

💡 说明

发现只运行了5个pod,原因是requests的cpu为200(requests值未设置时与limits相同),resourcequotas中requests.cpu的限制值为1500m,所以最多只能新建5个pod

限制可创建对象的个数

  • 创建
yaml
[root@kube-master yaml]# cat quota-count.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: count-quota
spec:
  hard:
    pods: 10
    replicationcontrollers: 5
    secrets: 10
    configmaps: 10
    persistentvolumeclaims: 5
    services: 5
    services.loadbalancers: 1
    services.nodeports: 2
    ssd.storageclass.storage.k8s.io/persistentvolumeclaims: 2
[root@kube-master yaml]# cat quota-count.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: count-quota
spec:
  hard:
    pods: 10
    replicationcontrollers: 5
    secrets: 10
    configmaps: 10
    persistentvolumeclaims: 5
    services: 5
    services.loadbalancers: 1
    services.nodeports: 2
    ssd.storageclass.storage.k8s.io/persistentvolumeclaims: 2

该命名空间最多创建 10 个 pod、5 个 Replication Controller、10 个 Secret、10 个 ConfigMap、4 个 PVC、5 个 Service、1 个 LoadBalancer、2 个 NodePort 和 2 个 StorageClass 为 ssd 的 PVC。

podReplication ControllerSecretConfigMapPVCServiceLoadBalancerNodePortssd PVC
105101045122
  • 查看
shell
[root@kube-master yaml]# kubectl apply  -f quota-count.yaml
resourcequota/count-quota created

[root@kube-master yaml]# kubectl describe resourcequotas count-quota
Name:                                                   count-quota
Namespace:                                              default
Resource                                                Used  Hard
--------                                                ----  ----
configmaps                                              1     10
persistentvolumeclaims                                  0     5
pods                                                    11    10
replicationcontrollers                                  0     5
secrets                                                 1     10
services                                                3     5
services.loadbalancers                                  0     1
services.nodeports                                      1     2
ssd.storageclass.storage.k8s.io/persistentvolumeclaims  0     2
[root@kube-master yaml]# kubectl apply  -f quota-count.yaml
resourcequota/count-quota created

[root@kube-master yaml]# kubectl describe resourcequotas count-quota
Name:                                                   count-quota
Namespace:                                              default
Resource                                                Used  Hard
--------                                                ----  ----
configmaps                                              1     10
persistentvolumeclaims                                  0     5
pods                                                    11    10
replicationcontrollers                                  0     5
secrets                                                 1     10
services                                                3     5
services.loadbalancers                                  0     1
services.nodeports                                      1     2
ssd.storageclass.storage.k8s.io/persistentvolumeclaims  0     2

6.Pod 服务质量 QoS

容器的资源配置满足以下两个条件:

  • Requests ≤ 节点可用资源
  • Requests ≤ Limits

6.0 资源

可压缩资源

  • Kubernetes 目前支持的可压缩资源是 CPU。
  • Pod 可以得到 Requests 配置的 CPU 使用量,而能否使用超过 Requests 值的部分取决于系统的负载和调度。
  • 空闲的 CPU 资源按照容器 Requests 值的比例分配。比如容器 A 的 CPU 配置为 Requests 1 Limits 10,容器 B 的 CPU 配置为 Requests 2 Limits 8。初始状态下容器可以用的 CPU 为 3cores,那么 A 和 B 恰好得到再其 Requests 中定义的 CPU 用量,即 1CPU 和 2CPU。如果 A 和 B 都需要更多的 CPU 资源,而恰好此时系统的其他任务释放了 1.5CPU,那么这 1.5CPU 将按照 A 和 B 的 Requests 值的比例 1:2 分配给 A 和 B,即最终 A 可使用 1.5CPU,B 可使用 3CPU。
  • 如果 Pod 的 CPU 用来超过了在 Limits 10 中配置的 CPU 用量,那么 cgroups 会对 Pod 中容器的 CPU 用来进行限流(Throttled);如果 Pod 没有配置 Limits 10,那么 Pod 会尝试抢占所有空间的 CPU 资源。

不可压缩资源

  • Kubernetes 目前支持的不可压缩资源是内存。
  • Pod 可以得到在 Requests 中配置的内存。
    • 如果 Pod 的内存用量小于它的 Requests 的配置,那么这个 Pod 可以正常运行(除非出现操作系统级别内存不足等严重问题)。
    • 如果 Pod 的内存用量超过它的 Requests 的配置,那么这个 Pod 有可能会被 Kubernetes “杀掉”:
      • 比如 Pod A 使用的内存量在 Requests ~ Limits 之间,同一机器上的另一个 Pod B 之前使用的内存小于 Requests 的值,此时 Pod B 压力增大,Pod B 向系统申请的总量不超过自己的 Requests 值的内存,那么 Pod A 可能会直接被 Kubernetes “杀掉”。
      • 另一种情况是 Pod A 使用的内存量在 Requests ~ Limits 之间,此时 Kubernetes 将一个新的 Pod 调度到这台机器上,只有 Pod A 使用量超过自己的 Requests 值的内存,那么也可能会直接被 Kubernetes “杀掉”。
  • 如果 Pod 的内存用量超过它的 Limits 值,那么操作系统内核会“杀掉”Pod 所有容器的所有进程中内存使用量最多的一个,直到内存不超过 Limits 时为止。

对调度策略的影响

  • Kubernetes 的 kube-scheduler 通过计算 Pod 中所有容器的 Requests 总和来决定对 Pod 的调度。
  • 不管是 CPU 还是内存,Kubernetes 调度器和 kubelet 都会确保节点上的所有 Pod 的 Requests 总和不会超过在该节点上可分配给容器使用的资源容量上限。

6.1 QoS 定义

QoS(QualityofService),可译为"服务质量等级",或者译作"服务质量保证",是作用在 Pod 上的一个配置,当

Kubernetes 创建一个 Pod 时,它就会给这个 Pod 分配一个 QoS 等级。

在 Kubernetes 的环境中,Kubernetes 允许节点的 Pod 过载使用资源,这意味着节点无法同时满足所有 Pod 以过载的方式运行。因此在内存资源紧缺的情况下,Kubernetes 需要借助 Pod 对象的服务质量和优先级等完成判定,进而挑选对应的 Pod 杀死。Kubernetes 根据 pod 的 Requests 和 Limits 属性,把 Pod 对象归类为三类: Guaranteed(完全可靠的)Burstable(弹性波动、较可靠的)BestEffort(尽力而为、不太可靠的),这三种依次递减的优先级来决定优先“杀掉”哪些容器。

优先级,Guaranteed > BurStabLe > BestEffort

6.2 QoS 类别

Guaranteed(完全可靠的)

如果 Pod 中的所有容器对所有资源类型都定义了 Limits 和 Requests,并且所有容器的 Limits 值都和 Requests 值相等(且都不为 0),那么该 Pod 的 QoS 级别就是 Guaranteed。

❌ 注意

在这种情况下,容器可以不定义 Requests,因为 Requests 值在未定义时默认等于 Limits。

Burstable(弹性波动、较可靠的)

Burstable 级别的 Pod 涉及两种情况:

  1. Pod 中的一部分容器在一种或多种类型的资源配置中定义了 Requests 值和 Limits 值(都不为 0),且 Requests 值小于 Limits 值。
  2. Pod 中的一部分容器未定义资源配置(Requests 和 Limits 都未定义)。

❌ 注意

容器未定义 Limits 时,Limits 值默认等于节点资源容量的上限。

BestEffort(尽力而为、不太可靠的)

如果 Pod 中所有容器都未定义资源配置(Requests 和 Limits 都未定义),那么该 Pod 的 QoS 级别就是 BestEffort。

当 Kubernetes 集群内存资源紧缺,优先杀死 BestEffort 类别的容器,因为系统不为该类资源提供任何服务保证,但此类资源最⼤的好处就是能够尽可能的使⽤资源.

如果系统中没有 BestEffort 类别的容器,接下来就轮到 Burstable 类别的容器,如果有多个 Burstable 类别的容器,就看谁的内存资源占⽤多,就优先⼲掉谁。⽐如 A 容器申请 1G 内存资源,实际使⽤了 95%,⽽ B 容器申请了 2G 内存资源,实际使⽤了 80%,但任然会优先⼲掉 A 容器,虽然 A 容器的⽤量少,但与⾃身的 Requests 值相⽐,它的占⽐要⼤于 B 容器。

对于 Guaranteed 类别的容器拥有最高优先级,它们不会被杀死,除非其内存资源需求超限,或者 00M 时没有其他更低优先级的 Pod 对象存在,才会干掉 Guaranteed 类容器.

6.3 QoS 的工作特点

在 Pod 的 CPU Requests 无法得到满足(比如节点的系统任务占用过多的 CPU 导致无法分配足够的 CPU 给容器使用)时,容器得到的 CPU 会被限流

由于内存是不可压缩的资源,所以针对内存资源紧缺的情况,会按照以下逻辑处理:

  1. BestEffort Pod 的优先级最低,在这类 Pod 中运行的进程会在系统内存紧缺时被第一优先“杀掉”。从另一个角度看,由于没有设置资源 Limits,所以在资源充足时它们可以充分利用所有闲置资源。
  2. Burstable Pod 的优先级居中,这类 Pod 在初始时会被分配较少的可靠资源,但可以按需申请更多资源。如果整个系统内存紧缺,又没有 BestEffort 容器可以被“杀掉”以释放资源,那么这类 Pod 中的进程可能会被“杀掉”。
  3. Guaranteed Pod 的优先级最高,而且一般情况下这类 Pod 只要不超过其资源 Limits 的限制就不会被“杀掉”。如果整个系统内存紧缺,又没有其他优先级更低的容器可以被“杀掉”以释放资源,那么这类 Pod 中的进程也可能会被“杀掉”。

6.4 OOM 计分规则

OOM(Out Of Memory)计分规则包括如下内容:

  • OOM 计分的计算方法:计算进程所使用的内存在系统中所占的百分比,取其中不含百分号的数值,再乘以 10,该结果是进程 OOM 的基础分;将进程 OOM 基础分的分值再加上这个进程 OOM_SCORE_ADJ(分数调整)值,作为进程 OOM 的最终分值(除 root 启动的进程外)。在系统发生 OOM 时,OOM Killer 会优先“杀掉”OOM 计分更高的进程。
  • 进程的 OOM 计分的基本分数范围是 0 ~ 1000,如果 A 进程的调整值 OOM_SCORE_ADJ 减去 B 进程的调整值的结果大于 1000,那么 A 进程的 OOM 计分最终值必然大于 B 进程,会优先“杀掉”A 进程。
  • 不论调整 OOM_SCORE_ADJ 值为多少,任何进程的最终分值范围也是 0 ~ 1000。

在 Kubernetes 中,不同 QoS 的 OOM 计分调整值如表所示:

QoSoom_score_adj
Guaranteed-998
BestEffort1000
Burstablemin(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

驱逐

具体地说,当 Kubernetes 所管理的宿主机上不可压缩资源短缺时,就有可能触发 Eviction 驱逐。目前,Kubernetes 为你设置的 Eviction 的默认阈值如下所示:

memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%
memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%

当宿主机的 Eviction 阈值达到后,就会进入 MemoryPressure 或者 DiskPressure 状态,从而避免新的 Pod 被调度到这台宿主机上,然后 kubelet 会根据 QoS 的级别来挑选 Pod 进行驱逐,

具体驱逐优先级是:BestEffort -> Burstable -> Guaranteed。

QoS 的级别是通过 Linux 内核 OOM 分数值来实现的,OOM 分数值取值范围在-1000 ~1000 之间。在 Kubernetes 中,常用服务的 OOM 的分值如下:

-1000  => sshd等进程 
-999   => Kubernetes 管理进程
-998   => Guaranteed Pod
0      => 其他进程 0
2~999  => Burstable Pod  
1000   => BestEffort Pod
-1000  => sshd等进程 
-999   => Kubernetes 管理进程
-998   => Guaranteed Pod
0      => 其他进程 0
2~999  => Burstable Pod  
1000   => BestEffort Pod

OOM 分数越高,就代表这个 Pod 的优先级越低,在出现资源竞争的时候,就越早被杀掉,分数为-999 和-1000 的进程永远不会因为 OOM 而被杀掉。

❌ 注意

如果期望 Pod 尽可能的不被驱逐,就应当把 Pod 里的每一个 Container 的 requests 和 limits 都设置齐全,并且 requests 和 limits 值要相等。

6.5 案例

创建 Guaranteed 的 Pod

对于 QoS 类为 Guaranteed 的 Pod

  • Pod 中的每个容器都必须指定内存请求和内存限制,且 Pod 中每个容器内存请求必须等于内存限制

  • Pod 中的每个容器都必须指定 CPU 请求和 CPU 限制,且 Pod 中每个容器 CPU 请求必须等于 CPU 限制

    1.创建⼀个 Pod,容器设置了内存请求和内存限制,值都是 200MiB。容器设置了 CPU 请求和 CPU 限制,值都是 700 milliCPU.

yaml
cat pod-qos-guaranteed.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-guaranteed
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        cpu: "500m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "128Mi"
cat pod-qos-guaranteed.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-guaranteed
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        cpu: "500m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "128Mi"
shell
#查看
[root@kube-master ~]# kubectl describe pod pod-qos-guaranteed
。。。
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        500m
      memory:     128Mi
。。。
QoS Class:                   Guaranteed
。。。
#查看
[root@kube-master ~]# kubectl describe pod pod-qos-guaranteed
。。。
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        500m
      memory:     128Mi
。。。
QoS Class:                   Guaranteed
。。。

创建 Burstable 的 Pod

如果满⾜下⾯条件,将会指定 Pod 的 QoS 类为 Burstable:

  • Pod 不符合 Guaranteed QoS 类的标准

  • Pod 中⾄少⼀个容器指定了,内存或 CPU 的请求或限制

    1.创建⼀个 Pod,容器设置了内存请求 100 MiB,以及内存限制 200MiB

yaml
cat pod-qos-burstable.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-burstable
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        memory: "128Mi"
      limits:
        memory: "128Mi"
cat pod-qos-burstable.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-burstable
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        memory: "128Mi"
      limits:
        memory: "128Mi"
shell
#查看
[root@kube-master yaml]# kubectl describe pod pod-qos-burstable

。。。
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        500m
      memory:     128Mi
。。。
QoS Class:                   Burstable
。。。
#查看
[root@kube-master yaml]# kubectl describe pod pod-qos-burstable

。。。
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        500m
      memory:     128Mi
。。。
QoS Class:                   Burstable
。。。

创建 BestEffort 的 Pod

对于 QoS 类为 BestEffort 的 Pod,Pod 中的容器必须 没有设置内存和 CPU 限制或请求

1.创建⼀个 Pod,容器没有设置内存和 CPU 限制或请求

yaml
[root@kube-master yaml]# cat pod-qos-besteffort.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-besteffort
spec:
  containers:
  - name: nginx
    image: nginx:latest
[root@kube-master yaml]# cat pod-qos-besteffort.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-besteffort
spec:
  containers:
  - name: nginx
    image: nginx:latest
shell
#查看
[root@kube-master yaml]# kubectl describe pod pod-qos-besteffort
...
QoS Class:                   BestEffort
...
#查看
[root@kube-master yaml]# kubectl describe pod pod-qos-besteffort
...
QoS Class:                   BestEffort
...

创建多容器 Pod

1.创建⼀个 Pod,⼀个容器指定了内存请求 200 MiB。 另外⼀个容器没有指定任何请求和限制。此 Pod 满⾜ Burstable QoS 类的标准。但它不满⾜ Guaranteed QoS 类标准,因为它的⼀个容器设有内存请求

yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-mutil
spec:
  containers:
    - name: nginx
      image: nginx:latest
      resources:
        limits:
          memory: '100Mi'
    - name: redis
      image: redis:latest
apiVersion: v1
kind: Pod
metadata:
  name: pod-qos-mutil
spec:
  containers:
    - name: nginx
      image: nginx:latest
      resources:
        limits:
          memory: '100Mi'
    - name: redis
      image: redis:latest
shell
#查看
[root@kube-master yaml]# kubectl describe pod pod-qos-mutil
...
    Limits:
      memory:  100Mi
    Requests:
      memory:     100Mi
...
QoS Class:                   Burstable
...
#查看
[root@kube-master yaml]# kubectl describe pod pod-qos-mutil
...
    Limits:
      memory:  100Mi
    Requests:
      memory:     100Mi
...
QoS Class:                   Burstable
...

6.6 ResourceQuota 和 LimitRange 实践

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

需要实现的功能如下:

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

1. 创建命名空间

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

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: quota-example
apiVersion: v1
kind: Namespace
metadata:
  name: quota-example
  • 创建
kubectl create -f namespace.yaml

#查看
kubectl get namespaces | grep -v 'kube'
kubectl create -f namespace.yaml

#查看
kubectl get namespaces | grep -v 'kube'

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

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

yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    persistentvolumeclaims: "2"
    services.loadbalancers: "2"
    services.nodeports: "0"
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    persistentvolumeclaims: "2"
    services.loadbalancers: "2"
    services.nodeports: "0"
  • 创建
kubectl create -f object-counts.yaml --namespace=quota-example
kubectl create -f object-counts.yaml --namespace=quota-example
  • 查看
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
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 文件内容如下:

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
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
  • 创建
kubectl create -f compute-resources.yaml --namespace=quota-example
kubectl create -f compute-resources.yaml --namespace=quota-example
  • 查看
bash
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
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:

kubectl create deployment nginx --image=nginx --replicas=1 --namespace=quota-example
kubectl create deployment nginx --image=nginx --replicas=1 --namespace=quota-example
  • 查看

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

kubectl get pods --namespace=quota-example 

No resources found in quota-example namespace.
kubectl get pods --namespace=quota-example 

No resources found in quota-example namespace.
kubectl get deployments --namespace=quota-example

NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   0/1     0            0           2m38s
kubectl get deployments --namespace=quota-example

NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   0/1     0            0           2m38s

再查看 Deployment 的详细信息:

kubectl describe deployments nginx --namespace=quota-example

kubectl describe replicasets nginx-6799fc88d8  --namespace=quota-example
kubectl describe deployments nginx --namespace=quota-example

kubectl describe replicasets nginx-6799fc88d8  --namespace=quota-example

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

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

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

yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: limits
spec:
  limits:
    - default:
        cpu: 200m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 256Mi
      type: Container
apiVersion: v1
kind: LimitRange
metadata:
  name: limits
spec:
  limits:
    - default:
        cpu: 200m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 256Mi
      type: Container
  • 创建资源
kubectl create -f limits.yaml --namespace=quota-example
kubectl create -f limits.yaml --namespace=quota-example
  • 查看
kubectl describe limitranges --namespace=quota-example
kubectl describe limitranges --namespace=quota-example

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

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

kubectl run nginx --image=nginx --requests=cpu=100m,memory=256Mi --limits=cpu=200m,memory=512Mi --namespace=quota-example
kubectl run nginx --image=nginx --requests=cpu=100m,memory=256Mi --limits=cpu=200m,memory=512Mi --namespace=quota-example

7. Downward API

文档,https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/downward-api/

作用:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息

DownwardAPI 可以让容器获取 Pod 的相关元数据信息,比如 Pod 名称,Pod 的 IP,Pod 的资源限制等,获取后通过 env、voLume 的方式将相关的环境信息注入到容器中,从而让容器通过环境变量这些信息,来设定容器的运行特性。

  • 例如:Nginx 进程根据节点的 CPU 核心数量自动设定要启动的 worker 进程数
  • 例如:JVM 虚拟根据 Pod 的内存资源限制,来设定对应容器的堆内存大小
  • 例如:获取 Pod 名称,以 Pod 名称注册到某个服务,当 Pod 结束后,调用 prestop 清理对应名称的注册信息

7.1 可注⼊的元数据信息

使⽤ pod.spec.containers.env.valueFrom.fieldRef 可以注⼊的字段有:

shell
metadata.name:Pod对象的名称

metadata.namespace:Pod对象隶属的名称空间

metadata.uid:Pod对象的UID

metadata.abels['<KEY>']:获取LabeL指定KEY对应的值

metadata.annotations['<KEY>']:获取Annotations对应KEY的值

status.podIP:Pod对象的IP地址

status.hostIP:节点IP

status.nodeName:节点名称

spec.serviceAccountName:Pod对象使用的ServiceAccount资源名称
metadata.name:Pod对象的名称

metadata.namespace:Pod对象隶属的名称空间

metadata.uid:Pod对象的UID

metadata.abels['<KEY>']:获取LabeL指定KEY对应的值

metadata.annotations['<KEY>']:获取Annotations对应KEY的值

status.podIP:Pod对象的IP地址

status.hostIP:节点IP

status.nodeName:节点名称

spec.serviceAccountName:Pod对象使用的ServiceAccount资源名称

使⽤pod.spec.containers.env.valueFrom.resourceFieldRef 可以注⼊的字段有:

requests.cpu
requests.memory
limits.cpu
limits.memory
requests.cpu
requests.memory
limits.cpu
limits.memory

7.2 环境变量⽅式注⼊元数据

1.创建 Pod 容器,将 Pod 相关环境变量注⼊到容器中,⽐如(pod 名称、命名空间、标签、以及 cpu、内存的请求和限制)

[root@kube-master yaml]# kubectl apply pod-downward.yaml

yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-downward
  labels:
    app: pod-downward
spec:
  containers:
    - name: pod-downward
      image: nginx:latest
      command: ['/bin/sh', '-c', 'env']
      resources:
        limits:
          cpu: '200m'
          memory: '128Mi'
        requests:
          cpu: '200m'
          memory: '64Mi'
      env:
        - name: THIS_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: THIS_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: THIS_POD_APP_LABEL
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['app']
        - name: THIS_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        - name: THIS_MEMORY_REQUEST
          valueFrom:
            resourceFieldRef:
              resource: requests.memory
              divisor: 1Mi # 默认显示为字节,通过divisor调整显示单位为兆
apiVersion: v1
kind: Pod
metadata:
  name: pod-downward
  labels:
    app: pod-downward
spec:
  containers:
    - name: pod-downward
      image: nginx:latest
      command: ['/bin/sh', '-c', 'env']
      resources:
        limits:
          cpu: '200m'
          memory: '128Mi'
        requests:
          cpu: '200m'
          memory: '64Mi'
      env:
        - name: THIS_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: THIS_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: THIS_POD_APP_LABEL
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['app']
        - name: THIS_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        - name: THIS_MEMORY_REQUEST
          valueFrom:
            resourceFieldRef:
              resource: requests.memory
              divisor: 1Mi # 默认显示为字节,通过divisor调整显示单位为兆
  • 查看
shell
[root@kube-master yaml]# kubectl logs pod-downward |grep THIS
THIS_CPU_LIMIT=1
THIS_POD_APP_LABEL=pod-downward
THIS_POD_NAME=pod-downward
THIS_MEMORY_REQUEST=64
THIS_POD_NAMESPACE=default
[root@kube-master yaml]# kubectl logs pod-downward |grep THIS
THIS_CPU_LIMIT=1
THIS_POD_APP_LABEL=pod-downward
THIS_POD_NAME=pod-downward
THIS_MEMORY_REQUEST=64
THIS_POD_NAMESPACE=default

7.3 存储卷⽅式注⼊元数据

向容器注入元数据信息的另外一种方式是使用 downwardAPI 存储卷,它将配置的字段数据映射为文件并可通过容器中的挂载进行访问

#查看帮助
 kubectl explain pod.spec.volumes.downwardAPI
 
 kubectl explain pod.spec.volumes.downwardAPI.items
 
 kubectl explain pod.spec.volumes.downwardAPI.items.resourceFieldRef
#查看帮助
 kubectl explain pod.spec.volumes.downwardAPI
 
 kubectl explain pod.spec.volumes.downwardAPI.items
 
 kubectl explain pod.spec.volumes.downwardAPI.items.resourceFieldRef

在 downwardAPI 存储卷中使用 fieldRef 引用如下两个数据源:

  • metadata.labels: Pod 对象的所有标签信息,每行一个,格式为 label-key="escaped-label-value"
  • metadata.annotations: Pod 对象的所有注解信息,每行一个,格式为 annotation-key="escaped-annotation-value"。
yaml
apiVersion: v1
kind: Pod
metadata:
  labels: east-china
    rack: rack-101
    app: dapi-vol-pod
  name: dapi-vol-pod
  annotations:
    annotation1: "test-value-1"
spec:
  containers:
    - name: volume-test-container
      image: busybox
      command: ["/bin/sh", "-c", "sleep 864000"]
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "256m"
      volueMounts:
      - name: podinfo
          mountPath: /etc/podinfo
          readOnly: false
  volumes:
  - name: podinfo
    downwardAPI:
      defaultMode: 420
      items:
      - fieldRef:
          fieldPath: metadata.namespace
        path: pod_namespace
      - fieldRef:
          fieldPath: metadata.labels
        path: pod_labels
      - fieldRef:
          fieldPath: metadata.annotations
        path: pod_annotations
      - resourceFieldRef:
          containerName: volume-test-container
          resource: requests.memory
          divisor: "1Mi"
        path: "mem_request"
apiVersion: v1
kind: Pod
metadata:
  labels: east-china
    rack: rack-101
    app: dapi-vol-pod
  name: dapi-vol-pod
  annotations:
    annotation1: "test-value-1"
spec:
  containers:
    - name: volume-test-container
      image: busybox
      command: ["/bin/sh", "-c", "sleep 864000"]
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "256m"
      volueMounts:
      - name: podinfo
          mountPath: /etc/podinfo
          readOnly: false
  volumes:
  - name: podinfo
    downwardAPI:
      defaultMode: 420
      items:
      - fieldRef:
          fieldPath: metadata.namespace
        path: pod_namespace
      - fieldRef:
          fieldPath: metadata.labels
        path: pod_labels
      - fieldRef:
          fieldPath: metadata.annotations
        path: pod_annotations
      - resourceFieldRef:
          containerName: volume-test-container
          resource: requests.memory
          divisor: "1Mi"
        path: "mem_request"
#查看
kubectl exec dapi-vol-pod -- cat /etc/podinfo/pod_labels
#查看
kubectl exec dapi-vol-pod -- cat /etc/podinfo/pod_labels