Pod 和容器的资源管理

在指定一个Pod时,你可以选择为容器指定它所需的每种资源数量。最常指定的资源是 CPU 和内存(RAM);还有其他资源。

当你为 Pod 中的容器指定资源的 request(请求)时,kube-scheduler 会使用这些信息来决定将 Pod 放置在哪个节点上。当你为容器指定资源 limit(限制)时,kubelet 会强制执行这些限制,以确保正在运行的容器使用的该资源不超过你设定的限制。kubelet 还会专门为该容器预留至少 request 指定的系统资源量。

请求和限制

如果运行 Pod 的节点有足够的可用资源,则容器可以使用比其针对该资源指定的 request 更多的资源,这是可能的(也是允许的)。

例如,如果你为一个容器设定了 256 MiB 的 memory request,并且该容器位于调度到具有 8 GiB 内存且没有其他 Pod 的节点的 Pod 中,则该容器可以尝试使用更多的 RAM。

限制则有所不同。cpumemory 限制都由 kubelet(以及容器运行时)应用,并最终由内核强制执行。在 Linux 节点上,Linux 内核使用cgroup 来强制执行限制。cpumemory 限制的强制执行行为略有不同。

cpu 限制通过 CPU 节流(throttling)来强制执行。当容器接近其 cpu 限制时,内核将限制对与容器限制相对应的 CPU 的访问。因此,cpu 限制是内核强制执行的硬限制。容器使用的 CPU 不得超过其 cpu 限制中指定的数量。

memory 限制由内核通过内存不足 (OOM) 终止来强制执行。当容器使用的内存超过其 memory 限制时,内核可能会终止它。然而,只有当内核检测到内存压力时才会发生终止。因此,一个过度分配内存的容器可能不会立即被终止。这意味着 memory 限制是被动强制执行的。容器使用的内存可能超过其 memory 限制,但如果它这样做,则可能会被终止。

资源类型

CPU内存 都属于一种 资源类型。资源类型都有一个基本单位。CPU 代表计算处理能力,其单位是Kubernetes CPU。内存的单位是字节。对于 Linux 工作负载,你可以指定 huge page 资源。Huge page 是 Linux 特有的功能,节点内核会分配比默认页面大小大得多的内存块。

例如,在一个默认页面大小为 4KiB 的系统上,你可以指定限制 hugepages-2Mi: 80Mi。如果容器尝试分配超过 40 个 2MiB 的 Huge Page(总计 80 MiB),则分配会失败。

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 核的机器上运行。

内存资源单位

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.minmemory.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 卷有一些相似之处。但是,当将内存用作卷时(例如内存支持的 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 也使用这种存储来存放节点级别的容器日志、容器镜像以及运行中容器的可写层。

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 不会为临时本地存储应用资源限制。

设置本地临时存储的请求和限制

你可以指定 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 卷,tmpfsemptyDir 卷除外
  • 保存节点级别日志的目录
  • 可写容器层

如果 Pod 使用的临时存储超过了你允许的量,kubelet 会设置一个驱逐信号,触发 Pod 驱逐。

对于容器级别的隔离,如果容器的可写层和日志使用量超过其存储限制,kubelet 会将该 Pod 标记为需要驱逐。

对于 Pod 级别的隔离,kubelet 通过将该 Pod 中所有容器的限制相加来计算 Pod 的总存储限制。在这种情况下,如果所有容器和 Pod 的 emptyDir 卷的本地临时存储使用总量超过 Pod 的总存储限制,kubelet 也会将该 Pod 标记为需要驱逐。

kubelet 支持多种方式来测量 Pod 的存储使用量:

kubelet 定期执行计划检查,扫描每个 emptyDir 卷、容器日志目录和可写容器层。

扫描测量使用了多少空间。

特性状态:Kubernetes v1.31 [beta] (默认禁用:false)

Project Quota 是一个操作系统级别的特性,用于管理文件系统的存储使用。在 Kubernetes 中,你可以启用 Project Quota 来监控存储使用。请确保节点上用于支持 emptyDir 卷的文件系统提供了 Project Quota 支持。例如,XFS 和 ext4fs 都提供 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,你应该:

扩展资源

扩展资源是 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

集群级别扩展资源

集群级别扩展资源不绑定到节点。它们通常由调度器扩展器管理,调度器扩展器负责处理资源消费和资源配额。

你可以在调度器配置中指定由调度器扩展器处理的扩展资源

示例

以下调度器策略配置指示集群级别扩展资源 "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 服务器限制扩展资源的数量必须为整数。有效数量的示例包括 33000m3Ki无效数量的示例包括 0.51500m (因为 1500m 会导致 1.5)。

要在 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 原因表明容器试图使用的内存超过其限制。

下一步可能是检查应用程序代码是否有内存泄漏。如果你发现应用程序行为符合预期,请考虑为该容器设置更高的内存限制(可能也包括请求)。

接下来

最后修改时间:2025 年 1 月 28 日下午 5:09 PST:使用站点相对超链接 (346c97c982)