接下来,需要为这两个工作组分别定义一个Context,即运行环境。这个运行环境将属于某个特定的命名空间通过kubectlconfigset-context命令定义Context,并将Context置于之前创建的命名空间中
使用kubectlconfiguse-context
1.详解Requests和Limits参数.以CPU为例,图10.3显示了未设置Limits和设置了Requests、Limits的CPU使用率的区别
尽管Requests和Limits只能被设置到容器上,但是设置Pod级别的Requests和Limits能大大提高管理Pod的便利性和灵活性,因此在Kubernetes中提供了对Pod级别的Requests和Limits的配置对于CPU和内存而言,Pod的Requests或Limits是指该Pod中所有容器的Requests或Limits的总和(对于Pod中没有设置Requests或Limits的容器,该项的值被当作0或者按照集群配置的默认值来计算)。下面对CPU和内存这两种计算资源的特点进行说明
基于Requests和Limits的Pod调度机制当一个Pod创建成功时,Kubernetes调度器(Scheduler)会为该Pod选择一个节点来执行。对于每种计算资源(CPU和Memory)而言,每个节点都有一个能用于运行Pod的最大容量值。调度器在调度时,首先要确保调度后该节点上所有Pod的CPU和内存的Requests总和,不超过该节点能提供给Pod使用的CPU和Memory的最大容量值这里需要注意:可能某节点上的实际资源使用量非常低,但是已运行Pod配置的Requests值的总和非常高,再加上需要调度的Pod的Requests值,会超过该节点提供给Pod的资源容量上限,这时Kubernetes仍然不会将Pod调度到该节点上。如果Kubernetes将Pod调度到该节点上,之后该节点上运行的Pod又面临服务峰值等情况,就可能导致Pod资源短缺。
kubelet在启动Pod的某个容器时,会将容器的Requests和Limits值转化为相应的容器启动参数传递给容器执行器(Docker或者rkt)如果容器的执行环境是Docker,那么容器的如下4个参数是这样传递给Docker的1)spec.container[].resources.requests.cpu这个参数会转化为core数(比如配置的100m会转化为0.1),然后乘以1024,再将这个结果作为--cpu-shares参数的值传递给dockerrun命令。在dockerrun命令中,--cpu-share参数是一个相对权重值(RelativeWeight),这个相对权重值会决定Docker在资源竞争时分配给容器的资源比例这里需要区分清楚的是:这个参数对于Kubernetes而言是绝对值,主要用于Kubernetes调度和管理;同时Kubernetes会将这个参数的值传递给dockerrun的--cpu-shares参数。--cpu-shares参数对于Docker而言是相对值,主要用于资源分配比例
注意:如果kubelet的启动参数--cpu-cfs-quota被设置为true,那么kubelet会强制要求所有Pod都必须配置CPULimits(如果Pod没有配置,则集群提供了默认配置也可以)。从Kubernetes1.2版本开始,这个--cpu-cfs-quota启动参数的默认值就是true
在Linux平台下,对于那些需要大量内存(1GB以上内存)的程序来说,大内存页的优势是很明显的,因为HugePage大大提升了TLB的缓存命中率,又因为Linux对HugePage提供了更为简单、便捷的操作接口,所以可以把它当作文件来进行读写操作。Linux使用HugePage文件系统hugetlbfs支持巨页,这种方式更为灵活,我们可以设置HugePage的大小,比如1GB、2GB甚至2.5GB,然后设置有多少物理内存用于分配HugePage,这样就设置了一些预先分配好的HugePage。可以将hugetlbfs文件系统挂载在/mnt/huge目录下,通过执行下面的指令完成设置
mkdir/mnt/hugemount-thugetlbfsnodev/mnt/huge在设置完成后,用户进程就可以使用mmap映射HugePage目标文件来使用大内存页了,IntelDPDK便采用了这种做法,测试表明应用使用大内存页比使用4KB的内存页性能提高了10%~15%
Kubernetes1.14版本对LinuxHugePage的支持正式更新为GA稳定版。我们可以将HugePage理解为一种特殊的计算资源:拥有大内存页的资源。而拥有HugePage资源的Node也与拥有GPU资源的Node一样,属于一种新的可调度资源节点(SchedulableResourceNode)HugePage也支持ResourceQuota来实现配额限制,类似CPU或者Memory,但不同于CPU或者内存,HugePage资源属于不可超限使用的资源,拥有HugePage能力的Node会将自身支持的HugePage的能力信息自动上报给KubernetesMaster
Kubernetes中Pod的Requests和Limits资源配置有如下特点(1)如果Pod配置的Requests值等于Limits值,那么该Pod可以获得的资源是完全可靠的。(2)如果Pod的Requests值小于Limits值,那么该Pod获得的资源可分成两部分:◎完全可靠的资源,资源量的大小等于Requests值;◎不可靠的资源,资源量最大等于Limits与Requests的差额,这份不可靠的资源能够申请到多少,取决于当时主机上容器可用资源的余量通过这种机制,Kubernetes可以实现节点资源的超售(OverSubscription),比如在CPU完全充足的情况下,某机器共有32GiB内存可提供给容器使用,容器配置为Requests值1GiB,Limits值为2GiB,那么在该机器上最多可以同时运行32个容器,每个容器最多可以使用2GiB内存,如果这些容器的内存使用峰值能错开,那么所有容器都可以正常运行超售机制能有效提高资源的利用率,同时不会影响容器申请的完全可靠资源的可靠性
2)不可压缩资源◎Kubernetes目前支持的不可压缩资源是内存◎Pod可以得到在Requests中配置的内存。如果Pod使用的内存量小于它的Requests的配置,那么这个Pod可以正常运行(除非出现操作系统级别的内存不足等严重问题);如果Pod使用的内存量超过了它的Requests的配置,那么这个Pod有可能被Kubernetes杀掉:比如PodA使用了超过Requests而不到Limits的内存量,此时同一机器上另外一个PodB之前只使用了远少于自己的Requests值的内存,此时程序压力增大,PodB向系统申请的总量不超过自己的Requests值的内存,那么Kubernetes可能会直接杀掉PodA;另外一种情况是PodA使用了超过Requests而不到Limits的内存量,此时Kubernetes将一个新的Pod调度到这台机器上,新的Pod需要使用内存,而只有PodA使用了超过了自己的Requests值的内存,那么Kubernetes也可能会杀掉PodA来释放内存资源◎如果Pod使用的内存量超过了它的Limits设置,那么操作系统内核会杀掉Pod所有容器的所有进程中使用内存最多的一个,直到内存不超过Limits为止。
对调度策略的影响Kubernetes的kubelet通过计算Pod中所有容器的Requests的总和来决定对Pod的调度不管是CPU还是内存,Kubernetes调度器和kubelet都会确保节点上所有Pod的Requests的总和不会超过在该节点上可分配给容器使用的资源容量上限服务质量等级(QoSClasses)在一个超用(OverCommitted,容器Limits总和大于系统容量上限)系统中,由于容器负载的波动可能导致操作系统的资源不足,最终可能导致部分容器被杀掉。在这种情况下,我们当然会希望优先杀掉那些不太重要的容器,那么如何衡量重要程度呢?Kubernetes将容器划分成3个QoS等级:Guaranteed(完全可靠的)、Burstable(弹性波动、较可靠的)和BestEffort(尽力而为、不太可靠的),这三种优先级依次递减
2)存储资源配额(VolumeCountQuota)可以在给定的命名空间中限制所使用的存储资源(StorageResources)的总量,目前支持的存储资源名称如表10.3所示
3)对象数量配额(ObjectCountQuota)指定类型的对象数量可以被限制。表10.4列出了ResourceQuota支持限制的对象类型
例如,我们可以通过资源配额来限制在命名空间中能创建的Pod的最大数量。这种设置可以防止某些用户大量创建Pod而迅速耗尽整个集群的PodIP和计算资源2.配额的作用域(QuotaScopes)每项资源配额都可以单独配置一组作用域,配置了作用域的资源配额只会对符合其作用域的资源使用情况进行计量和限制,作用域范围内超过了资源配额的请求都会报验证错误。表10.5列出了ResourceQuota的4种作用域
其中,BestEffort作用域可以限定资源配额来追踪pods资源的使用,Terminating、NotTerminating和NotBestEffort这三种作用域可以限定资源配额来追踪以下资源的使用◎cpu◎limits.cpu◎limits.memory◎memory◎pods◎requests.cpu◎requests.memory3.在资源配额(ResourceQuota)中设置Requests和Limits如果在资源配额中指定了requests.cpu或requests.memory,那么它会强制要求每个容器都配置自己的CPURequests或CPULimits(可使用LimitRange提供的默认值)同理,如果在资源配额中指定了limits.cpu或limits.memory,那么它也会强制要求每个容器都配置自己的内存Requests或内存Limits(可使用LimitRange提供的默认值)4.资源配额的定义
apiVersion:v1kind:ResourceQuotametadata:name:compute-resourcesnamespace:testspec:hard:pods:"4"requests.cpu:"1"requests.memory:1Gilimits.cpu:"2"limits.memory:2GiapiVersion:v1kind:ResourceQuotametadata:name:object-countsnamespace:testspec:hard:configmaps:"2"persistentvolumeclaims:"4"replicationcontrollers:"2"secrets:"10"services:"4"services.loadbalancers:"2"资源配额与集群资源总量的关系资源配额与集群资源总量是完全独立的。资源配额是通过绝对的单位来配置的,这也就意味着如果在集群中新添加了节点,那么资源配额不会自动更新,而该资源配额所对应的命名空间中的对象也不能自动增加资源上限在某些情况下,我们可能希望资源配额支持更复杂的策略,如下所述◎对于不同的租户,按照比例划分整个集群的资源◎允许每个租户都能按照需要来提高资源用量,但是有一个较宽容的限制,以防止意外的资源耗尽情况发生◎探测某个命名空间的需求,添加物理节点并扩大资源配额值
memory.available的值取自cgroupfs,而不是free-m命令,这是因为free-m不支持在容器内工作。如果用户使用了nodeallocatable功能,则除了节点自身的内存需要判断,还需要利用cgroup根据用户Pod部分的情况进行判断。下面的脚本展示了kubelet计算memory.available的过程
kubelet假设inactive_file(不活跃LRU列表中的file-backed内存,以字节为单位)在紧缺情况下可以回收,因此对其进行了排除kubelet支持以下两种文件系统(1)nodefs:保存kubelet的卷和守护进程日志等(2)imagefs:在容器运行时保存镜像及可写入层驱逐阈值kubelet可以定义驱逐阈值,一旦超出阈值,就会触发kubelet进行资源回收操作阈值的定义方式为
--eviction-hardmapStringStringAsetofevictionthresholds(e.g.memory.available<1Gi)thatifmetwouldtriggerapodeviction.(defaultimagefs.available<15%,memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%)
节点的状况kubelet会将一个或多个驱逐信号与节点的状况对应起来无论触发了硬阈值还是软阈值,kubelet都会认为当前节点的压力太大,如表10.7所示为节点状况与驱逐信号的对应关系
回收Node级别的资源如果达到了驱逐阈值,并且也过了宽限期,kubelet就会回收超出限量的资源,直到驱逐信号量回到阈值以内kubelet在驱逐用户Pod之前,会尝试回收Node级别的资源。在观测到磁盘压力的情况下,基于服务器是否为容器运行时定义了独立的imagefs,会导致不同的资源回收过程1.有Imagefs的情况(1)如果nodefs文件系统达到了驱逐阈值,则kubelet会删掉死掉的Pod、容器来清理空间。(2)如果imagefs文件系统达到了驱逐阈值,则kubelet会删掉所有无用的镜像来清理空间2.没有Imagefs的情况如果nodefs文件系统达到了驱逐阈值,则kubelet会按照下面的顺序来清理空间。(1)删除死掉的Pod、容器。(2)删除所有无用的镜像。
◎当memory.available超过阈值触发了驱逐操作时,kubelet会启动资源回收,并保证memory.available至少有500MiB。◎如果是nodefs.available超过阈值并触发了驱逐操作,则kubelet会恢复nodefs.available到至少1.5GiB。◎对于imagefs.available超过阈值并触发了驱逐操作的情况,kubelet会保证imagefs.available恢复到最少102GiB。
1.调度器的行为在节点资源紧缺的情况下,节点会向Master报告这一状况。在Master上运行的调度器(Scheduler)以此为信号,不再继续向该节点调度新的Pod。如表10.8所示为节点状况与调度行为的对应关系
2.Node的OOM行为如果节点在kubelet能够回收内存之前遭遇了系统的OOM(内存不足),节点则依赖oom_killer的设置进行响应(OOM评分系统详见10.4节的描述)kubelet根据Pod的QoS为每个容器都设置了一个oom_score_adj值,如表10.9所示
如果kubelet无法在系统OOM之前回收足够的内存,则oom_killer会根据内存使用比率来计算oom_score,将得出的结果和oom_score_adj相加,得分最高的Pod首先被驱逐这个策略的思路是,QoS最低且相对于调度的Request来说消耗最多内存的Pod会首先被驱逐,来保障内存的回收与Pod驱逐不同,如果一个Pod的容器被OOM杀掉,则是可能被kubelet根据RestartPolicy重启的3.对DaemonSet类型的Pod驱逐的考虑通过DaemonSet创建的Pod具有在节点上自动重启的特性,因此我们不希望kubelet驱逐这种Pod;然而kubelet目前并没有能力分辨DaemonSet的Pod,所以无法单独为其制定驱逐策略,所以强烈建议不要在DaemonSet中创建BestEffort类型的Pod,避免产生驱逐方面的问题