为容器和 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
字段。要指定内存限制,请包含 resources:limits
。
在这个练习中,你将创建一个包含一个容器的 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 的配置文件,该容器的内存请求为 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 Killer 的情况下,没有资源限制的容器被杀死的几率更大。
容器运行在一个具有默认内存限制的命名空间中,容器会自动被分配默认限制。集群管理员可以使用 LimitRange 来指定内存限制的默认值。
内存请求和限制的动机
通过为集群中运行的容器配置内存请求和限制,你可以高效利用集群节点上可用的内存资源。通过将 Pod 的内存请求设置得较低,可以提高 Pod 被调度的机会。通过将内存限制设置为大于内存请求,你可以实现两个目的:
- Pod 可以突发活动,利用恰好可用的内存。
- Pod 在突发期间可以使用的内存量被限制在合理的范围内。
清理
删除你的命名空间。这将删除你为该任务创建的所有 Pod。
kubectl delete namespace mem-example