管控好Pods的記憶體資源

Posted by Kubeguts on 2021-05-23

當Pod在運行時,必須要曉得創建時若沒有特別限制Pod的Container只能使用多少記憶體的話,一但容器內的服務突然發生記憶體用量大增的情況,可能會導致整個Node的資源都會吃光光了

所以本節要來探討Pod在記憶體控制的情況是如何,以及要如何在Pod內設置Memory資源,Here we go!

接下來的範例都是透過minikube所建置的

事前準備

  • 確保cluster內的每個node都有300MB記憶體
  • 先在minikube建立metrics-server
1
minikube addons enable metrics-server

會看到以下output

1
2
3
minikube addons enable metrics-server
▪ Using image k8s.gcr.io/metrics-server/metrics-server:v0.4.2
🌟 The 'metrics-server' addon is enabled

透過 kubectl get apiservices確認 metrics.k8s.io是否有在運行

1
2
NAME                                   SERVICE                      AVAILABLE   AGE
v1beta1.metrics.k8s.io kube-system/metrics-server True 42s
  • 定義namespace將範例用的pod與其他服務隔離開來
1
kubectl create namespace mem-example

限制Pod的記憶體

spec底下以下屬性來限制記憶體資源使用

  • resources:requests: 定義Pod需要多少記憶體資源
  • resources:limits: 限制Pod運行時最高只能用多少記憶體資源

接下來會使用一個壓測用的容器,來建立Pod,並在 args中分配這個容器有 150M的記憶體資源

pods-resource-memory-request-limit.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
namespace: mem-example
spec:
containers:
- name: memory-demo-ctr
image: polinux/stress
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

建立Pod

1
kubectl apply -f pods-resource-memory-request-limit.yml --namespace=mem-example

確認Pod是否有在運作

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

檢視Pod的詳細資訊

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

可以看到spec.resource定義了記憶體的限制資訊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Pod
metadata:
...
spec:
containers:
- args:
- --vm
- "1"
- --vm-bytes
- 150M
- --vm-hang
- "1"
command:
- stress
image: polinux/stress
imagePullPolicy: Always
name: memory-demo-ctr
resources:
limits:
memory: 200Mi
requests:
memory: 100Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-xk6rl
readOnly: true
...

接著透過 kubectl top讀取pod的metrics資訊

1
2
3
4
5
kubectl top pod memory-demo --namespace=mem-example

W0523 09:22:58.077337 17272 top_pod.go:140] Using json format to get metrics. Next release will switch to protocol-buffers, switch early by passing --use-protocol-buffers flag
NAME CPU(cores) MEMORY(bytes)
memory-demo 84m 150Mi

可看到剛創建的Pod使用了150 Megabyte的記憶體資源,比原本分配給Pod 100 MB來得高,但還沒超出200MB的限制

超出記憶體的情況

當Container的記憶體耗費超出所限制的話,就會優先列為被終止的對象

接著來創建一個會耗費過多記憶體的範例,直接給容器250MB,大於原本限制的100MB

memory-demo-2-ctr.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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"]

看一下Pod運行後會怎樣,可看到狀態為 OOMKilled,表示記憶體溢出,準備被砍掉,且一直被Restart

1
2
3
4
kubectl get pod memory-demo-2 --namespace=mem-example

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

在查看Pod的詳細資訊,可看到被終止的原因為記憶體消耗超出限制

1
kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
status:
conditions:
...
containerStatuses:
- containerID: docker://e0e8608e3045cf247998c1ed57e01259dabdb8f990284704c14d8e7f75ab18ed
image: polinux/stress:latest
imageID: docker-pullable://polinux/stress@sha256:b6144f84f9c15dac80deb48d3a646b55c7043ab1d83ea0a697c09097aaad21aa
lastState:
terminated: # 這裡可以看到終止的原因
containerID: docker://e0e8608e3045cf247998c1ed57e01259dabdb8f990284704c14d8e7f75ab18ed
exitCode: 1
finishedAt: "2021-05-23T09:31:03Z"
reason: OOMKilled
startedAt: "2021-05-23T09:31:03Z"

透過 kubectl describe pod memory-demo-2 --namespace=mem-example 確認pod的運行狀況

1
2
3
4
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning BackOff 3m1s (x7 over 4m14s) kubelet Back-off restarting failed container

接著檢視node資訊 kubectl describe nodes,會看到該Container的事件訊息

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

定義一個超過Node的記憶體上限的Pod

若目前Node的記憶體為8GB,直接分配1000GB的記憶體,來看看會發生什麼事

pods-resource-memory-request-limit-3.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-3
namespace: mem-example
spec:
containers:
- name: memory-demo-3-ctr
image: polinux/stress
resources:
limits:
memory: "1000Gi"
requests:
memory: "1000Gi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
1
kubectl apply -f pods-resource-memory-request-limit-3.yml --namespace=mem-example

檢視Pod資訊,會看到狀態是Pending中

1
2
3
4
kubectl get pod memory-demo-3 --namespace=mem-example

NAME READY STATUS RESTARTS AGE
memory-demo-3 0/1 Pending 0 33s

透過kubectl describe可以了解到,Pod無法被Scheduler排入創建列表內,因為Node沒有足夠記憶體可以給他

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

Warning FailedScheduling 65s (x2 over 65s) default-scheduler 0/1 nodes are available: 1 Insufficient memory.
透過 `kubectl describe`方式可以幫助我們了解一個Pod的執行狀況是如何

若沒有為Pod定義記憶體限制的情況

會有兩種情況:

  • 若有歸類在namespace底下,會根據 LimitRange 來分配預設的限制數目
  • 若單純只是起動起來,那就有可能會遭遇 OOMKilled,表示記憶體溢出,準備被砍掉,且一直被Restart

參考