Pod 和容器的资源管理
在指定一个Pod时,你可以选择为容器指定它所需的每种资源数量。最常指定的资源是 CPU 和内存(RAM);还有其他资源。
当你为 Pod 中的容器指定资源的 request(请求)时,kube-scheduler 会使用这些信息来决定将 Pod 放置在哪个节点上。当你为容器指定资源 limit(限制)时,kubelet 会强制执行这些限制,以确保正在运行的容器使用的该资源不超过你设定的限制。kubelet 还会专门为该容器预留至少 request 指定的系统资源量。
请求和限制
如果运行 Pod 的节点有足够的可用资源,则容器可以使用比其针对该资源指定的 request
更多的资源,这是可能的(也是允许的)。
例如,如果你为一个容器设定了 256 MiB 的 memory
request,并且该容器位于调度到具有 8 GiB 内存且没有其他 Pod 的节点的 Pod 中,则该容器可以尝试使用更多的 RAM。
限制则有所不同。cpu
和 memory
限制都由 kubelet(以及容器运行时)应用,并最终由内核强制执行。在 Linux 节点上,Linux 内核使用cgroup 来强制执行限制。cpu
和 memory
限制的强制执行行为略有不同。
cpu
限制通过 CPU 节流(throttling)来强制执行。当容器接近其 cpu
限制时,内核将限制对与容器限制相对应的 CPU 的访问。因此,cpu
限制是内核强制执行的硬限制。容器使用的 CPU 不得超过其 cpu
限制中指定的数量。
memory
限制由内核通过内存不足 (OOM) 终止来强制执行。当容器使用的内存超过其 memory
限制时,内核可能会终止它。然而,只有当内核检测到内存压力时才会发生终止。因此,一个过度分配内存的容器可能不会立即被终止。这意味着 memory
限制是被动强制执行的。容器使用的内存可能超过其 memory
限制,但如果它这样做,则可能会被终止。
注意
有一个 Alpha 功能MemoryQoS
尝试为内存增加更具前瞻性的限制强制执行(与 OOM killer 的被动强制执行相对)。然而,由于内存密集型应用可能导致潜在的活锁(livelock)情况,此项工作已经暂停。注意
如果你为某个资源指定了限制,但未指定任何请求,并且没有准入时机制为此资源应用默认请求,则 Kubernetes 会复制你指定的限制并将其用作该资源的请求值。资源类型
CPU 和 内存 都属于一种 资源类型。资源类型都有一个基本单位。CPU 代表计算处理能力,其单位是Kubernetes CPU。内存的单位是字节。对于 Linux 工作负载,你可以指定 huge page 资源。Huge page 是 Linux 特有的功能,节点内核会分配比默认页面大小大得多的内存块。
例如,在一个默认页面大小为 4KiB 的系统上,你可以指定限制 hugepages-2Mi: 80Mi
。如果容器尝试分配超过 40 个 2MiB 的 Huge Page(总计 80 MiB),则分配会失败。
注意
你不能超量分配(overcommit)hugepages-*
资源。这与 memory
和 cpu
资源不同。CPU 和内存统称为 计算资源 或 资源。计算资源是可以被请求、分配和消耗的可测量量。它们与API 资源不同。API 资源,例如 Pod 和Service,是可以通过 Kubernetes API 服务器读取和修改的对象。
Pod 和容器的资源请求与限制
对于每个容器,你可以指定资源限制和请求,包括以下各项:
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.limits.hugepages-<size>
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
spec.containers[].resources.requests.hugepages-<size>
尽管你只能为单个容器指定请求和限制,但考虑 Pod 的整体资源请求和限制也很有用。对于特定资源,一个 Pod 资源请求/限制 是该 Pod 中每个容器的该类型资源请求/限制的总和。
Pod 级别资源规范
Kubernetes v1.32 [alpha]
(默认禁用:false)从 Kubernetes 1.32 开始,你还可以指定 Pod 级别的资源请求和限制。在 Pod 级别,Kubernetes 1.33 仅支持针对特定资源类型(cpu
和/或 memory
)的资源请求或限制。此特性目前处于 Alpha 阶段,启用此特性后,Kubernetes 允许你为 Pod 声明一个总体资源预算,这在处理大量容器时特别有用,因为在这种情况下很难准确评估单个容器的资源需求。此外,它还使得 Pod 中的容器可以相互共享空闲资源,从而提高资源利用率。
对于一个 Pod,你可以通过包含以下内容来指定 CPU 和内存的资源限制和请求:
spec.resources.limits.cpu
spec.resources.limits.memory
spec.resources.requests.cpu
spec.resources.requests.memory
Kubernetes 中的资源单位
CPU 资源单位
CPU 资源的限制和请求以 cpu 单位进行度量。在 Kubernetes 中,1 个 CPU 单位相当于 1 个物理 CPU 核 或 1 个虚拟核,这取决于节点是物理主机还是在物理机内运行的虚拟机。
允许使用小数请求。当你将容器的 spec.containers[].resources.requests.cpu
设置为 0.5
时,你请求的 CPU 时间是请求 1.0
CPU 时的一半。对于 CPU 资源单位,Quantity 表达式 0.1
等价于表达式 100m
,可以读作“一百毫核(millicpu)”。有些人说“一百毫核(millicores)”,意思是一样的。
CPU 资源总是指定为资源的绝对数量,而不是相对数量。例如,500m
CPU 表示大致相同的计算能力,无论该容器是在单核、双核还是 48 核的机器上运行。
注意
Kubernetes 不允许你指定 CPU 资源的精度小于 1m
或 0.001
CPU。为了避免意外使用无效的 CPU 数量,在使用小于 1 个 CPU 单位时,使用毫核形式(milliCPU)而不是小数形式来指定 CPU 单位会很有帮助。
例如,你有一个使用 5m
或 0.005
CPU 的 Pod,并且想减少其 CPU 资源。使用小数形式时,很难发现 0.0005
CPU 是一个无效值,而使用毫核形式时,更容易发现 0.5m
是一个无效值。
内存资源单位
memory
的限制和请求以字节为单位进行度量。你可以将内存表示为整数或使用以下数量后缀之一的定点数:E、P、T、G、M、k。你还可以使用二进制对应的单位:Ei、Pi、Ti、Gi、Mi、Ki。例如,以下表示大致相同的值:
128974848, 129e6, 129M, 128974848000m, 123Mi
请注意后缀的大小写。如果你请求 400m
内存,这表示请求 0.4 字节。键入该值的人可能本意是想请求 400 Mebibytes (400Mi
) 或 400 Megabytes (400M
)。
容器资源示例
以下 Pod 有两个容器。两个容器都定义了 0.25 CPU 和 64MiB (226 字节) 的内存请求。每个容器的限制为 0.5 CPU 和 128MiB 内存。可以说该 Pod 的请求总和为 0.5 CPU 和 128 MiB 内存,限制总和为 1 CPU 和 256MiB 内存。
---
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Pod 资源示例
Kubernetes v1.32 [alpha]
(默认禁用:false)可以通过设置 PodLevelResources
feature gate 来启用此特性。以下 Pod 具有显式的请求 1 CPU 和 100 MiB 内存,以及显式的限制 1 CPU 和 200 MiB 内存。pod-resources-demo-ctr-1
容器已设置了显式的请求和限制。然而,pod-resources-demo-ctr-2
容器只会共享 Pod 资源边界内可用的资源,因为它没有设置显式的请求和限制。
apiVersion: v1
kind: Pod
metadata:
name: pod-resources-demo
namespace: pod-resources-example
spec:
resources:
limits:
cpu: "1"
memory: "200Mi"
requests:
cpu: "1"
memory: "100Mi"
containers:
- name: pod-resources-demo-ctr-1
image: nginx
resources:
limits:
cpu: "0.5"
memory: "100Mi"
requests:
cpu: "0.5"
memory: "50Mi"
- name: pod-resources-demo-ctr-2
image: fedora
command:
- sleep
- inf
如何调度带有资源请求的 Pod
创建 Pod 时,Kubernetes 调度器会为其选择一个运行节点。每个节点对于每种资源类型都有最大容量:它可以为 Pods 提供的 CPU 和内存量。调度器确保,对于每种资源类型,已调度容器的资源请求总和小于节点的容量。请注意,尽管节点上的实际内存或 CPU 资源使用量非常低,但如果容量检查失败,调度器仍然会拒绝将 Pod 放置在该节点上。这样做可以防止在资源使用量稍后增加(例如,在每日请求高峰期间)时节点上出现资源短缺。
Kubernetes 如何应用资源请求和限制
当 kubelet 启动作为 Pod 一部分的容器时,kubelet 会将该容器的内存和 CPU 请求及限制传递给容器运行时。
在 Linux 上,容器运行时通常会配置内核cgroup 来应用和强制执行你定义的限制。
- CPU 限制定义了容器可以使用多少 CPU 时间的硬上限。在每个调度周期(时间片)内,Linux 内核会检查是否超出此限制;如果超出,内核会等待,然后才允许该 cgroup 恢复执行。
- CPU 请求通常定义了一个权重。如果几个不同的容器 (cgroups) 想在争用的系统上运行,CPU 请求较大的工作负载将比请求较小的工作负载分配到更多的 CPU 时间。
- 内存请求主要用于 (Kubernetes) Pod 调度。在使用了 cgroups v2 的节点上,容器运行时可能会使用内存请求作为提示来设置
memory.min
和memory.low
。 - 内存限制定义了该 cgroup 的内存上限。如果容器试图分配超出此限制的内存,Linux 内核的内存不足 (out-of-memory) 子系统会激活,通常通过终止尝试分配内存的容器中的一个进程来介入。如果该进程是容器的 PID 1,并且容器被标记为可重启,Kubernetes 会重启该容器。
- Pod 或容器的内存限制也可以应用于内存支持卷(例如
emptyDir
)中的页面。kubelet 将tmpfs
emptyDir 卷视为容器的内存使用,而不是本地临时存储。使用内存支持的emptyDir
时,请务必查看下面的注意事项。
如果容器超出其内存请求,并且其运行的节点整体内存不足,则该容器所属的 Pod 很可能会被驱逐。
容器可能被允许或不被允许长时间超过其 CPU 限制。但是,容器运行时不会因为过度的 CPU 使用而终止 Pod 或容器。
要确定容器是否因资源限制而无法调度或被终止,请参阅故障排除部分。
监控计算和内存资源使用
kubelet 将 Pod 的资源使用情况报告为 Pod status
的一部分。
如果集群中提供了可选的监控工具,则可以直接从Metrics API 或通过监控工具检索 Pod 的资源使用情况。
内存支持的 emptyDir
卷的注意事项
注意
如果你没有为emptyDir
卷指定 sizeLimit
,则该卷可能会消耗高达该 Pod 内存限制 (Pod.spec.containers[].resources.limits.memory
) 的内存。如果你没有设置内存限制,则该 Pod 的内存消耗将没有上限,并且可以消耗节点上的所有可用内存。Kubernetes 根据资源请求 (Pod.spec.containers[].resources.requests
) 调度 Pod,并且在决定另一个 Pod 是否可以放在给定节点上时,不会考虑超出请求的内存使用量。这可能导致拒绝服务,并引起操作系统进行内存不足 (OOM) 处理。可以创建任意数量的 emptyDir
卷,这些卷可能会消耗节点上的所有可用内存,从而使 OOM 更有可能发生。从内存管理的角度来看,进程使用内存作为工作区与使用内存支持的 emptyDir
卷有一些相似之处。但是,当将内存用作卷时(例如内存支持的 emptyDir
),还有以下额外需要注意的事项:
- 存储在内存支持卷上的文件几乎完全由用户应用程序管理。与用作进程工作区不同,你不能依赖于语言级别的垃圾回收等机制。
- 将文件写入卷的目的是保存数据或在应用程序之间传递数据。Kubernetes 或操作系统都不会自动从卷中删除文件,因此当系统或 Pod 处于内存压力下时,这些文件使用的内存无法被回收。
- 内存支持的
emptyDir
因其性能而很有用,但内存通常比其他存储介质(如磁盘或 SSD)容量小得多且成本高得多。为emptyDir
卷使用大量内存可能会影响 Pod 或整个节点的正常运行,因此应谨慎使用。
如果你是集群或命名空间的管理员,你也可以设置ResourceQuota 来限制内存使用;你可能还需要定义一个LimitRange 来进行额外的约束。如果你为每个 Pod 指定了 spec.containers[].resources.limits.memory
,则 emptyDir
卷的最大大小将是该 Pod 的内存限制。
另外,集群管理员可以使用策略机制(例如ValidationAdmissionPolicy)来强制执行新 Pod 中 emptyDir
卷的大小限制。
本地临时存储
Kubernetes v1.25 [stable]
节点拥有本地临时存储,由本地连接的可写设备支持,有时也由 RAM 支持。“临时(Ephemeral)”意味着不能长期保证数据的持久性。
Pod 使用临时本地存储来作为临时空间、缓存和日志存储。kubelet 可以使用临时本地存储,通过将emptyDir
卷挂载到容器中,为 Pod 提供临时空间。
kubelet 也使用这种存储来存放节点级别的容器日志、容器镜像以及运行中容器的可写层。
注意
如果节点发生故障,其临时存储中的数据可能会丢失。你的应用程序无法从本地临时存储中获得任何性能 SLA(例如,磁盘 IOPS)保证。注意
要使资源配额在临时存储上工作,需要做两件事:
- 管理员在命名空间中设置临时存储的资源配额。
- 用户需要在 Pod 的规约中指定临时存储资源的限制。
如果用户未在 Pod 规约中指定临时存储资源限制,则临时存储上的资源配额将不被强制执行。
Kubernetes 允许你跟踪、预留和限制 Pod 可以消耗的临时本地存储量。
本地临时存储的配置
Kubernetes 支持两种在节点上配置本地临时存储的方式:
在这种配置中,你将所有不同类型的临时本地数据(emptyDir
卷、可写层、容器镜像、日志)放入一个文件系统。配置 kubelet 的最有效方式意味着将此文件系统专用于 Kubernetes (kubelet) 数据。
kubelet 也会写入节点级别的容器日志,并将其视为类似于临时本地存储。
kubelet 将日志写入其配置的日志目录(默认为 /var/log
)内的文件;并且有一个用于其他本地存储数据的基本目录(默认为 /var/lib/kubelet
)。
通常,/var/lib/kubelet
和 /var/log
都位于系统的根文件系统上,并且 kubelet 的设计考虑了这种布局。
你的节点可以拥有任意数量不用于 Kubernetes 的其他文件系统。
节点上有一个文件系统,你将其用于来自运行中 Pods 的临时数据:日志和 emptyDir
卷。你可以将此文件系统用于其他数据(例如:与 Kubernetes 无关的系统日志);它甚至可以是根文件系统。
kubelet 也会将节点级别的容器日志写入第一个文件系统,并将其视为类似于临时本地存储。
你还使用了一个由不同逻辑存储设备支持的独立文件系统。在这种配置中,你告诉 kubelet 放置容器镜像层和可写层的目录位于第二个文件系统上。
第一个文件系统不包含任何镜像层或可写层。
你的节点可以拥有任意数量不用于 Kubernetes 的其他文件系统。
kubelet 可以测量它正在使用的本地存储量。前提是你已经使用支持的本地临时存储配置之一来设置节点。
如果你使用的是其他配置,则 kubelet 不会为临时本地存储应用资源限制。
注意
kubelet 将tmpfs
的 emptyDir 卷算作容器内存使用量,而非本地临时存储。注意
kubelet 只跟踪根文件系统的临时存储。那些将独立磁盘挂载到/var/lib/kubelet
或 /var/lib/containers
的操作系统布局将无法正确报告临时存储。设置本地临时存储的请求和限制
你可以指定 ephemeral-storage
来管理本地临时存储。一个 Pod 中的每个容器都可以指定以下一项或两项:
spec.containers[].resources.limits.ephemeral-storage
spec.containers[].resources.requests.ephemeral-storage
ephemeral-storage
的限制和请求以字节为单位。你可以将其表达为一个普通的整数或使用以下后缀之一的定点数:E、P、T、G、M、k。你也可以使用相应的二进制单位:Ei、Pi、Ti、Gi、Mi、Ki。例如,以下数量都表示大致相同的值:
128974848
129e6
129M
123Mi
请注意后缀的大小写。如果你请求 400m
的 ephemeral-storage,这将是 0.4 字节的请求。输入此值的人可能本来想请求 400 mebibytes (400Mi
) 或 400 megabytes (400M
)。
在以下示例中,Pod 有两个容器。每个容器请求 2GiB 的本地临时存储。每个容器限制为 4GiB 的本地临时存储。因此,该 Pod 请求 4GiB 的本地临时存储,限制为 8GiB 的本地临时存储。该限制中的 500Mi 可能被 emptyDir
卷占用。
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
volumeMounts:
- name: ephemeral
mountPath: "/tmp"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
volumeMounts:
- name: ephemeral
mountPath: "/tmp"
volumes:
- name: ephemeral
emptyDir:
sizeLimit: 500Mi
带有临时存储请求的 Pod 如何调度
当你创建一个 Pod 时,Kubernetes 调度器会为其选择一个运行节点。每个节点可以为 Pod 提供最大量的本地临时存储。更多信息,请参阅节点可分配(Node Allocatable)。
调度器确保已调度容器的资源请求总和小于节点的容量。
临时存储使用量管理
如果 kubelet 将本地临时存储作为一种资源进行管理,那么 kubelet 将测量以下对象的存储使用量:
emptyDir
卷,tmpfs 的emptyDir
卷除外- 保存节点级别日志的目录
- 可写容器层
如果 Pod 使用的临时存储超过了你允许的量,kubelet 会设置一个驱逐信号,触发 Pod 驱逐。
对于容器级别的隔离,如果容器的可写层和日志使用量超过其存储限制,kubelet 会将该 Pod 标记为需要驱逐。
对于 Pod 级别的隔离,kubelet 通过将该 Pod 中所有容器的限制相加来计算 Pod 的总存储限制。在这种情况下,如果所有容器和 Pod 的 emptyDir
卷的本地临时存储使用总量超过 Pod 的总存储限制,kubelet 也会将该 Pod 标记为需要驱逐。
注意
如果 kubelet 未测量本地临时存储,则 Pod 即使超出其本地存储限制,也不会因为超出本地存储资源限制而被驱逐。
但是,如果可写容器层、节点级别日志或 emptyDir
卷的文件系统空间不足,节点会将自身污点(taints)标记为本地存储不足,此污点会触发驱逐任何不专门容忍该污点的 Pod。
请参阅本地临时存储的支持配置。
kubelet 支持多种方式来测量 Pod 的存储使用量:
kubelet 定期执行计划检查,扫描每个 emptyDir
卷、容器日志目录和可写容器层。
扫描测量使用了多少空间。
注意
在这种模式下,kubelet 不会跟踪已删除文件的打开文件描述符。
如果你(或容器)在 emptyDir
卷内创建了一个文件,然后某个进程打开了该文件,而你在文件仍然打开时将其删除,那么已删除文件的 inode 将保留直到你关闭该文件,但 kubelet 不会将该空间归类为正在使用。
Kubernetes v1.31 [beta]
(默认禁用:false)Project Quota 是一个操作系统级别的特性,用于管理文件系统的存储使用。在 Kubernetes 中,你可以启用 Project Quota 来监控存储使用。请确保节点上用于支持 emptyDir
卷的文件系统提供了 Project Quota 支持。例如,XFS 和 ext4fs 都提供 Project Quota。
注意
Project Quota 只允许你监控存储使用量;它不强制执行限制。Kubernetes 使用的 Project ID 从 1048576
开始。使用的 ID 注册在 /etc/projects
和 /etc/projid
中。如果系统上出于其他目的使用了此范围内的 Project ID,则必须将这些 Project ID 注册到 /etc/projects
和 /etc/projid
中,以便 Kubernetes 不会使用它们。
配额(Quota)比目录扫描更快、更准确。当一个目录被分配给一个 Project 后,在该目录下创建的所有文件都在该 Project 中创建,而内核只需跟踪该 Project 中文件使用的块数。如果一个文件被创建后又被删除,但仍有一个打开的文件描述符,它会继续占用空间。配额跟踪可以准确记录该空间,而目录扫描则会忽略已删除文件占用的存储。
要使用配额跟踪 Pod 的资源使用量,Pod 必须位于用户命名空间(user namespace)中。在用户命名空间内,内核限制对文件系统 Project ID 的更改,从而确保通过配额计算的存储指标的可靠性。
如果你想使用 Project Quota,你应该:
在kubelet 配置中通过
featureGates
字段启用LocalStorageCapacityIsolationFSQuotaMonitoring=true
特性门控。确保启用了
UserNamespacesSupport
特性门控,并且内核、CRI 实现和 OCI 运行时支持用户命名空间。确保根文件系统(或可选的运行时文件系统)启用了 Project Quota。所有 XFS 文件系统都支持 Project Quota。对于 ext4 文件系统,你需要在文件系统未挂载时启用 Project Quota 跟踪特性。
# For ext4, with /dev/block-device not mounted sudo tune2fs -O project -Q prjquota /dev/block-device
确保根文件系统(或可选的运行时文件系统)在挂载时启用了 Project Quota。对于 XFS 和 ext4fs,挂载选项名称为
prjquota
。
如果你不想使用 Project Quota,你应该:
- 在kubelet 配置中通过
featureGates
字段禁用LocalStorageCapacityIsolationFSQuotaMonitoring
特性门控。
扩展资源
扩展资源是 kubernetes.io
域之外的完全限定资源名称。它们允许集群操作员宣告(advertise)和用户消费非 Kubernetes 内置的资源。
使用扩展资源需要两个步骤。首先,集群操作员必须宣告一个扩展资源。其次,用户必须在 Pod 中请求该扩展资源。
管理扩展资源
节点级别扩展资源
节点级别的扩展资源绑定到节点。
设备插件管理的资源
有关如何在每个节点上宣告设备插件管理的资源,请参阅设备插件。
其他资源
要宣告新的节点级别扩展资源,集群操作员可以向 API 服务器提交 PATCH
HTTP 请求,以指定集群中节点 status.capacity
中的可用数量。此操作后,节点的 status.capacity
将包含一个新资源。status.allocatable
字段将由 kubelet 异步自动更新以包含新资源。
由于调度器在评估 Pod 是否适合某个节点时使用节点的 status.allocatable
值,因此调度器仅在该异步更新后才会考虑新值。在打补丁更新节点容量以包含新资源与请求该资源的第一个 Pod 可以调度到该节点之间可能会有短暂的延迟。
示例
以下示例展示了如何使用 curl
构建 HTTP 请求,以在主节点为 k8s-master
的节点 k8s-node-1
上宣告五个 "example.com/foo" 资源。
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status
注意
在上面的请求中,~1
是在补丁路径中字符 /
的编码。JSON-Patch 中的 operation path 值被解释为 JSON-Pointer。更多详情,请参阅IETF RFC 6901 第 3 节。集群级别扩展资源
集群级别扩展资源不绑定到节点。它们通常由调度器扩展器管理,调度器扩展器负责处理资源消费和资源配额。
你可以在调度器配置中指定由调度器扩展器处理的扩展资源
示例
以下调度器策略配置指示集群级别扩展资源 "example.com/foo" 由调度器扩展器处理。
- 只有当 Pod 请求 "example.com/foo" 时,调度器才会将该 Pod 发送给调度器扩展器。
ignoredByScheduler
字段指定调度器在执行PodFitsResources
谓词时不会检查 "example.com/foo" 资源。
{
"kind": "Policy",
"apiVersion": "v1",
"extenders": [
{
"urlPrefix":"<extender-endpoint>",
"bindVerb": "bind",
"managedResources": [
{
"name": "example.com/foo",
"ignoredByScheduler": true
}
]
}
]
}
消费扩展资源
用户可以在 Pod 规范中消费扩展资源,就像消费 CPU 和内存一样。调度器负责资源记账,确保分配给 Pod 的资源总量不超过可用数量。
API 服务器限制扩展资源的数量必须为整数。有效数量的示例包括 3
、3000m
和 3Ki
。无效数量的示例包括 0.5
和 1500m
(因为 1500m
会导致 1.5
)。
注意
扩展资源取代了不透明整数资源 (Opaque Integer Resources)。用户可以使用任何域名前缀,但kubernetes.io
是保留的。要在 Pod 中消费扩展资源,请将资源名称作为键添加到容器规范的 spec.containers[].resources.limits
映射中。
注意
扩展资源不能超量承诺,因此如果容器规范中同时存在请求和限制,则它们必须相等。只有当所有资源请求(包括 CPU、内存和任何扩展资源)都得到满足时,Pod 才会调度。只要资源请求无法满足,Pod 就会一直处于 PENDING
状态。
示例
下面的 Pod 请求 2 个 CPU 和 1 个 "example.com/foo"(一个扩展资源)。
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: myimage
resources:
requests:
cpu: 2
example.com/foo: 1
limits:
example.com/foo: 1
PID 限制
进程 ID (PID) 限制允许配置 kubelet 以限制给定 Pod 可以消耗的 PID 数量。有关信息,请参阅PID 限制。
故障排查
我的 Pod 处于 pending 状态,并带有事件消息 FailedScheduling
如果调度器找不到适合 Pod 运行的节点,Pod 将保持未调度状态,直到找到合适位置为止。调度器每次未能找到 Pod 位置时,都会产生一个事件。你可以使用 kubectl
查看 Pod 的事件;例如:
kubectl describe pod frontend | grep -A 9999999999 Events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 23s default-scheduler 0/42 nodes available: insufficient cpu
在上面的示例中,名为 "frontend" 的 Pod 因任何节点上的 CPU 资源不足而未能调度。类似的错误消息也可能表示因内存不足(PodExceedsFreeMemory)而失败。通常,如果 Pod 处于 pending 状态并带有此类消息,可以尝试以下几种方法:
- 向集群中添加更多节点。
- 终止不需要的 Pod 以腾出空间给 pending 的 Pod。
- 检查 Pod 是否不大于所有节点。例如,如果所有节点的容量都是
cpu: 1
,那么请求cpu: 1.1
的 Pod 永远不会被调度。 - 检查节点污点(taints)。如果你的大多数节点都有污点,并且新 Pod 不容忍该污点,则调度器只会考虑调度到剩余的没有该污点的节点上。
你可以使用 kubectl describe nodes
命令检查节点容量和已分配量。例如:
kubectl describe nodes e2e-test-node-pool-4lw4
Name: e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
cpu: 2
memory: 7679792Ki
pods: 110
Allocatable:
cpu: 1800m
memory: 7474992Ki
pods: 110
[ ... lines removed for clarity ...]
Non-terminated Pods: (5 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
kube-system fluentd-gcp-v1.38-28bv1 100m (5%) 0 (0%) 200Mi (2%) 200Mi (2%)
kube-system kube-dns-3297075139-61lj3 260m (13%) 0 (0%) 100Mi (1%) 170Mi (2%)
kube-system kube-proxy-e2e-test-... 100m (5%) 0 (0%) 0 (0%) 0 (0%)
kube-system monitoring-influxdb-grafana-v4-z1m12 200m (10%) 200m (10%) 600Mi (8%) 600Mi (8%)
kube-system node-problem-detector-v0.1-fj7m3 20m (1%) 200m (10%) 20Mi (0%) 100Mi (1%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
680m (34%) 400m (20%) 920Mi (11%) 1070Mi (13%)
在上面的输出中,你可以看到如果 Pod 请求超过 1.120 个 CPU 或超过 6.23Gi 内存,则该 Pod 将无法在节点上容纳。
通过查看“Pods”部分,你可以看到哪些 Pod 占用了节点上的空间。
Pod 可用的资源量少于节点容量,因为系统守护进程会占用一部分可用资源。在 Kubernetes API 中,每个节点都有一个 .status.allocatable
字段(详情请参阅NodeStatus)。
.status.allocatable
字段描述了该节点上 Pod 可用的资源量(例如:15 个虚拟 CPU 和 7538 MiB 内存)。有关 Kubernetes 中节点可分配资源的更多信息,请参阅为系统守护进程保留计算资源。
你可以配置资源配额(resource quotas)来限制一个命名空间可以消耗的总资源量。当某个命名空间中存在 ResourceQuota 时,Kubernetes 会对该命名空间中的对象强制执行配额。例如,如果你将不同的命名空间分配给不同的团队,你可以在这些命名空间中添加 ResourceQuota。设置资源配额有助于防止一个团队过度使用任何资源,从而影响其他团队。
你也应该考虑你授予该命名空间的访问权限:对命名空间的完全写访问权限允许具有该权限的人员移除任何资源,包括已配置的 ResourceQuota。
我的容器被终止
你的容器可能因为资源不足而被终止。要检查容器是否因达到资源限制而被杀死,请在你关心的 Pod 上调用 kubectl describe pod
:
kubectl describe pod simmemleak-hra99
输出类似于:
Name: simmemleak-hra99
Namespace: default
Image(s): saadali/simmemleak
Node: kubernetes-node-tf0f/10.240.216.66
Labels: name=simmemleak
Status: Running
Reason:
Message:
IP: 10.244.2.75
Containers:
simmemleak:
Image: saadali/simmemleak:latest
Limits:
cpu: 100m
memory: 50Mi
State: Running
Started: Tue, 07 Jul 2019 12:54:41 -0700
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Started: Fri, 07 Jul 2019 12:54:30 -0700
Finished: Fri, 07 Jul 2019 12:54:33 -0700
Ready: False
Restart Count: 5
Conditions:
Type Status
Ready False
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 42s default-scheduler Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
Normal Pulled 41s kubelet Container image "saadali/simmemleak:latest" already present on machine
Normal Created 41s kubelet Created container simmemleak
Normal Started 40s kubelet Started container simmemleak
Normal Killing 32s kubelet Killing container with id ead3fb35-5cf5-44ed-9ae1-488115be66c6: Need to kill Pod
在上面的示例中,Restart Count: 5
表示 Pod 中的 simmemleak
容器已被终止并重启了五次(目前)。OOMKilled
原因表明容器试图使用的内存超过其限制。
下一步可能是检查应用程序代码是否有内存泄漏。如果你发现应用程序行为符合预期,请考虑为该容器设置更高的内存限制(可能也包括请求)。
接下来
- 获取为容器和 Pod 分配内存资源的实操经验。
- 获取为容器和 Pod 分配 CPU 资源的实操经验。
- 阅读 API 参考中如何定义容器及其资源需求
- 阅读更多关于 XFS 中的Project Quota
- 阅读更多关于kube-scheduler 配置参考 (v1)
- 阅读更多关于Pod 的服务质量等级(Quality of Service classes)