為容器和 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