問題
默認kubelet
沒有配置資源預留,host
上所有的資源(cpu
, 內存, 磁盤) 都是可以給 pod
使用的。而當一個節點上的 pod
將資源喫滿時,系統層面可能會幹掉 k8s
核心組件進程, 從而導致改節點 not ready
,此時 k8s
會將改節點的所有 pod
調度到其他節點重建,如果其他節點資源也不夠,那麼其他節點也會 not ready
,進而引起集羣雪崩效應。
如何避免
通過爲 k8s
設置 kube
組件資源預留和 system
系統資源預留,保證節點的 pod
不會喫滿節點資源
目前支持cpu
, memory
, ephemeral-storage
三種資源預留。
cpu
:cpu
是配置cpu shares
,實際上對應的是cpu
的優先級,簡單來說,這個在cpu
繁忙時,它能有更高優先級獲取更多cpu
資源。memory
:k8s
默認不使用swap
,這裏指的就是實際的內存ephemeral-storage
是kubernetes1.8
開始引入的一個資源限制的對象,kubernetes 1.10
版本中kubelet
默認已經打開的了,到目前1.11
還是beta
階段,主要是用於對本地臨時存儲使用空間大小的限制,如對pod
的empty dir
、/var/lib/kubelet
、日誌、容器可讀寫層的使用大小的限制。
Node Capacity
是Node
的所有硬件資源,kube-reserved
是給kube
組件預留的資源,system-reserved
是給System
進程預留的資源, eviction-threshold
是kubelet eviction
的閾值設定,allocatable
纔是真正scheduler
調度Pod
時的參考值(保證Node
上所有Pods
的request 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
,調用算法篩選出合適的幾個pod
,kill
掉一個或多個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-qos
,Enabling QoS and Pod level cgroups
,默認開啓。開啓後,kubelet
將會管理所有workload Pods
的cgroups
。--cgroup-driver
,默認爲cgroupfs
,另一可選項爲systemd
。取決於容器運行時使用的cgroup driver
,kubelet
與其保持一致。比如你配置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
,用來配置kubelet
的hard eviction
條件,只支持memory
和ephemeral-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 ready
,kubectl 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
和系統配置的資源預留確實生效了
再看下節點 pod
的requests
而 kube-scheduler
根據 pod
設置的 resource requests
爲 pod
選取合適的節點,現在 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
設置不合理的pod
的requests memory
設置小一點,就可以調度新的 pod
了
所以這裏要清楚,k8s
通過 pod
配置的 resource requests
值來調度 pod
到資源有餘的合適的節點,而節點可用的總資源就是節點實際總資源減去爲 kube
和 system
預留的資源
使用 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
所使用資源的總和