为容器和 Pod 分配内存资源
本页介绍如何为容器分配内存的请求和限制。容器被保证拥有其请求的内存量,但不能使用超过其限制的内存量。
开始之前
你需要拥有一个 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
创建 Namespace
创建一个 Namespace,以便在本练习中创建的资源与集群的其余部分隔离。
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
超出容器的内存限制
如果 Node 有可用内存,容器可以超出其内存请求。但容器不允许使用超过其内存限制的内存。如果容器分配的内存超过其限制,则容器成为终止的候选者。如果容器继续消耗超过其限制的内存,容器将被终止。如果终止的容器可以重启,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
查看集群 Node 的详细信息
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
指定一个对于 Node 来说过大的内存请求
内存请求和限制与容器相关联,但将 Pod 视为具有内存请求和限制是有益的。Pod 的内存请求是 Pod 中所有容器内存请求的总和。同样,Pod 的内存限制是 Pod 中所有容器限制的总和。
Pod 调度基于请求。仅当 Node 有足够的可用内存满足 Pod 的内存请求时,Pod 才能被调度到该 Node 上运行。
在本练习中,你将创建一个内存请求过大,以至于超过集群中任何 Node 容量的 Pod。以下是一个 Pod 的配置文件,该 Pod 有一个容器,请求 1000 GiB 内存,这可能超过集群中任何 Node 的容量。
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 未被调度到任何 Node 上运行,并将无限期地保持 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
输出显示容器因 Node 内存不足而无法调度。
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
如果未指定内存限制
如果未为容器指定内存限制,则会出现以下情况之一
容器使用的内存量没有上限。容器可以使用运行所在的 Node 上的所有可用内存,这可能会触发 OOM Killer。此外,在 OOM Kill 的情况下,没有资源限制的容器被杀死的可能性更大。
容器在设置了默认内存限制的 Namespace 中运行,容器会自动被分配默认限制。集群管理员可以使用 LimitRange 来指定内存限制的默认值。
内存请求和限制的动机
通过为集群中运行的容器配置内存请求和限制,可以有效地利用集群 Node 上的内存资源。通过保持 Pod 的内存请求较低,可以提高 Pod 被调度的机会。通过将内存限制设置得大于内存请求,可以实现两点
- Pod 在活动突增期间可以使用恰好可用的内存。
- Pod 在突增期间可以使用的内存量被限制在一个合理的范围内。
清理
删除你的 Namespace。这将删除你为此任务创建的所有 Pod。
kubectl delete namespace mem-example