作为一个容器集群编排与管理项目,kubernetes为用户提供基础设施能力,包括:
Pod是最小的原子调度单位,所有跟调度和资源管理相关的属性都应该属于Pod对象的字段。这其中最重要的部分就是Pod的CPU和内存配置,如下所示:
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: db
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: wp
image: wordpress
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Pod可以由多个Container组成,所以CPU和内存资源的限额,是要配置在每个Container的定义上。这样Pod整体的资源配置,就由这些Container的配置值累加得到。
kubernetes里为CPU设置的单位是“CPU的个数”,比如,cpu=1指的就是这个Pod的CPU限额是1个CPU,具体的1个CPU在宿主机上如何解释:
kubernets运行将CPU配额设置为分数,比如500m(指的是500millicpu,即0.5个CPU),这样Pod被分配到的就是1个CPU一半的计算能力。
内存资源的单位是bytes,kubernetes支持使用Ei、Pi、Ti、Gi、Mi、Ki(或者E、P、T、G、M、K)的方式来作为bytes的值。如64Mib。
主要Mib(mebibyte)和MB(megabyte)的区别,1Mi=1024×1024,1M=1000×1000
kubernetes中Pod的CPU和内存资源,实际上分为limits和requests两种情况,如下所示:
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
两者的区别:
pod字段 | Cgroups属性值 | 描述 | 默认值 |
---|---|---|---|
requests.cpu=250m | cpu.shares=(250/1000)*1024 | 这样kubernetes就通过cpu.shares完成了对CPU时间的按比例分配 | cpu.shares默认值是1024 |
limits.cpu=500m | cpu.cfs_quota_us=(500/1000)*100ms | kubernetes为容器只分配50%CPU | cpu.cfs_period_us始终为100ms |
limits.memory=128Mi | memory.limit_in_bytes=128×1024×1024 | 在调度的时候,调度器只会使用requets.memory=64Mi来进行判断 |
kubernetes这种对CPU和内存资源限额的设计,实际上参考了Borg论文中对"动态资源边界"的定义。即,容器化作业在提交时所设置的资源边界,并不一定是调度系统所必须严格遵守的,因为在大多数作业使用到的资源其实远小于它所请求的资源限额。基于这种假设,borg在作业被提交后,会主动减小它的资源配额,以便容纳更多的作业、提升资源利用率。当作业资源使用量增加到一定阈值后,通过快速恢复过程,还原作业原始的资源配额,防止出现异常情况。
kubernetes的requests和limits是上述思想的简化版:用户在提交Pod时,可以声明一个相对较小的requests值供调度器使用,而kubernetes真正给容器Cgroups的则是相对较大的limits值。这与borg的思路是相通的。
在kubernetes中,不同的requests和limits的设置方式,会将这个Pod划分到不同的QoS级别中。
当Pod里的每个Container都同时设置了requests和limits,并且requests和limits值相等时,这个Pod就属于Guaranteed类别,如下所示:
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"
# 这个Pod创建之后,它的QoSClass字段被kubernetes自动设置为Guaranteed
需要注意的是,Pod仅设置了limits没有设置requests的时候,kubernetes会自动为它设置与limits相通的requests值,因此也属于Guaranteed类别。
当Pod不属于Guaranteed类别,但是至少有一个Container设置了requests,那么Pod就会被化为Burstable类别,如下所示:
apiVersion: v1
kind: Pod
metadata:
name: qos-demo-2
namespace: qos-example
spec:
containers:
- name: qos-demo-2-ctr
image: nginx
resources:
limits
memory: "200Mi"
requests:
memory: "100Mi"
如果一个Pod既没有设置requests也没有设置limits,那就属于BestEffort类别,如下所示:
apiVersion: v1
kind: Pod
metadata:
name: qos-demo-3
namespace: qos-example
spec:
containers:
- name: qos-demo-3-ctr
image: nginx
QoS的划分主要的应用场景是,当宿主机资源紧张的时候,kubelet对Pod进行Eviction(即资源回收)时候需要用到。
当kubernetes所管理的宿主机上不可压缩资源短缺时,就有可能触发Eviction。如可用内存(memory.available
)、可用宿主机磁盘空间(nodefs.available
),以及容器运行时镜像宿主机空间(imagefs.available
)等。
目前,kubernetes设置的Eviction的默认阈值如下:
memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%
# 上述各个触发条件在kubelet里都是可配置的
kubelet --eviction-hard=imagefs.available<10%,memory.available<500Mi, \
nodefs.available<5%,nodefs.inodesFree<5% \
--eviction-soft=imagefs.available<30%,nodefs.available<10% \
--eviction-soft-grace-period=imagefs.available=2m,nodefs.available=2m \
--eviction-max-pod-grace-period=600
# 在这个配置中,可用看到Eviction在kubernetes里其实分为soft和hard两种模式
imagefs.available=2m
就意味着imagefs不足的阈值达到2分钟之后,kubelet才会开始Eviction的过程kubernetes计算Eviction阈值的数据来源,主要依赖于从Cgroups读取的值,以及使用cAdvisor监控到的数据。
当宿主机上的Eviction阈值达到后,就会进入MemoryPressure或者DiskPressure状态(此时给node打上污点),从而避免新的Pod被调度到这台宿主机上。
而当Eviction发生的时候,kubelet具体会挑选哪些Pod进行删除操作,就需要参考这些Pod的QoS类别:
对于同QoS类别的Pod,kubernetes会根据Pod的优先级进行排序和选择。
在使用容器的时候,通过设置cpuset把容器绑定到某个CPU的内核上,而不是像cpuset那样共享CPU计算能力。这种情况下,由于操作系统在CPU之间进行上下文切换的次数大大减少,容器里应用的性能会大幅提升。事实上,cpuset方式是生产环境中部署在线类型(long running task)的Pod时,非常有用的一种方式。
如何在kubernetes中实现这样的操作?
如下例子所示:
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "200Mi"
cpu: "2"
这时候,该Pod就会被绑定在2个独占的CPU核上,具体是哪两个CPU核由kubelet分配。
DaemonSet的Pod都设置为Guaranteed的QoS类型,否则一旦DaemonSet的Pod被回收,它又会立即在原宿主机上被重建出来,这就使得前面资源回收的动作,完全没有意义了。