iT邦幫忙

2024 iThome 鐵人賽

0
Kubernetes

一起來看 Kubernetes 官方文件吧!系列 第 19

Day19 - 一起來看 Kubernetes 官方文件吧!- pods request/limit (QoS)

  • 分享至 

  • xImage
  •  

前言

前幾天我們看了 node 如何管理 CPU, memory 等硬體資源,接著來看看 pods 如何分配這些資源吧

今日目標

  • 配置 request/limit 並理解兩者差異
  • 了解 pods 的 Quality of Service (QOS)

request/limit 差異

在 nodes 上的硬體資源,預設情況下 pods 都可以無限使用,但這樣若是有個 pods 佔用了 node 上所有的資源的話,會導致整個 node 上面的服務資源吃緊,因此 k8s 設計了一套機制可以避免此情況發生
分別是配置 request/limit (在 container 層),看看官方提供的範例:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

  • request (請求資源量):當 pods 被部署時,會檢查 nodes 是否有足夠的 request 資源,若足夠才會排程到該 nodes 上,且會把 nodes 上可分配的資源吃掉一部分
  • limit (上限量):顧名思義,就是配置此 container 最多能吃到多少資源,當觸發上限時會依照資源的不同有相應的動作
    • memory:kubelet 會直接觸發 OOMKill 導致 container 重啟
    • CPU:kubelet 會觸發 CPU throttling,限制 container 可使用 CPU 的時間 (不會重啟,但某些情況下會導致 container 的效能變的很差)

除了基本的 CPU, memory 之外,若 nodes 上有定義其他資源,也可透過此機制限制 container 的用量 (e.g. GPU)

在執行 kubectl describe node 指令時,也可以看到 nodes 上相對應還可以使用的資源量,且要注意的是 request 無法被超出使用 (最高為 100%),但 limit 是可以超過 100% 的 (但不建議)

❯ k describe no k8s-master1
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                950m (47%)  0 (0%)
  memory             250Mi (8%)  340Mi (12%)
  ephemeral-storage  0 (0%)      0 (0%)
  hugepages-1Gi      0 (0%)      0 (0%)
  hugepages-2Mi      0 (0%)      0 (0%)

當某個 pods 持續卡在 pending 狀態時,有可能就是因為 request 的值任一的 node 都無法滿足導致,通常都可以透過 kubectl describe pods 的 event 欄位看到原因。

測試:添加/刪除一個新的自訂 resource 到 nodes 上

https://kubernetes.io/docs/tasks/administer-cluster/extended-resource-node/

因為 k8s 本身並無法感知到自訂資源的多寡,因此目前有以下限制:

  1. 資源給予必須為整數,無法給予像 4.5 這樣的數值
  2. request 與 limit 必須配置相同

假如說有某個 worker node 具有較高讀寫效能的磁碟,就可以透過自定義的方式加在 nodes 上,如下:

# 會占用 terminal 並開啟 8081 port
kubectl proxy
# 開啟另一個視窗
# 在 k8s-master3 上加上 example.com/special-storage 的資源
# ~1 是 / 的符號寫法
curl --header "Content-Type: application/json-patch+json" \
  --request PATCH \
  --data '[{"op": "add", "path": "/status/capacity/example.com~1special-storage", "value": "40Gi"}]' \
  http://localhost:8001/api/v1/nodes/k8s-master3/status

加上之後,使用 kubectl describe 查看:

> k describe no k8s-master3
 ...
 Capacity:
  cpu:                          2
  ephemeral-storage:            18596Mi
  example.com/special-storage:  40Gi
  hugepages-1Gi:                0
  hugepages-2Mi:                0
  memory:                       3960992Ki
  pods:                         110
Allocatable:
  cpu:                          2
  ephemeral-storage:            17549387338
  example.com/special-storage:  40Gi
  hugepages-1Gi:                0
  hugepages-2Mi:                0
  memory:                       3858592Ki
  pods:                         110
  ...

接著建立一個 pods 測試,先故意使用超過 40Gi:

# test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: special-storage
  namespace: default
spec:
  containers:
  - name: special-storage
    image: nginx
    resources:
      requests:
        example.com/special-storage: 100Gi
      limits:
        example.com/special-storage: 100Gi
❯ k apply -f test.yaml
❯ k get po
special-storage                   0/1     Pending   0          2s
❯ k describe po special-storage
...
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  118s  default-scheduler  0/3 nodes are available: 3 Insufficient example.com/special-storage. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod

因為資源不夠的關係,因此無法被排程上任何的 nodes

修改成為 20Gi 之後,再重新部署:

...
      requests:
        example.com/special-storage: 20Gi
      limits:
        example.com/special-storage: 20Gi
...
❯ k apply -f test.yaml --force
❯ k get po
special-storage                   1/1     Running   0          44s

接著檢查 k8s-master3 上的資源,確實被佔用了 20Gi 的資源:

❯ k describe no k8s-master3
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                     Requests    Limits
  --------                     --------    ------
  cpu                          750m (37%)  0 (0%)
  memory                       110Mi (2%)  0 (0%)
  ephemeral-storage            0 (0%)      0 (0%)
  hugepages-1Gi                0 (0%)      0 (0%)
  hugepages-2Mi                0 (0%)      0 (0%)
  example.com/special-storage  20Gi        20Gi

刪除自訂資源:

curl --header "Content-Type: application/json-patch+json" \
  --request PATCH \
  --data '[{"op": "remove", "path": "/status/capacity/example.com~1special-storage"}]' \
  http://localhost:8001/api/v1/nodes/k8s-master3/status

pods 的 Quality of Service (QoS)

pods 會根據 request/limit 的配置排列組合,分成三種 QoS classes,由優先度高到低分別為:

  • Guaranteed:pods 內每個 containers 都有配置 CPU/memory limit + request,且兩者一致
    • 若是 kubelet 有啟用 static CPU policy 的話,也僅有 Guaranteed 狀態的 pods 有資格使用。
  • Burstable:不符合 Guaranteed 條件,且至少一個 container 有配置 CPU or memory limit/request
  • BestEffort:pods 內完全沒有 container 有配置 CPU/memory limits + requests

這三個條件會直接影響到前幾天討論的驅逐優先度,當資源不足出現徵用時,kubelet 會優先挑 BestEffort 的 pods 開刀

這個狀態是 k8s 自行會去以照上述的情況加入到 .status.qosClass 中的,如 apiserevr 的例子:

k get po -n kube-system kube-apiserver-k8s-master1 -o yaml | grep qos
  qosClass: Burstable

當 yaml 內的 container 僅定義了 limit 但沒有配置 request 時,k8s 將會自動的把 limit 的值套用於 request,CPU 或 memory 都一樣。

Memory QoS with cgroup v2

在 container 中控制能使用多少 memory 是透過 cgroup 機制做到的:

  • 當配置 limit 時,會配置該 container 對應 process 的 memory.max 值,若該 container 使用超過時就會觸發 OOMkill

關於 memory.min 的配置,本來依照上方 request/limit 的相對關係,似乎 request 的值就會被配置為 memory.min 相當合理
但實際上測試時,會發現無論 request 配置多少,memory.min 都會被配置為 0
因此稍微查找了一下資料,此功能是在 2020/3 月提案的,編號為 Keps-2570,且此功能在 v1.22 時進入 alpha 階段,但直到目前的 v1.31 版本仍然不是預設開啟功能
在 github 討論上會發現前幾周此問題才有人發布討論,說目前啟用此功能的情況下,可能會導致 container 的限制行為,會比不限制還糟糕,因此此功能目前似乎短期內不會有什麼進展 ~~
ref: https://github.com/kubernetes/enhancements/issues/2570

測試 limit 與 cgroupv2 的配置

cgroupv2 的相關檔案會放在 /sys/fs/cgroup 目錄上,而 kubelet 會在此目錄內建立一個 kubepods.slice,裡面又根據 besteffort 與 burstable 分成不同目錄,會根據不同的 pods UID 分別放

先根據官方的 memory 範例建立兩個 pods,一個有配置 limit/request 一個沒有:


# memory.yaml
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"]
---
apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-no-limit-request
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources: {}
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
 

透過此 yaml 建立 pods 並確認位置:

❯ k apply -f memory.yaml
❯ k get po -n mem-example -o wide
NAME                           READY   STATUS    RESTARTS   AGE     IP           NODE          NOMINATED NODE   READINESS GATES
memory-demo                    1/1     Running   0          6h53m   10.0.1.150   k8s-master2   <none>           <none>
memory-demo-no-limit-request   1/1     Running   0          6h37m   10.0.1.110   k8s-master2   <none>           <none>
❯ k get po -n mem-example -o yaml | grep uid
    uid: 1be9ad71-0451-4d1e-bfd4-585a761e134c
    uid: 63da18ec-47b3-4761-bfa5-5dc1066fed4d

依順序來看,uid: 1be9 為有配置 request/limit 的 pods, 而 63da 開頭的則沒有特別配置

接著到 k8s-master2 主機上查看 cgroupv2 的相關設定:

# 查看有限制的情況,根據 uid 1be9 去搜尋
➜  ~ cat /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1be9ad71_0451_4d1e_bfd4_585a761e134c.slice/memory.max
209715200
➜  ~ cat /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1be9ad71_0451_4d1e_bfd4_585a761e134c.slice/memory.min
0
# 查看未限制的情況
➜  ~ cat /sys/fs/cgroup/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod63da18ec_47b3_4761_bfa5_5dc1066fed4d.slice/memory.max
max
➜  ~ cat /sys/fs/cgroup/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod63da18ec_47b3_4761_bfa5_5dc1066fed4d.slice/memory.min
0

可發現當 memory limit 配置為 200Mi 時,memory.max 配置為 209715200 (/1024/1024 = 200),也就是 200 MiB
當未配置 limit 時,則被配置為 max,意即不做限制

Some behavior is independent of QoS class

某些行為是與 Qos class 無關的,比如:

  • 當 container 超過資源 limit 用量時,會被 kubelet resetart,且不會影響到 pods 內裡面其他的 container
  • 當 container 超過資源 request 用量時,且該 nodes 正面臨資源壓力的情況下,這類型的 pods 會優先被驅逐重啟,也就會跑到其他的 nodes 上面
  • 當 preempt(搶佔) 發生時,kube-scheduler 不會考慮 pods Qos class (通常是發生於整個 cluster 沒有充足資源可以執行所有定義的 pods 發生)

preempt… 明天來看看好了,有點不是很理解這個行為 ~

結論

透過定義 CPU/memory 的 request/limit,k8s 就會大致上自動分配 pods 的等級,讓內部的機制知道哪些 pods 可以優先被排除

而透過自定義的資源,則可以針對 nodes 的特性打上一些特殊的標籤,讓有特定需求的 pods 使用

最後關於 kube-scheduler 的 preempt 機制還不是很了解,明天再來看看吧 ~~

參考

https://kubernetes.io/docs/tasks/administer-cluster/extended-resource-node/

https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/

https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/

https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#besteffort

https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2570-memory-qos/#readme


上一篇
Day18 - 一起來看 Kubernetes 官方文件吧!- Node-pressure Eviction
系列文
一起來看 Kubernetes 官方文件吧!19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言