为容器和 Pod 分配内存资源

本页展示了如何为容器分配内存请求(request)和内存限制(limit)。容器能够保证获得其请求的内存量,但不允许使用超过其限制的内存量。

开始之前

你需要有一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具以与你的集群通信。建议在至少有两个节点的集群上运行本教程,且这些节点不能作为控制平面主机。如果你还没有集群,可以通过 minikube 创建一个,或者使用以下 Kubernetes 演练场之一。

要检查版本,请输入 kubectl version

集群中的每个节点必须至少有 300 MiB 的内存。

本页中的一些步骤要求您在集群中运行 metrics-server 服务。如果您已经运行了 metrics-server,则可以跳过这些步骤。

如果您正在运行 Minikube,请运行以下命令来启用 metrics-server

minikube addons enable metrics-server

要查看 metrics-server 或其他资源指标 API(metrics.k8s.io)提供程序是否正在运行,请执行以下命令

kubectl get apiservices

如果资源指标 API 可用,输出内容将包含对 metrics.k8s.io 的引用。

NAME
v1beta1.metrics.k8s.io

创建命名空间

创建一个命名空间,以便您在此练习中创建的资源与集群的其余部分隔离。

kubectl create namespace mem-example

指定内存请求和内存限制

要为容器指定内存请求,请在容器的资源清单中包含 resources.requests.memory 字段。要指定内存限制,请包含 resources.limits.memory

在本练习中,您将创建一个包含一个容器的 Pod。该容器的内存请求为 100 MiB,内存限制为 200 MiB。以下是该 Pod 的配置文件

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

配置文件中的 args 部分为容器启动时提供了参数。"--vm-bytes", "150M" 参数告诉容器尝试分配 150 MiB 的内存。

创建 Pod

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example

验证 Pod 容器是否正在运行

kubectl get pod memory-demo --namespace=mem-example

查看 Pod 的详细信息

kubectl get pod memory-demo --output=yaml --namespace=mem-example

输出显示 Pod 中的容器内存请求为 100 MiB,内存限制为 200 MiB。

...
resources:
  requests:
    memory: 100Mi
  limits:
    memory: 200Mi
...

运行 kubectl top 以获取 Pod 的指标

kubectl top pod memory-demo --namespace=mem-example

输出显示该 Pod 正在使用约 162,900,000 字节的内存,即约 150 MiB。这超过了 Pod 100 MiB 的请求量,但在 Pod 200 MiB 的限制内。

NAME                        CPU(cores)   MEMORY(bytes)
memory-demo                 <something>  162856960

删除您的 Pod

kubectl delete pod memory-demo --namespace=mem-example

超出容器的内存限制

如果节点有可用内存,容器可以超过其内存请求。但容器不允许使用超过其内存限制的内存。如果容器分配的内存超过了其限制,该容器将成为终止候选者。如果容器继续消耗超过其限制的内存,容器将被终止。如果终止的容器可以重启,kubelet 将会重启它,就像处理任何其他类型的运行时故障一样。

在本练习中,您将创建一个尝试分配超过其限制的内存的 Pod。以下是一个 Pod 的配置文件,该 Pod 包含一个内存请求为 50 MiB、内存限制为 100 MiB 的容器

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

在配置文件的 args 部分,您可以看到容器将尝试分配 250 MiB 的内存,这远高于 100 MiB 的限制。

创建 Pod

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example

查看 Pod 的详细信息

kubectl get pod memory-demo-2 --namespace=mem-example

此时,容器可能正在运行或已被杀死。重复执行前一个命令,直到容器被杀死

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

获取容器状态的详细视图

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

输出显示容器因内存不足 (OOM) 而被杀死

lastState:
   terminated:
     containerID: 65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

本练习中的容器可以重启,因此 kubelet 会重启它。重复此命令几次,可以看到容器被反复杀死并重启

kubectl get pod memory-demo-2 --namespace=mem-example

输出显示容器被杀死、重启、再次被杀死、再次重启,依此类推

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

查看有关 Pod 历史记录的详细信息

kubectl describe pod memory-demo-2 --namespace=mem-example

输出显示容器反复启动并失败

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

查看有关集群节点的详细信息

kubectl describe nodes

输出中包含一条记录,显示容器因内存不足状况而被杀死

Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

删除您的 Pod

kubectl delete pod memory-demo-2 --namespace=mem-example

指定一个对节点而言过大的内存请求

内存请求和限制与容器相关联,但将 Pod 视为具有内存请求和限制也很有用。Pod 的内存请求是 Pod 中所有容器的内存请求之和。同样,Pod 的内存限制是 Pod 中所有容器的限制之和。

Pod 调度基于请求。只有当节点有足够的可用内存来满足 Pod 的内存请求时,Pod 才会调度到该节点上运行。

在本练习中,您将创建一个内存请求过大,超过集群中任何节点容量的 Pod。以下是一个 Pod 的配置文件,其中包含一个请求 1000 GiB 内存的容器,这很可能超过了您集群中任何节点的容量。

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "1000Gi"
      limits:
        memory: "1000Gi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

创建 Pod

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example

查看 Pod 状态

kubectl get pod memory-demo-3 --namespace=mem-example

输出显示 Pod 状态为 PENDING。这意味着 Pod 未被调度到任何节点上运行,并且将无限期保持在 PENDING 状态

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

查看有关 Pod 的详细信息,包括事件

kubectl describe pod memory-demo-3 --namespace=mem-example

输出显示容器因节点内存不足而无法调度

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

内存单位

内存资源以字节为单位进行度量。您可以使用纯整数或带有以下后缀之一的定点整数来表示内存:E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki。例如,以下内容表示大致相同的值

128974848, 129e6, 129M, 123Mi

删除您的 Pod

kubectl delete pod memory-demo-3 --namespace=mem-example

如果您没有指定内存限制

如果您没有为容器指定内存限制,则适用以下情况之一

  • 容器对其使用的内存量没有上限。容器可以使用节点上可用的所有内存,这进而可能导致 OOM Killer 被触发。此外,在发生 OOM Kill 时,没有资源限制的容器被杀死的几率更大。

  • 容器运行在具有默认内存限制的命名空间中,并自动被赋予默认限制。集群管理员可以使用 LimitRange 来指定内存限制的默认值。

内存请求和限制的动机

通过为集群中运行的容器配置内存请求和限制,您可以有效地利用集群节点上可用的内存资源。通过保持 Pod 的内存请求较低,您可以使 Pod 有更好的机会被调度。通过设置高于内存请求的内存限制,您可以实现两件事

  • Pod 可以出现活动突发,利用恰好可用的内存。
  • Pod 在突发期间可以使用的内存量被限制在合理的范围内。

清理

删除您的命名空间。这将删除您为此任务创建的所有 Pod

kubectl delete namespace mem-example

接下来

针对应用开发者

针对集群管理员


最后修改于 2025 年 10 月 18 日下午 7:59 (PST): 修复 configure-pod-container 中的拼写错误 (164bfbc27f)