资源申请和限制

Kubernetes 默认为 Pod 提供了对 CPU 和内存两类资源的申请和限制功能,其中 Pod 的资源申请量会影响该 Pod 的调度结果

Pod 的资源申请和限制都是针对其中的 单个容器 而言的,pod.spec.containers.resources.requests 决定了容器的 资源申请量pod.spec.containers.resources.limits 决定了容器的 资源限制量

资源申请

上面提到过,Kubernetes 默认支持指定 Pod 中每个容器的 CPU 和内存两类资源的 申请量

🪐 资源申请量 会影响 Pod 的 调度结果,因为 Pod 的调度是以申请量为准的,而不是以 Pod 的资源实际使用量。所以 Pod 的资源实际使用量 不受该字段的限制,可高于它,也可以低于它。

包含资源申请的 Pod 配置文件

下面展示一个包含了资源申请的 Pod 的配置文件案例,该 Pod 申请了 1/5 个 CPU 线程 以及 10 MB 内存

apiVersion: v1
kind: Pod
metadata:
  name: requests-pod
spec:
  containers:
  - image: busybox
    command: ["dd", "if=/dev/zero", "of=/dev/null"]    # 因为该 Pod 没有限制 CPU 使用量,所以该命令会令该容器跑满一个 CPU 线程
    name: main
    resources:
      requests:    # 资源申请量
        cpu: 200m    # 1000m = 1, 所以申请了 200m = 1/5 个 CPU 线程
        memory: 10Mi    # 申请了 10 MB 内存

在一个拥有 2 核单线程的宿主机中,在该 Pod 中使用 top 命令查看 dd 执行所花费的 CPU 资源量会发现为 50% 左右,从这个现象可以看出:

  1. 资源申请量与实际使用量并无联系,它不会限制容器的资源实际使用,所以该进程可以将一个线程跑满;

  2. 容器中使用 top 看到的是相对于整个宿主机的资源使用情况,所以显示的是 50% 而不是 100%

资源申请量对资源调度的影响

每个 Node 的 CPU 和内存数量是一致的,可以通过 kubectl describe nodes 中输出的 CapacityAllocatable 看到其可分配的资源数,这是由每个 Node 上的 Kubelet 组件 会向 API 服务器 报告的;并且该命令可以查看 Node 上现有所有 Pod 的资源申请量以及总分配的资源量。

调度过程

🌈 在调度 Pod 过程中的,不关注各类资源在当前时刻的实际使用量,而只关心节点上部署的所有 Pod 的资源申请量之和,这就是调度过程中 Predicate 过程 做的事,从而筛选出可以部署该 Pod 请求的所有 Node。在 Priority 过程 会从所有候选 Node 中筛选出最佳 Node,其中有两个基于资源请求量的优先级排序优选函数:

  1. LeastRequestedPriority:优先将 Pod 调度至 请求量最少 的 Node(目标是负载均衡);

  2. MostRequestedPriority:优先将 Pod 调度至 请求量最多 的 Node(目标是节点资源利用率最高);

像之前创建的未指定申请量和限制量的 Pod,则默认都是 0,这类 Pod 会被划分为最低优先级,可随时被高优先级 Pod 剥夺资源使用权,这个将在 Pod 的 QoS 优先级 一章进行详细说明。

调度失败情况

调度失败的 Pod 状态会卡在 Pending 状态,通过 kubectl describe pod 可以看到其提示调度失败,它会间隔一段时间后重新调度,直至调度成功为止。

CPU 的弹性使用

Pod 指定 CPU 的申请量时,对应的是底层 Docker 容器的 --cpu-share 字段,该字段是一个相对值。

打一个比方,假设有一个 1 核单线程的宿主机,上面创建了两个 Pod,一个 Pod A 的 CPU 申请量为 200m,另一个 B 为 400m,此时该 Node 还剩下 400m 未分配。此时两个 Pod 的资源实际使用量是怎样的呢?分下面三种情况讨论:

  1. A 跑满,B 空闲:此时 A 可以使用整个宿主机的所有 CPU 时间;

  2. A 空闲,B 跑满:此时 B 可以使用整个宿主机的所有 CPU 时间;

  3. A 跑满,B 跑满:由于 A 的申请量与 B 相比为 1:2,因此 A 可以使用约 333m,B 可以使用约 666m;

从上述结果可以看出,Docker 容器的 --cpu-share 可以实现同一个 Node 上的容器的 CPU 资源的弹性比例分配。

资源限制

上面提到过,Kubernetes 默认支持指定 Pod 中每个容器的 CPU 和内存两类资源的 限制量

限制内存上限的原因

☀️ 在上一节提到过,Pod 的 CPU 资源的使用是可以弹性伸缩的,但是内存资源不一样,一旦占用,除非进程主动释放,否则是无法压缩的,因此为了保证可靠的服务质量,有必要对内存资源设置资源上限。

包含资源限制的 Pod 配置文件

下面展示创建一个包含资源限制的 Pod 的配置文件案例,该 Pod 中的一个容器最多可以使用 1个 CPU 线程 以及 20 MB 内存

apiVersion: v1
kind: Pod
metadata:
  name: limited-pod
spec:
  containers:
  - image: busybox
    command: ["dd", "if=/dev/zero", "of=/dev/null"]
    name: main
    resources:
      limits:    # 设置容器的资源使用上限
        cpu: 1    # 最多使用 1 个 CPU 线程
        memory: 20Mi    # 最多使用 20 MB 内存

当没有指定申请量 requests 时,它将被设置为与限制量 limits 相同的值。

资源超卖

资源超卖的意思是指宿主机中的 Pod 的 资源限制量总和 超过了宿主机的 实际资源总量

在这种情况下,对于 CPU 资源是无所谓的,因为容器对 CPU 资源本身可以弹性使用,只是会分配不到那么多的 CPU 时间而已,还是可以正常运行的。

🔥 但是对于 内存 资源不行,当进程尝试申请分配比 限制量 更多的内存时,会被 OOMKilled,即 Out Of Memory,该状态可使用 kubectl describe nodes 看到。若 Pod 的重启策略为 AlwaysOnFailure,则进程会立即重启,但是若继续超限,仍然会被继续杀死。这种情况下,Pod 的状态会处于 CrashLoopBackOff,表示该 Pod 由于某些原因无法正常启动,但是 Kubernetes 仍然在尝试重启它。

一些坑

🚀 在容器内看到的始终是 Node 的 CPU 总核数 以及 内存总量。这意味着如果跑在容器内的应用尝试根据查看到的资源信息分配线程数或内存量时,可能会造成超卖导致 Pod 被 OOMKilled 的问题。

解决方法:可以使用 Downward API 将 CPU 和内存的限制量传入容器并使用该值,或者通过 cgroups 系统获取限制量。

定义和申请自定义资源

Kubernetes 允许用户为 Node 添加自定义的资源并初始化该资源的总量。

定义资源

可以通过向 API 服务器发起 PATCH 请求,将定义的资源名及资源总量放入请求体中提交。

🍓 资源名可以是不以 kubernetes.io 域名开头的任意值,例如 example.org/my-resource

🥑 自定义资源的初始总量必须是整数(例如不能设为 100m;可以设为 1000m,或者直接设为 1)或者带 Mi, Gi 这种单位的整数!!对于前者,容器针对该资源申请或限制时同样只能用整数;对于后者,则同样只能用整数,但是单位可以使用 Mi, Gi 这类单位中任意一种。该值降自动从 Node 的 capacity 字段复制到 allocatable 字段。

下面展示一个例子,创建一个名为 example.com/ysj 的资源,其初始总量为 800Gi

curl -X PATCH '<API服务器地址和端口>/api/v1/nodes/<Node名>/status' \
--header 'Content-Type: application/json-patch+json' \
--data-raw '[
    {
        "op": "add",
        "path": "/status/capacity/example.com~1ysj",
        "value": "800Gi"
    }
]'

~1/ 的编码

删除资源

相应的,可以通过向 API 服务器发起 PATCH 请求,将自定义的资源删除。

下面展示一个例子,删除上面创建的名为 example.com/ysj 的资源:

curl --location --request PATCH '<API服务器地址和端口>/api/v1/nodes/<Node名>/status' \
--header 'Content-Type: application/json-patch+json' \
--data-raw '[
    {
        "op": "remove",
        "path": "/status/capacity/example.com~1ysj"
    }
]'

创建申请自定义资源的 Pod

创建申请自定义资源的 YAML 配置文件的方式与申请 CPU、内存等资源的 Pod 的类似,都是在 pod.spec.resources.request 中进行指定。比如申请上面创建的 example.com/ysj 资源,申请量为 200 MB

resources:
  requests:    # 指定申请量
    example.com/ysj: 200Mi
  limits:    # 指定限制量(必须)
    example.com/ysj: 300Mi

Last updated

Was this helpful?