kubernetes集羣節點資源預留

問題

默認kubelet沒有配置資源預留,host上所有的資源(cpu, 內存, 磁盤) 都是可以給 pod 使用的。而當一個節點上的 pod 將資源喫滿時,系統層面可能會幹掉 k8s 核心組件進程, 從而導致改節點 not ready,此時 k8s 會將改節點的所有 pod 調度到其他節點重建,如果其他節點資源也不夠,那麼其他節點也會 not ready,進而引起集羣雪崩效應。

如何避免

通過爲 k8s 設置 kube 組件資源預留和 system 系統資源預留,保證節點的 pod 不會喫滿節點資源

目前支持cpu, memory, ephemeral-storage 三種資源預留。

  1. cpu: cpu是配置cpu shares,實際上對應的是cpu的優先級,簡單來說,這個在cpu繁忙時,它能有更高優先級獲取更多cpu資源。
  2. memory: k8s默認不使用 swap,這裏指的就是實際的內存
  3. ephemeral-storagekubernetes1.8開始引入的一個資源限制的對象,kubernetes 1.10版本中kubelet默認已經打開的了,到目前1.11還是beta階段,主要是用於對本地臨時存儲使用空間大小的限制,如對podempty dir/var/lib/kubelet、日誌、容器可讀寫層的使用大小的限制。

Node CapacityNode的所有硬件資源,kube-reserved是給kube組件預留的資源,system-reserved是給System進程預留的資源, eviction-thresholdkubelet eviction的閾值設定,allocatable纔是真正scheduler調度Pod時的參考值(保證Node上所有Podsrequest resource不超過Allocatable

Node Allocatable Resource = Node Capacity - Kube-reserved - system-reserved - eviction-threshold

      Node Capacity
---------------------------
|     kube-reserved       |
|-------------------------|
|     system-reserved     |
|-------------------------|
|    eviction-threshold   |
|-------------------------|
|                         |
|      allocatable        |
|   (available for pods)  |
|                         |
|                         |
---------------------------

eviction-threshold實際上是對pod limit_resource的補充,因爲limit_resource只能針對單個pod做資源限制,當這個pod達到限制的閥值後,kubelet便會oom_killer掉這個pod,而eviction-threshold根據事先設定的Eviction Thresholds來觸發Eviction,調用算法篩選出合適的幾個podkill掉一個或多個pod回收資源,被eviction掉的pod會被kube-scheduler在其他節點重新調度起來

eviction-threshold 分爲兩類:

Soft Eviction Thresholds

達到觸發值後,發送信號給 pod, 並不是馬上去驅逐pod,而是等待一個緩衝時間 grace period,

配置 eviction-soft 必須指定 grace period

Hard Eviction Thresholds

達到觸發值後,直接篩選出對應的pod kill

配置

  • --enforce-node-allocatable,默認爲pods,要爲kube組件和System進程預留資源,則需要設置爲pods,kube-reserved,system-reserve
  • --cgroups-per-qosEnabling QoS and Pod level cgroups,默認開啓。開啓後,kubelet將會管理所有workload Podscgroups
  • --cgroup-driver,默認爲cgroupfs,另一可選項爲systemd。取決於容器運行時使用的cgroup driverkubelet與其保持一致。比如你配置docker使用systemd cgroup driver,那麼kubelet也需要配置--cgroup-driver=systemd
  • --kube-reserved,用於配置爲kube組件(kubelet,kube-proxy,dockerd等)預留的資源量,比如--kube-reserved=cpu=1000m,memory=8Gi,ephemeral-storage=16Gi
  • --kube-reserved-cgroup,如果你設置了--kube-reserved,那麼請一定要設置對應的cgroup,並且該cgroup目錄要事先創建好,否則kubelet將不會自動創建導致kubelet啓動失敗。比如設置爲kube-reserved-cgroup=/system.slice/kubelet.service
  • --system-reserved,用於配置爲System進程預留的資源量,比如--system-reserved=cpu=500m,memory=4Gi,ephemeral-storage=4Gi
  • --system-reserved-cgroup,如果你設置了--system-reserved,那麼請一定要設置對應的cgroup,並且該cgroup目錄要事先創建好,否則kubelet將不會自動創建導致kubelet啓動失敗。比如設置爲system-reserved-cgroup=/system.slice
  • --eviction-hard,用來配置kubelethard eviction條件,只支持memoryephemeral-storage兩種不可壓縮資源。當出現MemoryPressure時,Scheduler不會調度新的Best-Effort QoS Pods到此節點。當出現DiskPressure時,Scheduler不會調度任何新Pods到此節點。

配置示例

[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=/var/lib/kubelet
# CPUAccounting=true
# MemoryAccounting=true
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/pids/system.slice/kubelet.service
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/cpu/system.slice/kubelet.service
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/cpuacct/system.slice/kubelet.service
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/cpuset/system.slice/kubelet.service
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/memory/system.slice/kubelet.service
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/systemd/system.slice/kubelet.service
ExecStart=/usr/local/bin/kubelet \
  --cgroup-driver=systemd \
  --enforce-node-allocatable=pods,kube-reserved,system-reserved \
  --kube-reserved-cgroup=/system.slice/kubelet.service \
  --system-reserved-cgroup=/system.slice \
  --kube-reserved=cpu=1000m,memory=2Gi,ephemeral-storage=1Gi \
  --system-reserved=cpu=1000m,memory=2Gi,ephemeral-storage=1Gi \
  --max-pods=100 \
  --eviction-hard=imagefs.available<15%,memory.available<300Mi,nodefs.available<10%,nodefs.inodesFree<3% \
  --eviction-max-pod-grace-period=40 \
  --eviction-minimum-reclaim=memory.available=1Gi,nodefs.available=5Gi,imagefs.available=5Gi \
  --eviction-soft-grace-period=memory.available=30s,nodefs.available=2m,imagefs.available=2m,nodefs.inodesFree=2m \
  --eviction-soft=imagefs.available<20%,memory.available<1Gi,nodefs.available<15%,nodefs.inodesFree<5% \
  --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \
  --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
  --rotate-certificates \
  --cert-dir=/etc/kubernetes/ssl \
  --cluster_dns=10.96.0.2 \
  --cluster_domain=cluster.local. \
  --hairpin-mode=promiscuous-bridge \
  --allow-privileged=true \
  --fail-swap-on=false \
  --serialize-image-pulls=false \
  --logtostderr=true \
  --network-plugin=cni \
  --cni-conf-dir=/etc/cni/net.d \
  --cni-bin-dir=/opt/cni/bin \
  --v=1
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target

注意: 如果設置完資源預留,重啓 kubelet 之後,發現 node 變成 not readykubectl describe node 查看, 報錯爲:

failed to write 6442450944 to memory.limit_in_bytes: write /sys/fs/cgroup/memory/system.slice/memory.limit_in_bytes: device or resource busy

是因爲實際系統內存使用量大於 --system-reserved=cpu=1000m,memory=6Gi 的配置,將 6g 改大之後重啓kubelet即可,當然具體改爲多少,要看節點上實際系統佔用內存(筆者測試機上裝有 ceph, 所以系統部分所需內存較大)

測試

環境

hostname role cpu memory –kube-reserved=cpu –kube-reserved=memory –system-reserved=cpu –system-reserved=memory
k8s1 master 12 47G 1000m 2Gi 1000m 15Gi
k8s2 master 32 31G 1000m 2Gi 1000m 8Gi
k8s3 node 16 62G 1000m 2Gi 1000m 8Gi

describe node

# k8s1
Addresses:
  InternalIP:  10.10.1.223
  Hostname:    k8s1
Capacity:
 cpu:                12
 ephemeral-storage:  574727312Ki
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             49425780Ki # k8s1 實際總內存: 47G
 pods:               110
Allocatable:
 cpu:                10
 ephemeral-storage:  523226238919
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             31292788Ki # k8s1 節點可以分配給 pod 的總內存: 30G
 pods:               110
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests            Limits
  --------           --------            ------
  cpu                8023m (80%)         35643m (356%)
  memory             32389500928 (101%)  75199505664 (234%)
  ephemeral-storage  0 (0%)              0 (0%)
  
# k8s2
Capacity:
 cpu:                32
 ephemeral-storage:  459378800Ki
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             32906916Ki
 pods:               110
Allocatable:
 cpu:                30
 ephemeral-storage:  416921050436
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             22113956Ki
 pods:               110
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests          Limits
  --------           --------          ------
  cpu                8123m (27%)       30203m (100%)
  memory             22098028Ki (99%)  60566922496 (267%)
  ephemeral-storage  0 (0%)            0 (0%)
  
# k8s3
Capacity:
 cpu:                16
 ephemeral-storage:  575257712Ki
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             65916188Ki
 pods:               110
Allocatable:
 cpu:                14
 ephemeral-storage:  523715055558
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             55123228Ki
 pods:               110
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests          Limits
  --------           --------          ------
  cpu                12785m (91%)      71410m (510%)
  memory             54820406Ki (99%)  155630527744 (275%)
  ephemeral-storage  0 (0%)            0 (0%)

k8s1 節點實際總內存 47G,減去 --kube-reserved=memory=2Gi, 再減去 --system-reserved=memory=15Gi, 爲 k8s1 可以分配給 pod 的總內存 30G,並且該 30G 就是節點 resource requests 的上限

可以看到爲 kube 和系統配置的資源預留確實生效了

再看下節點 podrequests

在這裏插入圖片描述

kube-scheduler 根據 pod 設置的 resource requestspod 選取合適的節點,現在 3臺節點上requests 都滿了,故無法再爲新的 pod 調度,這時候新建 pod 會處於 Pending 狀態,describe pod 會顯示

Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 Insufficient memory.

而實際上各節點的 free -h 輸出如下:

# k8s1
root@k8s1:~# free -h
              total        used        free      shared  buff/cache   available
Mem:            47G         25G         14G         13M        7.7G         21G
Swap:            0B          0B          0B

# k8s2
root@k8s2:~# free -h
              total        used        free      shared  buff/cache   available
Mem:            31G         27G        210M         13M        4.2G        5.1G
Swap:            0B          0B          0B

# k8s3
root@k8s3:~# free -h
              total        used        free      shared  buff/cache   available
Mem:            62G         36G         12G        134M         14G         29G
Swap:            0B          0B          0B

會發現實際上,除去 --kube-reserved=memory--system-reserved=memory ,還有可用內存。

所以我們將上圖中那些resource requests設置不合理的podrequests memory 設置小一點,就可以調度新的 pod

所以這裏要清楚,k8s 通過 pod 配置的 resource requests 值來調度 pod 到資源有餘的合適的節點,而節點可用的總資源就是節點實際總資源減去爲 kubesystem 預留的資源

使用 kubectl top pod -A 查看 pod 實際資源使用,這裏面顯示的值就是 resource limit oom killer 依據的值

root@k8s1:/opt/kubespray# kubectl top pod
NAME                      CPU(cores)   MEMORY(bytes)
whoami-5b4bb9c787-m2vdt   0m           3Mi

注意:kubectl top node 顯示的資源值,並不是節點上所有pod所使用資源的總和

參考

從一次集羣雪崩看Kubelet資源預留的正確姿勢

kubernetes集羣節點資源預留

爲系統守護進程預留計算資源

Configure Out of Resource Handling

從kubectl top看K8S監控

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章