[TOC]
kubelet中有几个参数,通过这几个参数可以为系统进程预留资源,不至于pod把计算资源耗尽,而导致系统操作都无法正常进行。
```
--enforce-node-allocatable
--system-reserved
--system-reserved-cgroup
--kube-reserved
--kube-reserved-cgroup
--eviction-hard
```
### **Allocatable**
在kubernetes 1.6版本后,引入了Node的Allocatable特性,通过该特性我们可以控制每个节点可分配的资源。
借用官网的图如下:
```
capacity
-----------------------------
| kube-reserved |
------------------------------
| system-reserved |
------------------------------
| eviction-threshhold |
------------------------------
| allocatable |
| (available for pods) |
------------------------------
```
Capacity是指Node的容量,allocatable的值为
```
allocatable = capacity - kube_reserved - system_reserved - eviction_threshhold
```
当kubelet启动后,Node的allocatable就是固定的,不会因为pod的创建与销毁而改变。
* **allocatable vs requests vs limits**
在pod的yaml文件中,我们可以为pod设置requests与limits。其中limits与allocatable没有什么关系。但requests与allocatable关系紧密。
**调度到某个节点上的Pod的requests总和不能超过该节点的allocatable。limits的总和没有上限。**
比如某个节点的内存的allocatable为10Gi,有三个Pod(requests.memory=3Gi)已经调度到该节点上,那么第4个Pod就无法调度到该节点上,即使该Node上的空闲内存大于3Gi。
### **资源预留 - 不设cgroup**
假设我们现在需要为系统预留一定的资源,那么我们可以配置如下的kubelet参数(在这里我们不设置对应的cgroup参数):
```
--enforce-node-allocatable=pods
--kube-reserved=memory=...
--system-reserved=memory=...
--eviction-hard=...
```
在上面提到,节点上Pod的requests总和不能超过allocatable。
当我们设置了以上的四个参数时,节点上所有Pod实际使用的资源总和不会超过`capacity - kube_reserved - system_reserved`
我们可以通过实验进行验证。
1、参数设置
kubelet的启动参数如下:
> /usr/bin/kubelet --address=0.0.0.0 --allow-privileged=true --cluster-dns=10.254.0.10 --cluster-domain=kube.local --fail-swap-on=true --hostname-override=192.168.1.101 --kubeconfig=/etc/kubernetes/kubeconfig --pod-infra-container-image=10.142.232.115:8021/library/pause:latest --port=10250 --enforce-node-allocatable=pods --kube-reserved=memory=1Gi --system-reserved=memory=1Gi --cgroup-driver=cgroupfs --eviction-hard=memory.available<100Mi
2、查看capacity及allocatable
查看到Node的capacity及allocatable的值如下:
```
Capacity:
cpu: 2
memory: 4016436Ki (约3.83Gi)
pods: 110
Allocatable:
cpu: 2
memory: 1816884Ki (约1.73Gi)
pods: 110
```
我们可以计算出allocatable的值,刚好与上面的一致:
```
allocatale = capacity - kube_reserved - system_reserved - eviction_hard
1816884Ki = 4016436Ki - 1*1024*1024Ki - 1*1024*1024Ki - 100*1024Ki
```
我们可以通过free命令来查看Node的total值,与capacity一致:
```
$ free -k
total used free shared buff/cache available
Mem: 4016436 1224372 2234872 17100 557192 2453156
Swap: 0 0 0
```
3、查看kubepods控制组
查看kubepods控制组中对内存的限制,该值决定了Node上所有的Pod能使用的资源上限:
```
$ cat /sys/fs/cgroup/memory/kubepods/memory.limit_in_bytes
1965346816
```
1965346816 Bytes = 1919284Ki = allocatable + 100Mi
根据上面的计算可知,Node上Pod能实际使用的资源上限值为:
```
kubepods/memory.limit_in_bytes = capacity - kube_reserved - system_reserved
```
**注意:根据上面的公式,我们可以知道,一个节点上所有Pod能使用的内存总和,与eviction-hard无关**
4、查看内存的空闲情况
查看内存的使用情况,发现空闲内存为 2.3Gi
```
$ free -h
total used free shared buff/cache available
Mem: 3.8G 1.2G 2.1G 16M 544M 2.3G
Swap: 0B 0B 0B
```
5、创建pod
此时内存的空闲值为2.3Gi,allocatable为1.73Gi,kubepod.limit为1.83Gi。
我们创建一个Pod,pod.request为0.1Gi,pod.limit为20Gi,Pod实际消耗内存1Gi。理论上该Pod能创建成功,实际也成功了,如下:
备注:yaml文件消耗内存的脚本见本文附录
```
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
centos-659755bf78-jdlrc 1/1 Running 0 44s
```
查看Node的内存使用情况:
```
$ free -h
total used free shared buff/cache available
Mem: 3.8G 2.2G 1.1G 16M 546M 1.3G
Swap: 0B 0B 0B
```
此时,空闲内存为1.3Gi,Node剩余的request为1.63Gi,Node的kubepods.limit还剩0.83Gi。
我们再创建一个同样的Pod,根据推测,Pod可以调度成功,但是由于要消耗1Gi的实际内存,超过了0.83Gi,那么该Pod会出现OOM。实验结果也的确如此:
```
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
centos-659755bf78-j8wjv 0/1 OOMKilled 0 5s
centos-659755bf78-jdlrc 1/1 Running 1 1m
```
### **资源预留 - 设置对应的cgroup**
如果还设置了对应的 --system-reserved-cgroup 和 --kube-reserved-cgroup参数,Pod能实际使用的资源上限不会改变(即kubepods.limit_in_bytes不变),但系统进程与kube进程也会受到资源上限的限制。如果系统进程超过了预留资源,那么系统进程会被cgroup杀掉。
但是如果不设这两个参数,那么系统进程可以使用超过预留的资源上限。
### **配置建议**
为kubelet设置以下四个参数即可:
```
--enforce-node-allocatable=pods
--kube-reserved=cpu=xx,memory=xx,ephemeral-storage=xx
--system-reserved=cpu=xx,memory=xx,ephemeral-storage=xx
--eviction-hard=memory.available<10%,nodefs.available<10%
```
一般来说,我们不希望资源的使用率超过70%,所以kube-reserved、system-reserved、eviction-hard都应该设为10%。但由于kube-reserved与system-reserved不能设置百分比,所以它们要设置为绝对值。
### **总结**
* Node的allocatable在kubelet启动后是一个固定的值,不会因为pod的创建与删除而改变
* 当我们为Pod设置了resources.requests时,调度到Node上的Pod的resources.requests的总和不会超过Node的allocatable。但Pod的resources.limits总和可以超过Node的allocatable
* 一个Pod能否成功调度到某个Node,关键看该Pod的resources.request是否小于Node剩下的request,而不是看Node实际的资源空闲量。即使空闲资源小于Pod的requests,Pod也可以调度到该Node上
* 当Pod的内存资源实际使用量超过其limits时,docker(实际是cgroup)会把该Pod内超出限额的进程杀掉(OOM);如果CPU超过,不会杀掉进程,只是进程会一直等待CPU。
* 当我们只设置如下四个参数时,可以达到为系统预留资源的效果,即Pod的资源实际使用量不会超过allocatable的值(因为kubepods控制组中memory.limit_in_bytes的值就为allocatable的值)。即使系统本身没有使用完预留的那部分资源,Pod也无法使用。当系统超出了预留的那部分资源时,系统进程可以抢占allocatable中的资源,即对系统使用的资源没有限制。
```
--enforce-node-allocatable=pods
--kube-reserved=memory=...
--system-reserved=memory=...
--eviction-hard=...
```
* 当我们除了设置了以上四个参数,还设置了对应的cgroup时(如下),那么除了Pod使用的资源上限不会超过allocatable外,系统使用的资源上限也不会超过预留资源。当系统进程超过预留资源时,系统进程也会被cgroup杀掉。所以推荐使用上面的设置方法
```
--enforce-node-allocatable=pods,kube-reserved,system-reserved
--kube-reserved=memory=...
--kube-reserved-cgroup=...
--system-reserved=memory=...
--system-reserved-cgroup=...
--eviction-hard=...
```
* allocatable与kubepods.limit的值不一样,它们之间相差一个 eviction_hard
```
allocatable = capacity - kube_reserved - system_reserved - eviction_hard
kubepods.limit = capacity - kube_reserved - system_reserved
```
### **附录**
* centos.yaml
```
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: centos
namespace: default
spec:
replicas: 1
selector:
matchLabels:
name: centos
template:
metadata:
labels:
name: centos
spec:
volumes:
- name: volume1
hostPath:
path: /home/docker/yaml/mem.py
containers:
- name: centos
image: 10.142.232.115:8021/library/centos:centos7
command:
- python
- /mem.py
- 1GB
volumeMounts:
- mountPath: /mem.py
name: volume1
resources:
requests:
memory: 0.1Gi
limits:
memory: 20Gi
```
* mem.py
```
import sys
import re
import time
def print_help():
print 'Usage: '
print ' python mem.py 100MB'
print ' python mem.py 1GB'
if __name__ == "__main__":
if len(sys.argv) == 2:
pattern = re.compile('^(\d*)([M|G]B)$')
match = pattern.match(sys.argv[1].upper())
if match:
num = int(match.group(1))
unit = match.group(2)
if unit == 'MB':
s = ' ' * (num * 1024 * 1024)
else:
s = ' ' * (num * 1024 * 1024 * 1024)
time.sleep(10000)
else:
print_help()
else:
print_help()
```
### **参考**
* https://kubernetes.io/zh/docs/tasks/administer-cluster/out-of-resource/
- 常用命令
- 安装
- 安装Kubeadm
- 安装单Master集群
- 安装高可用集群(手动分发证书)
- 安装高可用集群(自动分发证书)
- 启动参数解析
- certificate-key
- ETCD相关参数
- Kubernetes端口汇总
- 安装IPv4-IPv6双栈集群
- 下载二进制文件
- 使用Kata容器
- 快速安装shell脚本
- 存储
- 实践
- Ceph-RBD实践
- CephFS实践
- 对象存储
- 阿里云CSI
- CSI
- 安全
- 认证与授权
- 认证
- 认证-实践
- 授权
- ServiceAccount
- NodeAuthorizor
- TLS bootstrapping
- Kubelet的认证
- 准入控制
- 准入控制示例
- Pod安全上下文
- Selinux-Seccomp-Capabilities
- 给容器配置安全上下文
- PodSecurityPolicy
- K8S-1.8手动开启认证与授权
- Helm
- Helm命令
- Chart
- 快速入门
- 内置对象
- 模板函数与管道
- 模板函数列表
- 流程控制
- Chart依赖
- Repository
- 开源的Chart包
- CRD
- CRD入门
- 工作负载
- Pod
- Pod的重启策略
- Container
- 探针
- 工作负载的状态
- 有状态服务
- 网络插件
- Multus
- Calico+Flannel
- 容器网络限速
- 自研网络插件
- 设计文档
- Cilium
- 安装Cilium
- Calico
- Calico-FAQ
- IPAM
- Whereabouts
- 控制平面与Pod网络分开
- 重新编译
- 编译kubeadm
- 编译kubeadm-1.23
- 资源预留
- 资源预留简介
- imagefs与nodefs
- 资源预留 vs 驱逐 vs OOM
- 负载均衡
- 灰度与蓝绿
- Ingress的TLS
- 多个NginxIngressController实例
- Service的会话亲和
- CNI实践
- CNI规范
- 使用cnitool模拟调用
- CNI快速入门
- 性能测试
- 性能测试简介
- 制作kubemark镜像
- 使用clusterloader2进行性能测试
- 编译clusterloader2二进制文件
- 搭建性能测试环境
- 运行density测试
- 运行load测试
- 参数调优
- Measurement
- TestMetrics
- EtcdMetrics
- SLOMeasurement
- PrometheusMeasurement
- APIResponsivenessPrometheus
- PodStartupLatency
- FAQ
- 调度
- 亲和性与反亲和性
- GPU
- HPA
- 命名规范
- 可信云认证
- 磁盘限速
- Virtual-kubelet
- VK思路整理
- Kubebuilder
- FAQ
- 阿里云日志服务SLS