首页 > 其他分享 >19-资源限制:如何保障你的 Kubernete 集群资源不会被打爆

19-资源限制:如何保障你的 Kubernete 集群资源不会被打爆

时间:2024-01-11 13:45:18浏览次数:29  
标签:容器 Requests Limits 19 cpu CPU Pod Kubernete 打爆

前面的课时中,我们曾提到通过 HPA 控制业务的资源水位,通过 ClusterAutoscaler 自动扩充集群的资源。但如果集群资源本身就是受限的情况下,或者一时无法短时间内扩容,那么我们该如何控制集群的整体资源水位,保障集群资源不会被“打爆”?

今天我们就来看看 Kubernetes 中都有哪些能力可以帮助我们保障集群资源?

设置 Requests 和 Limits

Kubernetes 中对容器的资源限制实际上是通过 CGroup 来实现的。CGroup 是 Linux 内核的一个功能,用来限制、控制与分离一个进程组的资源(如 CPU、内存、磁盘输入输出等)。每一种资源比如 CPU、内存等,都有对应的 CGroup 。如果我们没有给 Pod 设置任何的 CPU 和 内存限制,这就意味着 Pod 可以消耗宿主机节点上足够多的 CPU 和 内存。

所以一般来说,我们都会对 Pod 进行资源限制, Kubernetes 通过给 Pod 设置资源请求(Requests)和资源限制(Limits)来实现这个资源限制。

  • Requests 表示容器可以得到的资源,或者可以理解为 Pod 运行的最低资源要求。

  • Limits 表示着容器最多可以得到的资源。Pod 运行过程中,比如 CPU 使用量会增加,那么最多能使用多少内存,这就是资源限制。

这里有一点需要注意的就是,Limits 永远不要低于 Requests,如果设置不对,Kubernetes 也会拒绝 Pod 的创建。

通过设置 Requests 和 Limits,我们既保证了 Pod 可以运行,又限制 Pod 能使用多少资源。这样能避免某些恶意的容器“吞噬”宿主机的资源,也可以避免某些容器异常导致宿主机 OOM,从而引起该节点上的所有 Pod 异常,甚至导致整个集群“雪崩”。

我们来看看个 Requests 和 Limits 的例子:

apiVersion: v1
kind: Pod
metadata:
  name: pod-resource-demo
  namespace: demo
spec:
  containers:
  - name: demo-container-1
    image: nginx:1.19
    resources:
    requests:
      memory: "64Mi"
      cpu: "250m"
    limits:
      memory: "128Mi"
      cpu: "500m"
  - name: demo-container-2
    image: nginx:1.19
    resources:
    requests:
      memory: "64Mi"
      cpu: "250m"
    limits:
      memory: "128Mi"
      cpu: "500m"

如上所示,Pod 中的每个容器都可以设置自己的 Requests 和 Limits,每个容器使用的资源都不能超过各自的限制。当 Pod 在调度时,会把这些容器的 Requests 和 Limits 进行相加,当作整个 Pod 的资源申请量。因此在上面的示例中,Pod 的总 Requests 为 500 mCPU,128 MiB 内存,总 Limits 为 1 CPU和 256 MiB。关于单位的含义,官方文档有更详细的说明。

一旦 Pod 成功被调度后,Kubernetes 会将其调度到可以为其提供该资源的节点上。

而根据设置的 Requests 和 Limit,Kubernetes 又将其分为不同的 QoS (Quality of Service)级别。Kubernetes 中 Pod 是最小的单元,所以 QoS 是对整个 Pod 而言而非某个容器。

Kubernetes 支持了三种 QoS 级别,分别为BestEffort、Burstable 和 Guranteed,当资源紧张时 Kubernetes 会根据它们的分级决定调度和驱逐策略(这个我会在后面的课程中单独说明,在此略过),这三个分级分别代表:

  • BestEffort表示 Pod 中没有一个容器设置了 Requests 或 Limits,它的优先级最低;

  • Burstable表示 Pod 中每个容器至少定义了 CPU 或 Memory 的 Requests,或者 Requests 和 Limits 不相等,它属于中等优先级;

  • Guranteed则表示 Pod 中每个容器 Requests 和 Limits 都相等,这类 Pod 的运行优先级最高。简单来说就是cpu.limits = cpu.requests,memory.limits = memory.requests。

你可以通过 QoS 的代码来研究下 Kubernetes 是如何确定 Pod 对应的 QoS 的。这里,我们通过一个 Burstable Pod 的例子,来直观感受下 Kubernetes 的资源限制能力。

一个 Burstable Pod 的例子

这是一个 Burstable Pod 的 YAML 文件,该 Pod 内只有一个容器,且为容器的内存设置了 Requests 和 Limits,分别为 50 Mi 和 100 Mi。

apiVersion: v1
kind: Pod
metadata:
  name: memory-burstable-demo
  namespace: demo
spec:
  containers:
  - name: memory-demo
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

我们通过kubectl create创建好了以后,来查看该 Pod 的状态:

$ kubectl -n demo get po
NAME                                  READY     STATUS        RESTARTS   AGE
memory-burstable-demo      0/1       OOMKilled     1          11s

可以看到该 Pod 被 OOM 杀掉了,因为限制使用100M,而实际使用 250M。那么如果是 CPU 使用超过了 Limits 呢?

这是一个为容器的 CPU 资源设置了 Requests 和 Limits 的 Pod YAML 文件:

apiVersion: v1
kind: Pod
metadata:
  name: cpu-burstable-demo
  namespace: demo
spec:
  containers:
  - name: cpu-demo
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

这里我们同样先用kubectl create创建,然后用kubectl top来查看容器 cpu-demo 的资源使用情况:

kubectl -n demo top cpu-burstable-demo cpu-demo
NAME       CPU(cores)   MEMORY(bytes)
cpu-demo   1000m        0Mi

可以看到 Pod 的内存使用虽然超过了 Limits,实际使用的 CPU 被限制只有 1000 m,但是不会被 OOM 掉,这是因为 CPU 不同于内存,CPU 是可压缩资源(Compressible Resource),而内存是不可压缩资源(Incompressible Resource)。
如果只是为了限制资源,用 Requests 和 Limits 就足够了,那么为何 Kubernetes 还要单独引入 QoS 的概念呢?要回答这个问题,我们就要来看看 QoS 的主要作用。

QoS 的主要作用

集群运行一段时间以后,Node 上会有很多 Running 的 Pod。当 Node 上的资源紧张时,可能由于某些BestEffort的 Pod 使用的 CPU 和 Memory 越来越多,或者宿主机某些进程(例如 Kubelet、Docker)占用了 CPU 和 Memory,这个时候Kubernetes 就会根据 QoS 的优先级来选择 Kill 掉一部分 Pod,哪些会先被 Kill 掉呢?

当然是优先级最低的,即BestEffort类型的 Pod,占用的资源越多越优先被 Kill 掉。如果所有BestEffort的 Pod 都被杀死了但是资源依旧紧张,那么接下来会选择 Kill 中等优先级的,即Burstable类型的,之后以此类推。

这里 QoS 的一个作用就是跟oom_score进行挂钩。Kubernetes 会根据 QoS 设置 OOM 的评分调整参数oom_score_adj,有兴趣可以阅读详细的计算代码。当发生 OOM 时,oom_score_adj数值越高就越优先被 Kill。这里我给你展示了三个 QoS 对应的oom_score_adj计算公式。

image.png

除此之外,QoS 还与 Pod 驱逐有关系。当节点的内存、CPU 资源不足时,Kubelet 会开始驱逐节点上的 Pod,它会依据 QoS 的优先级确定驱逐的顺序,跟上面 OOM kill 的次序一样。我们会在后续的课程中单独讲这部分。

在实际使用的时候,我们可能会担心某些 Pod 申请了过大的资源,恶意占用,那么我们又该如何避免呢?

通过 LimitRange 设置资源防线

Kubernetes 提供了 LimitRange 可以帮助你限定 CPU 和 Memory 的申请范围。

这是一个完整的 LimitRange 定义,你可以根据需要按需选择进行配置。

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
  namespace: example
spec:
  limits:
  - default:  # 默认 limit
      memory: 512Mi
      cpu: 2
    defaultRequest:  # 默认 request
      memory: 256Mi
      cpu: 0.5
    max:  # 最大 limit
      memory: 800Mi
      cpu: 3
    min:  # 最小 request
      memory: 100Mi
      cpu: 0.3
    maxLimitRequestRatio:  # limit/request 的最大比率
      memory: 2
      cpu: 2
    type: Container # 支持 Container / Pod / PersistentVolumeClaim 三种类型
  • default 字段可以设置 Pod 中容器的默认 Limits;

  • defaulRequest 字段可以设置 Pod 中容器的默认 Requests;

  • max 字段可以设置 Pod 中容器可以设置的最大 Limits,default 字段不能高于此值。同样,在容器上设置的 Limits 也不能高于此值。在使用的时候需要注意的是,如果设置了该字段而又没有设置 default,那么所有未显式设置这些值的容器都将使用此处的最大值作为 Limits。

  • min 字段可以设置 Pod 中容器可以设置的最小 Requests。defaulRequest 字段不能低于此值。同样,在容器上设置的 Requests 也不能低于此值。同样需要注意的是,如果设置了该字段而又没有设置 defaulRequest,那么所有未显式设置这些值的容器都将使用此处的最小值作为 Requests。

LimitRange 会设置默认的申请、限制的值,它会自动在 Pod 创建时就注入 Container 中。

你可以参照如下几个官方文档中的详细例子学习体会一下:
如何配置每个命名空间最小和最大的 CPU 约束
如何配置每个命名空间最小和最大的内存约束
如何配置每个命名空间默认的 CPU 申请值和限制值
如何配置每个命名空间默认的内存申请值和限制值
如何配置每个命名空间最小和最大存储使用量

除了对单个 Pod、Container、PVC 做资源限制外,我们还可以对某个 namespace 下的资源总量进行限制。

ResourceQuota 设置资源总量限制

我们可以使用 ResourceQuota 对 namespace 内的资源总量进行限制,比如这个例子:

apiVersion: v1
kind: ResourceQuota 
metadata: 
  name: compute-resources
  namespace: demo                  #在demo空间下
spec: 
  hard:
    requests.cpu: "10"             #cpu预配置10
    requests.memory: 100Gi         #内存预配置100Gi
    limits.cpu: "40"               #cpu最大不超过40
    limits.memory: 200Gi           #内存最大不超过200Gi

你可以看到它有四个部分,每个部分都是可选的,你可以根据自己的需要进行组合。

  • requests.cpu 是该命名空间中所有容器的 CPU Requests 总和。在上面的例子中,你可以拥有10 个具有 1 个 CPU 请求的容器,或者 5 个具有 2 个 CPU 请求的容器。只要命名空间 demo 中所有容器的 CPU Requests 总和小于 10 即可。

  • requests.memory 是该命名空间中所有容器的 Memory Requests 总和。同 CPU 一样,只要该命名空间中内存的总请求小于100Gi 即可。

  • limits.cpu 是命名空间中所有容器的 CPU Limits 的总和。和 requests.cpu 一样,只不过这里是 Limits。

  • limits.memory 是命名空间中所有容器的内存 Limits 的总和。和 requests.memory 一样,这里也是指 Limits。

除了 CPU 和内存这类资源以外,ResourceQuota 还支持扩展资源,详见官方文档的说明

ResourceQuota 的功能非常强大,还可以对对象的数量进行限制。比如这个例子:

apiVersion: v1 
kind: ResourceQuota 
metadata: 
  name: object-counts 
  namespace: demo                  #在demo命名空间下
spec: 
  hard: 
    configmaps: "10"               #最多10个configmap
    pods: "20"                     #最多20个pod
    persistentvolumeclaims: "4"    #最多10个pvc
    replicationcontrollers: "20"   #最多20个rc
    secrets: "10"                  #最多10个secrets
    services: "10"                 #最多10个service
    services.loadbalancers: "2"    #最多10个lb类型的service
    requests.nvidia.com/gpu: 4        #最多10个GPU

我们就可以限制该命名空间下最多可以创建 20 个 Pod,10 个 Configmap 等。

写在最后

对于一些重要的线上应用,我们要合理地设置 Requests 和 Limits,且最好使两者的设置相等,当节点资源不足时,Kubernetes 会优先保证这些 Pod 的正常运行。

此外,你可以用 ResourceQuota 限制命名空间中所有容器的内存请求总量、内存限制总量、CPU 请求总量、CPU 限制总量等。而如果你想对单个容器而不是所有容器进行限制,就可以使用 LimitRange。

到这里这节课就结束了,如果你对本节课有什么想法或者疑问,欢迎你在留言区留言,我们一起讨论。


标签:容器,Requests,Limits,19,cpu,CPU,Pod,Kubernete,打爆
From: https://www.cnblogs.com/huangjiale/p/17958413

相关文章

  • 20-资源优化:Kubernete 中有 GC(垃圾回收)吗?
    GarbageCollector即垃圾回收,通常简称GC,和你之前在其他编程语言中了解到的GC基本上是一样的,用来清理一些不用的资源。Kubernetes中有各种各样的资源,当然需要GC啦,今天我们就一起来了解下Kubernetes中的GC。你可能最先想到的就是容器的清理,即Kubelet侧的GC,清理许多处于......
  • 02-高屋建瓴:Kubernete 的架构为什么是这样的?
    通过上一课时的学习,我们已经对Kubernetes的前世今生有所了解。接下来,我们开始具体学习如何将Kubernetes应用到自己的项目中,首先就需要了解Kubernetes的架构。所以,在本节课程中,我们会一起学习Kubernetes的架构设计,以及背后的设计哲学。Google使用Linux容器有超过15年......
  • 03-集群搭建:手把手教你玩转 Kubernete 集群搭建
    通过上一节课的学习,我们已经对Kubernetes的架构有了清楚的认识。但是到现在还没有和Kubernetes集群真正打过交道,所以你可能有一种“不识庐山真面目”的云里雾里的感觉。那么本节课,我们就来学习如何搭建Kubernetes集群,开启探索Kubernetes的第一站。在线Kubernetes集群......
  • 04-核心定义:Kubernete 是如何搞定“不可变基础设施”的?
    在上一节课,我们已经了解了Kubernetes集群的搭建方式。从现在开始,我们就要跟Kubernetes集群打交道了。本节课我们会学习Kubernetes中最重要、也最核心的对象——Pod。在了解Pod之前,我们先来看一下CNCF官方是怎么定义云原生的。云原生技术有利于各组织在公有云、私有云......
  • 06-无状态应用:剖析 Kubernete 业务副本及水平扩展底层原理
    在上两节课中,我们已经了解了Kubernetes中最关键的对象Pod,也学习了一些Pod的常见用法。每一个Pod都是应用的一个实例,但是通常来说你不会直接在Kubernetes中创建和运行单个Pod。因为Pod的生命周期是短暂的,即“用后即焚”。理解这一点很重要,这也是“不可变基础设施”这......
  • 07-有状态应用:Kubernete 如何通过 StatefulSet 支持有状态应用?
    在上一节课中,我们学习了Kubernetes中的无状态工作负载,并上手实践了Deployment对象,相信现在你已经慢慢喜欢上Kubernetes了。那么本节课,我们来一起看看Kubernetes中的另外一种工作负载StatefulSet。从名字就可以看出,这个工作负载主要用于有状态的服务发布。关于有状态服务......
  • 前世今生:Kubernete 是如何火起来的?
    本课时,我们就开始正式进入Kubernetes的学习,或许你已经听过或简单了解过Kubernetes,它是一款由Google开源的容器编排管理工具,而我们想要深入地掌握Kubernetes框架,就不得不先了解Kubernetes的前世今生,而这一切都要从“云计算”的兴起开始讲起。云计算平台说来也巧,“云计算......
  • openGauss学习笔记-192 openGauss 数据库运维-常见故障定位案例-XFS文件系统问题
    openGauss学习笔记-192openGauss数据库运维-常见故障定位案例-XFS文件系统问题192.1在XFS文件系统中,使用du命令查询数据文件大小大于文件实际大小192.1.1问题现象在数据库使用过程中,通过如下du命令查询数据文件大小,查询结果大于文件实际的大小。du-shfile192.1.2原因......
  • openGauss学习笔记-193 openGauss 数据库运维-常见故障定位案例-备机卡住-数据库只读
    openGauss学习笔记-193openGauss数据库运维-常见故障定位案例-备机卡住-数据库只读193.1switchover操作时,主机降备卡住193.1.1问题现象一主多备模式下,系统资源不足时,发生switchover,出现主机降备时卡住。193.1.2原因分析当系统资源不足时,无法创建第三方管理线程,导致其管理......
  • kubernetes-dashboard crashloopbackoff
    Kubernetes(简称K8S)是现阶段非常热门的容器编排平台,可以用于部署、扩展和管理容器化应用程序。Kubernetes提供了一个称为Dashboard的web界面,用于监视和管理集群中的各种资源。然而,有时我们可能会遇到kubernetes-dashboardcrashloopbackoff的问题,本篇文章将介绍这个问题的原因和解决......