简述
默认情况下,k8s不会对pod的资源使用进行限制,也就是说,pod可以无限使用主机的资源,例如CPU、内存等。为了保障k8s整体环境运行的稳定性,一般情况下,建议是对pod的资源使用进行限制,将其限制在一个范围内,防止起过度使用主机资源造成节点负载过大,导致其上面运行的其他应用受到影响。

前提
已有的k8s环境(这里安装的是k8s-v1.18.12版本)

k8s集群安装了metrics-server服务(这里借助rancher搭建k8s集群,默认安装了该服务)

何为CGroup
参考Wiki,cgroups其名称源自控制组群(英语:control groups)的简写,是Linux内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘输入输出等)。

也就是说,通过设置CGroup,可以达到对资源的控制,例如限制内存、CPU的使用。在k8s中,对pod的资源限制就是通过CGroup这个技术来实现的。

内存限制
pod示例
首先创建一个pod,并设置内存限制

resources:
  limits:
    memory: "200Mi"
  requests:
    memory: "100Mi"

内存单位:E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki

其中M = 1000 x 1000,Mi = 1024 x 1024

limit必须要≥request

完整yaml参考

apiVersion: v1
kind: Pod
metadata:
name: stress-memory
namespace: default
spec:
containers:

  • name: stress
    image: polinux/stress
    imagePullPolicy: IfNotPresent
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
    resources:

    limits:
      memory: "200Mi"
    requests:
      memory: "100Mi"

nodeName: node01 ## 这里指定调度到某个节点上,方便接下来的操作
这里设置了内存的limit为"200Mi",内存的request为"100Mi",并通过stress,指定分配150M的内存空间

接着我们运行该yaml

kubectl create -f stress-memory.yaml
等到pod运行起来后,我们看一下pod的详细信息

kubectl get pod stress-memory -oyaml
输出结果如下

...
...
name: stress-memory
namespace: default
...
...
uid: b84cf8e8-03b3-4365-b5b5-5d9f99969705
...
...

resources:
  limits:
    memory: 200Mi
  requests:
    memory: 100Mi

...
...
status:
...
...
qosClass: Burstable
...
...
从上述输出中可以获取如下两个信息:

pod的uid为b84cf8e8-03b3-4365-b5b5-5d9f99969705
pod的QoSClass为Burstable
这里Burstable对应的就是pod在CGroup中的路径,我们进入到CGroup的memory目录下,并进入到kubepods/Burstable目录中

cd /sys/fs/cgroup/memory/kubepods/burstable/

ls

...
podb84cf8e8-03b3-4365-b5b5-5d9f99969705
...
通过执行ls命令,能看到其中一个目录为podb84cf8e8-03b3-4365-b5b5-5d9f99969705,正好对应上面我们获得pod的uid,也就是说,对pod的资源限制,就是在这个CGroup目录下实现的

我们看一下这个目录下有什么文件

cd podb84cf8e8-03b3-4365-b5b5-5d9f99969705/
ls -1
8c9adca52f2bfd9e1862f6abaac5b435bdaf233925b7d640edb28e36da0b9b39
ca20137f7d5a42910e22425cca06443c16246c90ee9caa27814442e34a832b21
cgroup.clone_children
cgroup.event_control
cgroup.procs
memory.failcnt
memory.force_empty
memory.kmem.failcnt
memory.kmem.limit_in_bytes
memory.kmem.max_usage_in_bytes
memory.kmem.slabinfo
memory.kmem.tcp.failcnt
memory.kmem.tcp.limit_in_bytes
memory.kmem.tcp.max_usage_in_bytes
memory.kmem.tcp.usage_in_bytes
memory.kmem.usage_in_bytes
memory.limit_in_bytes
memory.max_usage_in_bytes
memory.memsw.failcnt
memory.memsw.limit_in_bytes
memory.memsw.max_usage_in_bytes
memory.memsw.usage_in_bytes
memory.move_charge_at_immigrate
memory.numa_stat
memory.oom_control
memory.pressure_level
memory.soft_limit_in_bytes
memory.stat
memory.swappiness
memory.usage_in_bytes
memory.use_hierarchy
notify_on_release
tasks
相关文件作用如下:

文件 作用
cgroup.event_control 用于event_fd()接口
cgroup.procs 展示process列表
memory.failcnt 内存使用达到限制的次数
memory.force_empty 强制触发当前CGroup中的内存回收
memory.limit_in_bytes 内存限制的最大值
memory.max_usage_in_bytes 记录该CGroup中历史最大内存使用量
memory.move_charge_at_immigrate 设置/显示当前CGroup的进程移动到另一个CGroup时,当前已占用的内存是否迁移到新的CGroup中,默认为0,即不移动
memory.numa_stat 显示numa相关的内存信息
memory.oom_control 设置/显示oom相关信息,其中oom_kill_disable为0,则超过内存会被kill;oom_kill_disable为1则停止进程,直至额外的内存被释放,当进程被暂停时,under_oom返回1
memory.pressure_level 设置内存压力的通知事件,配合cgroup.event_control一起使用
memory.soft_limit_in_bytes 设置/显示内存的软限制,默认不限制
memory.stat 显示当前CGroup中内存使用情况
memory.swappiness 设置/显示vmscan的swappiness参数(参考sysctl的vm.swappiness)
memory.usage_in_bytes 显示当前内存使用情况
memory.use_hierarchy 如果该值为0,将会统计到root cgroup里;如果值为1,则统计到它的父cgroup里面
notify_on_release 是否在cgroup中最后一个任务退出时通知运行release agent,默认情况下是0,表示不运行。(release_agent在CGroup最顶层的目录)
tasks 控制的进程组(这里看不到对应进程,需要进入到子group中查看)
不涉及内核内存(memory.kmem.)和swap分区内存(memory.memsw.),这里就不详细介绍

主要关注这几个文件

8c9adca52f2bfd9e1862f6abaac5b435bdaf233925b7d640edb28e36da0b9b39和ca20137f7d5a42910e22425cca06443c16246c90ee9caa27814442e34a832b21:分别对应的是pod运行的主容器ID和pause容器ID

memory.usage_in_bytes已使用的内存,例如我这里查看的结果是160817152,也就是153MB左右

cat memory.usage_in_bytes

160382976
使用kubect top命令查看使用情况

kubectl top pod

NAME CPU(cores) MEMORY(bytes)
stress-memory 13m 151Mi
memory.limit_in_bytes:内存限制的最大值,等于我们设置的内存limit的值

cat memory.limit_in_bytes

160587776
memory.max_usage_in_bytes:历史内存最大使用量,再查看一下该CGroup下内存历史最大使用量,正好200M

cat memory.max_usage_in_bytes

209715200
创建一个内存使用超出limit的pod
这时候我们将内存使用设置到250M,超出200M的限制

apiVersion: v1
kind: Pod
metadata:
name: stress-memory2
namespace: default
spec:
containers:

  • name: stress
    image: polinux/stress
    imagePullPolicy: IfNotPresent
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"] ## 修改为250M,也就是分配250M内存
    resources:

    limits:
      memory: "200Mi"
    requests:
      memory: "100Mi"

nodeName: node01
执行kubectl create命令,运行这个pod

kubectl create -f pod-memory-2.yaml
查看pod状态

kubectl get pod

NAME READY STATUS RESTARTS AGE
stress-memory 1/1 Running 1 10m6s
stress-memory2 0/1 OOMKilled 2 26s
此时会发现,pod在不断重启,并且stress-memory2这个pod的状态为OOMKilled,这个是怎么回事呢,我们可以进到pod对应的CGroup下查看内存使用情况,我们继续看一下目前pod的状态

不过还是有些办法可以应付一些工作,前提当然是拿到了一些信息的情况。

kubectl get pod stress-memory2 -oyaml
...
...
status:
...
...

lastState:
  terminated:
    containerID: docker://a7d686a3b56aa03b66fd4fed07217693d8e41d75529c02bae34769dca6f01f9e
    exitCode: 1
    finishedAt: "2021-01-18T14:13:21Z"
    reason: OOMKilled
    startedAt: "2021-01-18T14:13:21Z"

可以看到pod退出的原因是OOMKilled,什么是OOMKilled呢?简单来说,就是当进程申请的内存超出了已有的内存资源,那么为了保证主机的稳定运行,就会基于进程oom_score的值,有选择性的杀死某个进程。也就是说,在这个例子中,pod申请的内存为250Mi,超过了限制的200Mi,那么就会从该进程所在的CGroup中,杀死对应的进程,具体我们可以看一下该CGroup中内存情况:

通过kubectl get pod stress-memory2 -oyaml命令,获取pod uid,进入到对应的CGroup

cd /sys/fs/cgroup/memory/kubepods/burstable/pod92c2a4c2-3b5c-4a9a-8a00-5d59575e96e7/
首先看一下内存限制是多少

cat memory.limit_in_bytes

209715200
再查看一下内存使用量,只有1M左右,这是因为此时pod状态不是运行状态

cat memory.usage_in_bytes

1093632
再查看一下该CGroup下内存历史最大使用量,正好200M

cat memory.max_usage_in_bytes

209715200
此时我们再看看内存使用量达到限制值的次数

cat memory.failcnt

531
从以上信息可以得知,内存不断申请超出内存限制的值,导致进程被kill,最终导致pod退出

只设置request
设置request=100M,不设置limit,并设置pod使用内存150M

apiVersion: v1
kind: Pod
metadata:
name: stress-memory4
namespace: default
spec:
containers:

  • name: stress
    image: polinux/stress
    imagePullPolicy: IfNotPresent
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
    resources:

    requests:
      memory: "100Mi"

nodeName: node01
执行kubectl create命令,运行这个pod

kubectl create -f pod-memory-4.yaml
查看pod状态

kubectl get pod

NAME READY STATUS RESTARTS AGE
stress-memory3 1/1 Running 0 79s
查看pod内存使用

kubectl top pod

NAME CPU(cores) MEMORY(bytes)
stress-memory3 51m 150Mi
可以发现,pod内存使用时150Mi,说明只设置内存request,并不会对其限制其内存的使用

注意:如果只设置limit,则request=limit

内存资源限制的目的
如果没有指定内存限制,则容器可以无限制的使用主机内存资源,当使用完主机的所有可用资源后,就会导致该节点调用OOMkilled,此时没有设置内存限制的pod对应的进程的score会更大,所以被kill的可能性更大。

为集群中的pod设置内存请求和限制,可以有效的利用集群上的内存资源,防止应用突发高峰期的时候内存猛涨影响其他应用的稳定运行

CPU限制
pod示例
首先创建一个pod,并设置CPU限制

resources:
  limits:
    cpu: "1"
  requests:
    cpu: "0.5"

CPU单位:小数值是可以使用。一个请求 0.5 CPU 的容器保证会获得请求 1 个 CPU 的容器的 CPU 的一半。 可以使用后缀 m 表示毫。例如 100m CPU、100 milliCPU 和 0.1 CPU 都相同。 精度不能超过 1m。

完整yaml参考

apiVersion: v1
kind: Pod
metadata:
name: stress-cpu
spec:
containers:

  • name: stress-cpu
    image: vish/stress
    resources:

    limits:
      cpu: "0.9"
    requests:
      cpu: "0.5"

    args:

    • -cpus
    • "2"

nodeName: node01 ## 这里指定调度到某个节点上,方便接下来的操作
这里设置了CPU的limit为"1",CPU的request为"0.5",并通过stress指定分配2个进程去跑满2个CPU

接着我们运行该yaml

kubectl create -f stress-cpu.yaml
等到pod运行起来后,我们看一下pod的详细信息

kubectl get pod stress-cpu -oyaml
输出结果如下

...
...
name: stress-cpu
namespace: default
...
...
uid: 10929272-932f-4b4d-85c8-046a9f0e39d8
...
...

resources:
  limits:
    cpu: 900m
  requests:
    cpu: 500m

...
...
status:
...
...
qosClass: Burstable
...
...
结合之前的内存相关的实验,从输出的结果中可以得知pod uid为10929272-932f-4b4d-85c8-046a9f0e39d8,对应的CGroup路径为kubepods/Burstable

在CGroup中可以看到cpu、cpuset、cpuacct三种跟CPU相关的CGroup,其中cpu用于对cpu使用率的划分;cpuset用于设置cpu的亲和性等,主要用于numa架构的os;cpuacct记录了cpu的部分信息。通过定义可以得知,k8s CPU限制在cpu这个目录中,我们进入到对应的pod的CGroup空间下,查看CGroup相关文件是如何工作的

cd /sys/fs/cgroup/cpu/kubepods/burstable/pod10929272-932f-4b4d-85c8-046a9f0e39d8/

ls -1
cgroup.clone_children
cgroup.procs
cpuacct.stat
cpuacct.usage
cpuacct.usage_percpu
cpu.cfs_period_us
cpu.cfs_quota_us
cpu.rt_period_us
cpu.rt_runtime_us
cpu.shares
cpu.stat
d5b407752d32b7cd8937eb6a221b6f013522d00bb237134017ae5d8324ce9e30
eba8c20349137130cdc0af0e9db2a086f6ea5d6f37ad118394b838adfc1325bd
notify_on_release
tasks
相关文件作用如下:

文件 作用
cgroup.clone_children 子cgroup是否会继承父cgroup的配置,默认是0
cgroup.procs 树中当前节点的cgroup中的进程组ID,现在我们在根节点,这个文件中是会有现在系统中所有进程组ID
cpu.cfs_period_us 统计CPU使用时间的周期,单位是微秒(us)。
例如如果设置该CGroup中的进程访问CPU的限制为每1秒访问单个CPU0.2秒,则需要设置cpu.cfs_period_us=200000,cpu.cfs_quota_us=1000000
cpu.cfs_quota_us 周期内允许占用的CPU时间,即CGroup中的进程能使用cpu的最大时间,该值是硬限(-1为不限制)
一旦cgroup中的任务用完了配额指定的所有时间,它们就会在该时间段指定的剩余时间中受到限制,并且直到下一个时间段才允许运行。cpu.cfs_quota_us参数的上限为1秒,下限为1000微秒
cpu.rt_period_us 仅适用于实时调度任务,CPU使用时间的周期,单位是微秒(us),默认是1s(1000000us)
cpu.rt_runtime_us 仅适用于实时调度任务,此参数指定cgroup中的任务可以访问CPU资源的最长连续时间
cpu.shares cpu.shares是软限制,理解为CPU的相对权重。
例如,A、B、C三个进程的权重为100、200、300,那么在CPU满载的情况下,A进程最多使用1/6 CPU时间片;而如果CPU不是满载的情况,则各个进程允许使用超出相对权重的大小的CPU时间
cpu.stat CPU时间统计信息,其中:
nr_periods—已经过去的周期间隔数(在cpu.cfs_period_us中指定)
nr_throttled — cgroup中的任务被限制的次数(即,由于它们已经用尽了配额所指定的所有可用时间,因此不允许运行)
throttled_time — cgroup中的任务已被限制的总持续时间(以纳秒为单位)
不涉及到cpuacct,这里就不详细介绍

在CPU的CGroup中,可以使用两个调度程序来调度对CPU资源的访问:

CFS:完全公平调度程序,比例共享调度程序,根据任务或分配给cgroup的优先级/权重,在任务组(cgroup)之间按比例划分CPU时间。
RT:实时调度程序,一种任务调度程序,它提供一种方法来指定实时任务可以使用的CPU时间。(不做讨论)
这里主要讨论k8s是如何设置CFS调度算法去限制进程对CPU使用,主要关注这几个文件

cpu.cfs_period_us和cpu.cfs_quota_us,由这两个文件组成CPU硬限制,在这个例子中,我们设置CPUlimit的值为900m,所以cfs_quota_us/cfs_period_us等于90000/100000,也就是0.9个CPU

cat cpu.cfs_period_us

100000

cat cpu.cfs_quota_us

90000
cpu.shares,CPU软限制,在这个例子中我们设置了CPU request为500m,可以看出,CPU request对应了cpu.shares(此时软限制不会起到作用)

cat cpu.shares

512
此时查看podCPU使用情况,可以看到确实已经被限制住了

kubectl top pod

NAME CPU(cores) MEMORY(bytes)
stress-cpu 902m 0Mi
request之CPU软限制
在前面讲内存限制的时候说到,如果只设置request,则limit没有限制。

如果CPU只设置request,此时limit也是没有限制。但是不同于内存,CPU request的值会设置到cpu.shares中,也就是说,只设置了request的pod,会有一个CPU软限制。

此时正常情况下,当节点CPU资源充足的时候,设置了request的pod,还是可以正常的请求超出request值的CPU资源,可是当节点可分配的CPU资源不足时,那么CPU软限制就会起到作用,限制pod对CPU的访问。

下面我们测试一下CPU软限制是如何生效的

查看节点可分配的CPU大小

kubectl describe node node01
...
...
Allocatable:
cpu: 4
ephemeral-storage: 57971659066
hugepages-2Mi: 0
memory: 8071996Ki
pods: 110
...
...
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 380m (9%) 10m (0%)
memory 100Mi (1%) 190Mi (2%)
ephemeral-storage 0 (0%) 0 (0%)
目前该节点可用CPU数量为4000m,已使用了380m,也就是剩余可用CPU为3620m

我们先创建一个CPU,设置request为0.5的pod,并通过stress指定分配2个进程去跑满2个CPU

kind: Pod
metadata:
name: stress-cpu-1
spec:
containers:

  • name: stress-cpu
    image: vish/stress
    resources:

    requests:
      cpu: "0.5"

    args:

    • -cpus
    • "2"

执行kubectl create命令,运行这个pod

kubectl create -f stress-cpu-1.yaml
查看pod状态

kubectl get pod

NAME READY STATUS RESTARTS AGE
stress-cpu 1/1 Running 0 5s
查看podCPU使用情况

kubectl top pod

NAME CPU(cores) MEMORY(bytes)
stress-cpu 2001m 0Mi
也可以使用top命令实时查看进程CPU使用情况

top

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
30285 root 20 0 5824 3692 3120 R 200.0 0.0 56:18.95 stress
此时我们可以看到,我们进程的CPU使用率达到了2001m,超过了request设置的值

这时候我们再启动一个pod,设置request为2.5的pod,并通过stress指定分配2个进程去跑满3个CPU

apiVersion: v1
kind: Pod
metadata:
name: stress-cpu-2
spec:
containers:

  • name: stress-cpu
    image: vish/stress
    resources:

    requests:
      cpu: "2.5"

    args:

    • -cpus
    • "3"

执行kubectl create命令,运行这个pod

kubectl create -f stress-cpu-2.yaml
查看pod状态

kubectl get pod

NAME READY STATUS RESTARTS AGE
stress-cpu 1/1 Running 0 15m
stress-cpu-2 1/1 Running 0 8s
查看podCPU使用情况,此时由于pod占用了大量的CPU资源,执行kubectl会卡住无法执行,可以通过top命令查看CPU使用情况

top

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20732 root 20 0 5568 3688 3120 R 300.0 0.0 6:04.80 stress
30285 root 20 0 5824 3692 3120 R 96.2 0.0 63:57.08 stress
我们再来回顾一下,首先这台主机可用CPU资源3620m,总的CPU资源为4个CPU(4000m),其中380m的CPU资源已分配给其他pod使用,但是并没有满负载,所以总的资源我们可以按照4个CPU来算。

这里可以看到PID为30285的进程是之前我们创建的pod stress-cpu,当时设置的request是0.5(500m),并指定分配2个进程去跑满2个CPU(2000m),也就是软限制在资源充足的情况下,使用率为200%(2000m),超过了CPUrequest软限制。

后面我们又创建了一个request为2.5(2500m),并指定分配3个进程去跑满3个CPU(3000m)的pod stress-cpu-2,此时CPU总的CPU使用需求为2+3=5CPU(5000m),但是CPU的总资源只有4CPU(4000m),此时CPU软限制就会开始发挥作用。

我们查看一下对应的cpu.shares,stress-cpu pod 的cpu.shares的值为512,stress-cpu-2 pod 的cpu.shares的值为2560。

cpu.shares是软限制,理解为CPU的相对权重。根据之前表格中的算法,那么当CPU满负载的情况下(此时假设只有这两个pod正在满负载的运行):

stress-cpu可以使用4 x 512/(512+2560)≈0.666(666m CPU)
stress-cpu-2可以使用4 x 2560/(512+2560)≈3.333(3333m CPU)
由这个公式可以得知,在软限制的前提下,stress-cpu-2可以跑满3333m CPU,但是我们只分配了3个进程去跑满CPU,也就是说,实际上stress-cpu-2可以跑到3000mCPU ,正好符合我们使用top看到的数据,占用了300%(3000m),还剩余333m左右的CPU资源未使用,而这部分的资源可以被其他进程使用。那么可以思考一下,stress-cpu可以使用多少CPU资源?

其实从上面top命令的结果就可以知道,stress-cpu使用了近1000mCPU的资源,这是因为,当CPU满负载时,会相应的分配666mCPU的资源,此时stress-cpu-2并没有完全使用完,还剩余333mCPU未被使用,那么由于软限制的作用下,实际上是可以使用这部分未被使用的资源,也就是说,stress-cpu可以使用666m + 333m = 999m(CPU),也就是符合使用top命令看到的96%CPU使用率。

CPU资源限制的目的
如果没有指定CPU限制,则容器可以无限制的使用主机CPU资源。为集群中的pod设置CPU请求和限制,可以有效的利用集群上的CPU资源,防止应用突发高峰期的时候CPU猛涨影响其他应用的稳定运行
QoS
前面我们讲了如何通过设置资源的request和limit来限制pod对主机资源的使用,在前面几个例子中,我们看到,当我们设置了资源配额时,查看pod yaml可以看到qosClass的值为 Burstable,这是因为在k8s中,会为pod设置不同的QoS类型,以保证pod的资源可用,其中QoS有三个类型:

Guaranteed:Pod中的所有容器(包括init容器)都必须设置内存和CPU的请求(limit)和限制(request),且请求和限制的值要相等。(如果只设置limit,则默认request=limit);
Burstable:Pod中至少有一个容器设置了内存和CPU请求,且不符合Guaranteed QoS类型的标准;
BestEffort:Pod中所有的容器都没有设置任何内存和CPU的限制和请求。
即会根据对应的请求和限制来设置QoS等级,接下来我们分别创建对应的QoS等级来感受一下

Guaranteed
定义:Pod中的所有容器(包括init容器)都必须设置内存和CPU的请求(limit)和限制(request),且请求和限制的值要相等。其中如果只设置limit,则默认request=limit

举个例子:创建一个包含内存和CPU请求和限制的pod,其中内存的请求和限制都为300Mi,CPU的请求和限制都为500m

apiVersion: v1
kind: Pod
metadata:
name: pod-guaranteed
spec:
containers:

  • name: pod-guaranteed
    image: zerchin/network
    resources:

    limits:
      memory: "300Mi"
      cpu: "500m"
    requests:
      memory: "300Mi"
      cpu: "500m"

创建pod

kubectl create -f pod-guaranteed.yaml
查看pod 详细信息

kubectl get pod pod-guaranteed -oyaml
输出结果如下,可以看到pod的qosClass为Guaranteed

...
...
name: pod-guaranteed
uid: 494e608c-d63e-41a9-925c-3ac7acf7b465
...
...

  limits:
    cpu: 500m
    memory: 300Mi
  requests:
    cpu: 500m
    memory: 300Mi

...
...
status:
qosClass: Guaranteed
根据前面的经验,这里Guaranteed对应的就是pod在CGroup中的路径,对应的CGroup路径如下:

CPU:/sys/fs/cgroup/memory/kubepods/pod494e608c-d63e-41a9-925c-3ac7acf7b465
内存:/sys/fs/cgroup/cpu/kubepods/pod494e608c-d63e-41a9-925c-3ac7acf7b465
Burstable
定义:Pod中至少有一个容器设置了内存和CPU请求,且不符合Guaranteed QoS类型的标准;

回顾一下我们前面我们在了解内存和CPU限制的时候,创建的pod都是Burstable Qos类型,包括:

在这上面可以综合的查到 ip 或者域名的一些活动信息,或者 whois 信息,也可以单去查 whois,只是需要去分辨下历史 whois 的信息,确认当前域名的所有者到底是不是同一个人。当然,现在部分域名商有隐藏 whois 的情况,这种暂时没思路。

单一设置内存或CPU的request
同时设置了内存或CPU的request和limit,且request≠limit
同时设置了内存或CPU的request和limit,且request=limit
上述这些都是pod中只含有单个容器,还有一种情况就是单个pod包含多个容器,如果一个容器指定了资源请求,另一个容器没有指定任何请求和限制,则也是属于Burstable Qos类型

举个例子:创建一个多容器的pod,其中一个容器设置了内存和CPU的请求和限制且值相等,另一个容器不限制资源

apiVersion: v1
kind: Pod
metadata:
name: pod-burstable
spec:
containers:

  • name: pod-burstable-1
    image: zerchin/network
    resources:

    limits:
      memory: "300Mi"
      cpu: "500m"
    requests:
      memory: "300Mi"
      cpu: "500m"
  • name: pod-burstable-2
    image: busybox:1.28
    args: ["sh", "-c", "sleep 3600"]

创建pod

kubectl create -f pod-burstable.yaml
查看pod 详细信息

kubectl get pod pod-burstable -oyaml
输出结果如下,可以看到pod的qosClass为Guaranteed

...
...
name: pod-burstable
uid: 7d858afc-f88a-454e-85dc-81670a0ddb8b
...
...
status:
qosClass: Burstable
...
...
BestEffort
定义:pod中所有的容器都没有设置任何内存和CPU的限制和请求。

这个很好理解,我们创建个pod试试

apiVersion: v1
kind: Pod
metadata:
name: pod-besteffort
spec:
containers:

  • name: pod-besteffort
    image: zerchin/network

创建pod

kubectl create -f pod-besteffort.yaml
查看pod 详细信息

kubectl get pod pod-besteffort -oyaml
输出结果如下,可以看到pod的qosClass为BestEffort

...
...
name: pod-besteffort
uid: 1316dbf7-01ed-415b-b901-2be9d650163c
...
...
status:
qosClass: BestEffort
那么,在CGroup中对应的路径为kubepods/besteffort/pod1316dbf7-01ed-415b-b901-2be9d650163c

QoS优先级
当集群资源被耗尽时,容器会被杀死,此时会根据QoS优先级对pod进行处理,即优先级高的会尽量被保护,而优先级低的会优先被杀死

三种Qos类型优先级(由高到低):Guaranteed > Burstable > BestEffort

其中CPU属于可压缩资源,而内存属于不可压缩资源,这里我们主要讨论一下当内存耗尽时,是如何根据QoS优先级处理pod

BestEffort:BestEffort类型的pod没有设置资源限制,此类pod被认为是最低优先级,当系统内存不足时,这些pod会首先被杀死
Burstable:Burstable类型的pod设置有部分资源的请求和限制,此类pod的优先级高于BestEffort类型的pod,当系统内存不足且系统中不存在BestEffort类型的pod时才会被杀死
Guaranteed:Guaranteed类型的pod同时设置了CPU和内存的资源限制和请求,此类pod的优先级最高,只有在系统内存不足且系统系统中不存在BestEffort和Burstable的pod时才会被杀死
OOM score
在前面讲内存限制时提到过,就是当进程申请的内存超出了已有的内存资源,那么为了保证主机的稳定运行,就会基于进程oom_score的值,有选择性的杀死某个进程,这个过程就是OOMKilled。

这里我们先了解一下什么是oom_score:

当系统内存资源被耗尽时,就需要释放部分内存保证主机的运行。而内存是被进程所占用的,所以释放内存实际上就是要杀死进程。那么系统是如何选择进程进行杀死的呢?答案就是基于oom_score的值选择可以杀死的进程。oom_score的值在0-1000范围,其中oom_score的值越高,则被杀死的可能性越大。

oom_score的值还会受到oom_score_adj影响,最后的得分会加上oom_score_adj的值,也就是说,可以通过设置oom_score_adj的大小从而影响最终oom_score的大小。

其中正常情况下,oom_score是该进程消耗的内存百分比的10倍,通过oom_score_adj进行调整,例如如果某个进程使用了100%的内存,则得分为1000;如果使用0%的内存,则得分为0(其他例如root用户启动的进程会减去30这里不讨论)

那么我们来看看不同的QoS类型的score值是如何设置的

BestEffort:由于它的优先级最低,为了保证BestEffort类型的pod最先被杀死,所以设置oom_score_adj为1000,那么BestEffort类型pod的oom_score值为1000

Guaranteed:由于它的优先级最高,为了保证Guaranteed类型的pod的不被杀死,所以设置oom_score_adj为-998,那么Guaranteed类型pod的oom_score值为0或者1

Burstable:这里要分几种情况讨论

如果总内存请求 > 可用内存的99.8%,则设置oom_score_adj的值为2,否则将oom_score_adj设置为1000 - 10 x 内存请求的百分比,这样可以确保Burstable类型的pod的oom_score > 1。
如果内存请求为0,则设置oom_score_adj的值为999。所以,如果Burstable类型的pod和Guaranteed类型的发生冲突时,保证Burstable类型的pod被杀死。
如果Burstable类型的pod使用的内存少于请求的内存,则其oom_score<1000,因此,如果BestEffort类型的pod与使用少于请求内存的Burstable类型的pod发生冲突时,则BestEffort类型的pod将被杀死
如果Burstable类型的pod使用的内存大于内存请求时,oom_score=1000,否则oom_score<1000
如果一个使用的内存多于请求内存的Burstable类型的pod,与另一个使用的内存少于请求内存的Burstable类型的pod发生冲突时,则前者将被杀死
如果Burstable类型的pod与多个进程发生冲突,则OOM分数的计算公式是一种启发式的,它不能保证“请求和限制”的保证
infra 容器(pause)或init 容器,oom_score_adj为-998

已知支付宝账号(手机号、邮箱),大额转账可验证姓氏,如果对的话,会提示成功,所以可以尝试多次。

默认kubelet和docker的oom_score_adj为-999

基于上述oom_score,就能保证当系统资源耗尽时,首先被杀死的是BestEffort类型的pod,其次是Burstable类型的pod,最后才是Guaranteed类型的pod