Kubernetes的资源模型与资源管理(20250219)

Kubernetes的资源模型与资源管理(20250219)

​ Pod 是最小的原子调度单位。这也就意味着,所有跟调度和资源管理相关的属性都应该是属于 Pod 对象的字段。而这其中最重要的部分,就是 Pod 的 CPU 和内存配置

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: wp
    image: wordpress
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

​ 在 Kubernetes 中,像 CPU 这样的资源被称作“可压缩资源”(compressible resources)。它的典型特点是,当可压缩资源不足时,Pod 只会“饥饿”,但不会退出。

​ 而像内存这样的资源,则被称作“不可压缩资源(incompressible resources)。当不可压缩资源不足时,Pod 就会因为 OOM(Out-Of-Memory)被内核杀掉。

​ CPU是可压缩资源,当CPU不足时POD只会饥饿不会退出。内存和硬盘是不可压缩资源,当不可压缩资源不足时,POD就会因为OOM等被内核杀掉

​ Kubernetes 里为 CPU 设置的单位是“CPU 的个数”。比如,cpu=1 指的就是,这个 Pod 的 CPU 限额是 1 个 CPU。当然,具体“1 个 CPU”在宿主机上如何解释,是 1 个 CPU 核心,还是 1 个 vCPU,还是 1 个 CPU 的超线程(Hyperthread),完全取决于宿主机的 CPU 实现方式。Kubernetes 只负责保证 Pod 能够使用到“1 个 CPU”的计算能力。

环境 cpu=1 的等效资源 性能特点
物理机(无超线程) 1 物理核心 稳定,独占物理资源
物理机(有超线程) 1 超线程(逻辑 CPU) 可能受同核心其他超线程负载影响
云虚拟机(通用实例) 1 vCPU(通常为超线程或部分物理核心) 共享物理资源,性能可能波动
容器运行时(cgroups) 100ms/100ms 的 CPU 时间片 算力隔离,但依赖宿主机的 CPU 调度机制

​ 此外,Kubernetes 允许你将 CPU 限额设置为分数,比如在我们的例子里,CPU limits 的值就是 500m。所谓 500m,指的就是 500 millicpu,也就是 0.5 个 CPU 的意思。这样,这个 Pod 就会被分配到 1 个 CPU 一半的计算能力。

​ 你也可以直接把这个配置写成 cpu=0.5。但在实际使用时,我还是推荐你使用 500m 的写法,毕竟这才是 Kubernetes 内部通用的 CPU 表示方式。

Pod 里的 requests 和 limits

Kubernetes 里 Pod 的 CPU 和内存资源,实际上还要分为 limits 和 requests 两种情况

spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory

​ Kubernetes 的 requests+limits 的做法,其实就是上述思路的一个简化版:用户在提交 Pod 时,可以声明一个相对较小的 requests 值供调度器使用,而 Kubernetes 真正设置给容器 Cgroups 的,则是相对较大的 limits

​ 当 Pod 里的每一个 Container 都同时设置了 requests 和 limits,并且 requests 和 limits 值相等的时候,这个 Pod 就属于 Guaranteed 类别

k8s 三种 QoS 等级: Guaranteed(可保证的)、Burstable(突发的)、BestEffort(尽力而为的)

如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

​ 需要注意的是,当 Pod 仅设置了 limits 没有设置 requests 的时候,Kubernetes 会自动为它设置与 limits 相同的 requests 值,所以,这也属于 Guaranteed 情况。

​ 而当 Pod 不满足 Guaranteed 的条件,但至少有一个 Container 设置了 requests。那么这个 Pod 就会被划分到 Burstable 类别

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits
        memory: "200Mi"
      requests:
        memory: "100Mi"

只要有一个 container 设置了 requests 就行

​ 而如果一个 Pod 既没有设置 requests,也没有设置 limits,那么它的 QoS 类别就是 BestEffort。比如下面这个例子:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx

​ 那么,Kubernetes 为 Pod 设置这样三种 QoS 类别,具体有什么作用呢?

​ 实际上,QoS 划分的主要应用场景,是当宿主机资源紧张的时候,kubelet 对 Pod 进行 Eviction(即资源回收)时需要用到的。

Eviction 的默认阈值

​ 当 Kubernetes 所管理的宿主机上不可压缩资源短缺时,就有可能触发 Eviction。比如,可用内存(memory.available)、可用的宿主机磁盘空间(nodefs.available),以及容器运行时镜像存储空间(imagefs.available)

注意,是“不可压缩资源”的短缺。

​ Kubernetes 为你设置的 Eviction 的默认阈值如下所示

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

​ 上述各个触发条件在 kubelet 里都是可配置的。比如下面这个例子:

kubelet --eviction-hard=imagefs.available<10%,memory.available<500Mi,nodefs.available<5%,nodefs.inodesFree<5% --eviction-soft=imagefs.available<30%,nodefs.available<10% --eviction-soft-grace-period=imagefs.available=2m,nodefs.available=2m --eviction-max-pod-grace-period=600

​ Soft Eviction 允许你为 Eviction 过程设置一段“优雅时间”,比如上面例子里的 imagefs.available=2m,就意味着当 imagefs 不足的阈值达到 2 分钟之后,kubelet 才会开始 Eviction 的过程。而 Hard Eviction 模式下,Eviction 过程就会在阈值达到之后立刻开始

​ 当宿主机的 Eviction 阈值达到后,就会进入 MemoryPressure 或者 DiskPressure 状态,从而避免新的 Pod 被调度到这台宿主机上。

​ 而当 Eviction 发生的时候,kubelet 具体会挑选哪些 Pod 进行删除操作,就需要参考这些 Pod 的 QoS 类别了。

  • 首当其冲的,自然是 BestEffort 类别的 Pod。
  • 其次,是属于 Burstable 类别、并且发生“饥饿”的资源使用量已经超出了 requests 的 Pod。
  • 最后,才是 Guaranteed 类别。并且,Kubernetes 会保证只有当 Guaranteed 类别的 Pod 的资源使用量超过了其 limits 的限制,或者宿主机本身正处于 Memory Pressure 状态时,Guaranteed 的 Pod 才可能被选中进行 Eviction 操作。

​ 当然,对于同 QoS 类别的 Pod 来说,Kubernetes 还会根据 Pod 的优先级来进行进一步地排序和选择。

​ 在理解了 Kubernetes 里的 QoS 类别的设计之后,我再来为你讲解一下Kubernetes 里一个非常有用的特性:cpuset 的设置。

cpuset 的设置

​ 我们知道,在使用容器的时候,你可以通过设置 cpuset 把容器绑定到某个 CPU 的核上,而不是像 cpushare 那样共享 CPU 的计算能力。

​ 事实上,cpuset 方式,是生产环境里部署在线应用类型的 Pod 时,非常常用的一种方式。

​ 这样的需求在 Kubernetes 里又该如何实现

​ 首先,你的 Pod 必须是 Guaranteed 的 QoS 类型;然后,你只需要将 Pod 的 CPU 资源的 requests 和 limits 设置为同一个相等的整数值即可。

requests 和 limits 值相等的时候

​ 比如下面这个例子:

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "200Mi"
        cpu: "2"

​ 这时候,该 Pod 就会被绑定在 2 个独占的 CPU 核上。当然,具体是哪两个 CPU 核,是由 kubelet 为你分配的。

posted @ 2025-02-19 10:22  guixiang  阅读(101)  评论(0)    收藏  举报