问题
默认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
所使用资源的总和