Kubernetes系列之Kubernetes资源管理php
Kubernetes 从建立之初的核心模块之一就是资源调度。想要在生产环境使用好 Kubernetes,必须对它的资源模型以及资源管理很是了解。这篇文章算是对散布在网络上的 Kubernetes 资源管理内容的一个总结。干货文章,强列推荐一读。node
在 Kubernetes 中,有两个基础可是很是重要的概念:Node 和 Pod。Node 翻译成节点,是对集群资源的抽象;Pod 是对容器的封装,是应用运行的实体。Node 提供资源,而 Pod 使用资源,这里的资源分为计算(CPU、Memory、GPU)、存储(Disk、SSD)、网络(Network Bandwidth、IP、Ports)。这些资源提供了应用运行的基础,正确理解这些资源以及集群调度如何使用这些资源,对于大规模的 Kubernetes 集群来讲相当重要,不只能保证应用的稳定性,也能够提升资源的利用率。git
在这篇文章,咱们主要介绍 CPU 和内存这两个重要的资源,它们虽然都属于计算资源,但也有所差距。CPU 可分配的是使用时间,也就是操做系统管理的时间片,每一个进程在必定的时间片里运行本身的任务(另一种方式是绑核,也就是把 CPU 彻底分配给某个 Pod 使用,但这种方式不够灵活会形成严重的资源浪费,Kubernetes 中并无提供);而对于内存,系统提供的是内存大小。github
CPU 的使用时间是可压缩的,换句话说它自己无状态,申请资源很快,也能快速正常回收;而内存大小是不可压缩的,由于它是有状态的(内存里面保存的数据),申请资源很慢(须要计算和分配内存块的空间),而且回收可能失败(被占用的内存通常不可回收)。算法
把资源分红可压缩和不可压缩,是由于在资源不足的时候,它们的表现很不同。对于不可压缩资源,若是资源不足,也就没法继续申请资源(内存用完就是用完了),而且会致使 Pod 的运行产生没法预测的错误(应用申请内存失败会致使一系列问题);而对于可压缩资源,好比 CPU 时间片,即便 Pod 使用的 CPU 资源不少,CPU 使用也能够按照权重分配给全部 Pod 使用,虽然每一个人使用的时间片减小,但不会影响程序的逻辑。docker
在 Kubernetes 集群管理中,有一个很是核心的功能:就是为 Pod 选择一个主机运行。调度必须知足必定的条件,其中最基本的是主机上要有足够的资源给 Pod 使用。数据库
资源除了和调度相关以外,还和不少事情紧密相api
####Kubernetes 资源的表示
用户在 Pod 中能够配置要使用的资源总量,Kubernetes 根据配置的资源数进行调度和运行。目前主要能够配置的资源是 CPU 和 Memory,对应的配置字段是 spec.containers[].resource.limits/request.cpu/memory。服务器
须要注意的是,用户是对每一个容器配置 Request 值,全部容器的资源请求之和就是 Pod 的资源请求总量,而咱们通常会说 Pod 的 资源请求 和 Limits。网络
Limits 和 Requests 的区别咱们下面会提到,这里先说说比较容易理解的 CPU 和 Memory。
CPU 通常用核数来标识,一核 CPU 相对于物理服务器的一个超线程核,也就是操做系统 /proc/cpuinfo 中列出来的核数。由于对资源进行了池化和虚拟化,所以 Kubernetes 容许配置非整数个的核数,好比 0.5 是合法的,它标识应用可使用半个 CPU 核的计算量。CPU 的请求有两种方式,一种是刚提到的 0.5,1 这种直接用数字标识 CPU 核心数;另一种表示是 500m,它等价于 0.5,也就是说 1 Core = 1000m。
内存比较容易理解,是经过字节大小指定的。若是直接一个数字,后面没有任何单位,表示这么多字节的内存;数字后面还能够跟着单位, 支持的单位有 E、P、T、G、M、K,前者分别是后者的 1000 倍大小的关系,此外还支持 Ei、Pi、Ti、Gi、Mi、Ki,其对应的倍数关系是 2^10 = 1024。好比要使用 100M 内存的话,直接写成 100Mi便可。
理想状况下,咱们但愿节点上全部的资源均可以分配给 Pod 使用,但实际上节点上除了运行 Pods 以外,还会运行其余的不少进程:系统相关的进程(好比 SSHD、Udev等),以及 Kubernetes 集群的组件(Kubelet、Docker等)。咱们在分配资源的时候,须要给这些进程预留一些资源,剩下的才能给 Pod 使用。预留的资源能够经过下面的参数控制:
--kube-reserved=[cpu=100m][,][memory=100Mi][,][ephemeral-storage=1Gi]
:控制预留给 Kubernetes 集群组件的 CPU、Memory 和存储资源。--system-reserved=[cpu=100mi][,][memory=100Mi][,][ephemeral-storage=1Gi]
:预留给系统的 CPU、Memory 和存储资源。这两块预留以后的资源才是 Pod 真正能使用的,不过考虑到 Eviction 机制(下面的章节会提到),Kubelet 会保证节点上的资源使用率不会真正到 100%,所以 Pod 的实际可以使用资源会稍微再少一点。主机上的资源逻辑分配图以下所示:
NOTE:须要注意的是,Allocatable 不是指当前机器上能够分配的资源,而是指能分配给 Pod 使用的资源总量,一旦 Kubelet 启动这个值是不会变化的。
Allocatable 的值能够在 Node 对象的 status 字段中读取,好比下面这样:
status: allocatable: cpu: "2" ephemeral-storage: "35730597829" hugepages-2Mi: "0" memory: 3779348Ki Pods: "110" capacity: cpu: "2" ephemeral-storage: 38770180Ki hugepages-2Mi: "0" memory: 3881748Ki Pods: "110"
在这部分,咱们来介绍 Kubernetes 中提供的让咱们管理 Pod 资源的原生对象。
前面说过用户在建立 Pod 的时候,能够指定每一个容器的 Requests 和 Limits 两个字段,下面是一个实例:
resources: requests: #这块设定的值是必需要分配这么多,一开始就划分出来了 memory: "64Mi" cpu: "250m" limits: #这块设置的值能够不一开始就分配,等须要的时候在分配 memory: "128Mi" cpu: "500m"
Requests 是容器请求要使用的资源,Kubernetes 会保证 Pod 能使用到这么多的资源。请求的资源是调度的依据,只有当节点上的可用资源大于 Pod 请求的各类资源时,调度器才会把 Pod 调度到该节点上(若是 CPU 资源足够,内存资源不足,调度器也不会选择该节点)。
须要注意的是,调度器只关心节点上可分配的资源,以及节点上全部 Pods 请求的资源,而不关心节点资源的实际使用状况,换句话说,若是节点上的 Pods 申请的资源已经把节点上的资源用满,即便它们的使用率很是低,好比说 CPU 和内存使用率都低于 10%,调度器也不会继续调度 Pod 上去。
Limits 是 Pod 能使用的资源上限,是实际配置到内核 cgroups 里面的配置数据。对于内存来讲,会直接转换成 docker run 命令行的 --memory 大小,最终会配置到 cgroups 对应任务的 /sys/fs/cgroup/memory/……/memory.limit_in_bytes 文件中。
NOTE:若是 Limit 没有配置,则代表没有资源的上限,只要节点上有对应的资源,Pod 就可使用。
使用 Requests 和 Limits 概念,咱们能分配更多的 Pod,提高总体的资源使用率。可是这个体系有个很是重要的问题须要考虑,那就是怎么去准确地评估 Pod 的资源 Requests?若是评估地太低,会致使应用不稳定;若是太高,则会致使使用率下降。这个问题须要开发者和系统管理员共同讨论和定义。
####Limit Range(默认资源配置)
为每一个 Pod 都手动配置这些参数是挺麻烦的事情,Kubernetes 提供了 LimitRange 资源,可让咱们配置某个 Namespace 默认的 Request 和 Limit 值,好比下面的实例:
apiVersion: "v1" kind: "LimitRange" metadata: name: huoban-php spec: limits: - type: "Container" max: cpu: "2" memory: "1Gi" min: cpu: "100m" memory: "4Mi" default: cpu: "500m" memory: "200Mi" defaultRequest: cpu: "200m" memory: "100Mi"
若是对应 Namespace 建立的 Pod 没有写资源的 Requests 和 Limits 字段,那么它会自动拥有下面的配置信息:
内存请求是 100Mi,上限是 200Mi
CPU 请求是 200m,上限是 500m
固然,若是 Pod 本身配置了对应的参数,Kubernetes 会使用 Pod 中的配置。使用 LimitRange 可以让 Namespace 中的 Pod 资源规范化,便于统一的资源管理。
####资源配额(Resource Quota)
前面讲到的资源管理和调度能够认为 Kubernetes 把这个集群的资源整合起来,组成一个资源池,每一个应用(Pod)会自动从整个池中分配资源来使用。默认状况下只要集群还有可用的资源,应用就能使用,并无限制。Kubernetes 自己考虑到了多用户和多租户的场景,提出了 Namespace 的概念来对集群作一个简单的隔离。
基于 Namespace,Kubernetes 还可以对资源进行隔离和限制,这就是 Resource Quota 的概念,翻译成资源配额,它限制了某个 Namespace 可使用的资源总额度。这里的资源包括 CPU、Memory 的总量,也包括 Kubernetes 自身对象(好比 Pod、Services 等)的数量。经过 Resource Quota,Kubernetes 能够防止某个 Namespace 下的用户不加限制地使用超过时望的资源,好比说不对资源进行评估就大量申请 16核 CPU 32G 内存的 Pod。
下面是一个资源配额的实例,它限制了 Namespace 只能使用 20 核 CPU 和 1G 内存,而且能建立 10 个 Pod、20 个 RC、5 个 Service,可能适用于某个测试场景。
apiVersion: v1 kind: ResourceQuota metadata: name: quota spec: hard: cpu: "20" memory: 1Gi Pods: "10" replicationcontrollers: "20" resourcequotas: "1" services: "5"
Resource Quota 可以配置的选项还不少,好比 GPU、存储、Configmaps、PersistentVolumeClaims 等等,更多信息能够参考官方文档。
Resource Quota 要解决的问题和使用都相对独立和简单,可是它也有一个限制:那就是它不能根据集群资源动态伸缩。一旦配置以后,Resource Quota 就不会改变,即便集群增长了节点,总体资源增多也没有用。Kubernetes 如今没有解决这个问题,可是用户能够经过编写一个 Controller 的方式来本身实现。
Requests 和 Limits 的配置除了代表资源状况和限制资源使用以外,还有一个隐藏的做用:它决定了 Pod 的 QoS 等级。
上一节咱们提到了一个细节:若是 Pod 没有配置 Limits ,那么它可使用节点上任意多的可用资源。这类 Pod 能灵活使用资源,但这也致使它不稳定且危险,对于这类 Pod 咱们必定要在它占用过多资源致使节点资源紧张时处理掉。优先处理这类 Pod,而不是处理资源使用处于本身请求范围内的 Pod 是很是合理的想法,而这就是 Pod QoS 的含义:根据 Pod 的资源请求把 Pod 分红不一样的重要性等级。
Kubernetes 把 Pod 分红了三个 QoS 等级:
Pod 的 Requests 和 Limits 是如何对应到这三个 QoS 等级上的,能够用下面一张表格归纳:
看到这里,你也许看出来一个问题了:若是不配置 Requests 和 Limits,Pod 的 QoS 居然是最低的。没错,因此推荐你们理解 QoS 的概念,而且按照需求必定要给 Pod 配置 Requests 和 Limits 参数,不只可让调度更准确,也能让系统更加稳定。
NOTE:按照如今的方法根据 Pod 请求的资源进行配置不够灵活和直观,更理想的状况是用户能够直接配置 Pod 的 QoS,而不用关心具体的资源申请和上限值。但 Kubernetes 目前尚未这方面的打算。
Pod 的 QoS 还决定了容器的 OOM(Out-Of-Memory)值,它们对应的关系以下:
能够看到,QoS 越高的 Pod OOM 值越低,也就越不容易被系统杀死。对于 Bustable Pod,它的值是根据 Request 和节点内存总量共同决定的:
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
其中 memoryRequest
是 Pod 申请的资源,memoryCapacity
是节点的内存总量。能够看到,申请的内存越多,OOM 值越低,也就越不容易被杀死。
QoS 的做用会在后面介绍 Eviction 的时候详细讲解。
###Pod 优先级(Priority)
除了 QoS,Kubernetes 还容许咱们自定义 Pod 的优先级,好比:
apiVersion: scheduling.k8s.io/v1alpha1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "This priority class should be used for XYZ service Pods only."
优先级的使用也比较简单,只须要在 Pod.spec.PriorityClassName
指定要使用的优先级名字,便可以设置当前 Pod 的优先级为对应的值。
Pod 的优先级在调度的时候会使用到。首先,待调度的 Pod 都在同一个队列中,启用了 Pod priority 以后,调度器会根据优先级的大小,把优先级高的 Pod 放在前面,提早调度。
另外,若是在调度的时候,发现某个 Pod 由于资源不足没法找到合适的节点,调度器会尝试 Preempt 的逻辑。简单来讲,调度器会试图找到这样一个节点:找到它上面优先级低于当前要调度 Pod 的全部 Pod,若是杀死它们,能腾足够的资源,调度器会执行删除操做,把 Pod 调度到节点上。更多内容能够参考:Pod Priority and Preemption - Kubernetes
###驱逐(Eviction)
至此,咱们讲述的都是理想状况下 Kubernetes 的工做情况,咱们假设资源彻底够用,并且应用也都是在使用规定范围内的资源。
但现实不会如此简单,在管理集群的时候咱们经常会遇到资源不足的状况,在这种状况下咱们要保证整个集群可用,而且尽量减小应用的损失。保证集群可用比较容易理解,首先要保证系统层面的核心进程正常,其次要保证 Kubernetes 自己组件进程不出问题;可是如何量化应用的损失呢?首先能想到的是若是要杀死 Pod,要尽可能减小总数。另一个就和 Pod 的优先级相关了,那就是尽可能杀死不那么重要的应用,让重要的应用不受影响。
Pod 的驱逐是在 Kubelet 中实现的,由于 Kubelet 能动态地感知到节点上资源使用率实时的变化状况。其核心的逻辑是:Kubelet 实时监控节点上各类资源的使用状况,一旦发现某个不可压缩资源出现要耗尽的状况,就会主动终止节点上的 Pod,让节点可以正常运行。被终止的 Pod 全部容器会中止,状态会被设置为 Failed。
那么哪些资源不足会致使 Kubelet 执行驱逐程序呢?目前主要有三种状况:实际内存不足、节点文件系统的可用空间(文件系统剩余大小和 Inode 数量)不足、以及镜像文件系统的可用空间(包括文件系统剩余大小和 Inode 数量)不足。
下面这图是具体的触发条件:
有了数据的来源,另一个问题是触发的时机,也就是到什么程度须要触发驱逐程序?Kubernetes 运行用户本身配置,而且支持两种模式:按照百分比和按照绝对数量。好比对于一个 32G 内存的节点当可用内存少于 10% 时启动驱逐程序,能够配置 memory.available<10%
或者 memory.available<3.2Gi
。
NOTE:默认状况下,Kubelet 的驱逐规则是
memory.available<100Mi
,对于生产环境这个配置是不可接受的,因此必定要根据实际状况进行修改。
###软驱逐(Soft Eviction)和硬驱逐(Hard Eviction)
由于驱逐 Pod 是具备毁坏性的行为,所以必需要谨慎。有时候内存使用率增高只是暂时性的,有可能 20s 内就能恢复,这时候启动驱逐程序意义不大,并且可能会致使应用的不稳定,咱们要考虑到这种状况应该如何处理;另外须要注意的是,若是内存使用率太高,好比高于 95%(或者 90%,取决于主机内存大小和应用对稳定性的要求),那么咱们不该该再多作评估和考虑,而是赶忙启动驱逐程序,由于这种状况再花费时间去判断可能会致使内存继续增加,系统彻底崩溃。
为了解决这个问题,Kubernetes 引入了 Soft Eviction 和 Hard Eviction 的概念。
软驱逐能够在资源紧缺状况并无哪些严重的时候触发,好比内存使用率为 85%,软驱逐还须要配置一个时间指定软驱逐条件持续多久才触发,也就是说 Kubelet 在发现资源使用率达到设定的阈值以后,并不会当即触发驱逐程序,而是继续观察一段时间,若是资源使用率高于阈值的状况持续必定时间,才开始驱逐。而且驱逐 Pod 的时候,会遵循 Grace Period ,等待 Pod 处理完清理逻辑。和软驱逐相关的启动参数是:
--eviction-soft:软驱逐触发条件,好比 memory.available<1Gi。
--eviction-sfot-grace-period:触发条件持续多久才开始驱逐,好比 memory.available=2m30s。
--eviction-max-Pod-grace-period:Kill Pod 时等待 Grace Period 的时间让 Pod 作一些清理工做,若是到时间尚未结束就作 Kill。
前面两个参数必须同时配置,软驱逐才能正常工做;后一个参数会和 Pod 自己配置的 Grace Period 比较,选择较小的一个生效。
硬驱逐更加直接干脆,Kubelet 发现节点达到配置的硬驱逐阈值后,当即开始驱逐程序,而且不会遵循 Grace Period,也就是说当即强制杀死 Pod。对应的配置参数只有一个 --evictio-hard,能够选择上面表格中的任意条件搭配。
设置这两种驱逐程序是为了平衡节点稳定性和对 Pod 的影响,软驱逐照顾到了 Pod 的优雅退出,减小驱逐对 Pod 的影响;而硬驱逐则照顾到节点的稳定性,防止资源的快速消耗致使节点不可用。
软驱逐和硬驱逐能够单独配置,不过仍是推荐二者都进行配置,一块儿使用。
###驱逐哪些 Pods?
上面咱们已经总体介绍了 Kubelet 驱逐 Pod 的逻辑和过程,那这里就牵涉到一个具体的问题:要驱逐哪些 Pod?驱逐的重要原则是尽可能减小对应用程序的影响。
若是是存储资源不足,Kubelet 会根据状况清理状态为 Dead 的 Pod 和它的全部容器,以及清理全部没有使用的镜像。若是上述清理并无让节点回归正常,Kubelet 就开始清理 Pod。
一个节点上会运行多个 Pod,驱逐全部的 Pods 显然是没必要要的,所以要作出一个抉择:在节点上运行的全部 Pod 中选择一部分来驱逐。虽然这些 Pod 乍看起来没有区别,可是它们的地位是不同的,正如乔治·奥威尔在《动物庄园》的那句话:
全部动物生而平等,但有些动物比其余动物更平等。
Pod 也是不平等的,有些 Pod 要比其余 Pod 更重要。只管来讲,系统组件的 Pod 要比普通的 Pod 更重要,另外运行数据库的 Pod 天然要比运行一个无状态应用的 Pod 更重要。Kubernetes 又是怎么决定 Pod 的优先级的呢?这个问题的答案就藏在咱们以前已经介绍过的内容里:Pod Requests 和 Limits、优先级(Priority),以及 Pod 实际的资源使用。
简单来讲,Kubelet 会根据如下内容对 Pod 进行排序:Pod 是否使用了超过请求的紧张资源、Pod 的优先级、而后是使用的紧缺资源和请求的紧张资源之间的比例。具体来讲,Kubelet 会按照以下的顺序驱逐 Pod:
####防止波动
这里的波动有两种状况,咱们先说说第一种。驱逐条件出发后,若是 Kubelet 驱逐一部分 Pod,让资源使用率低于阈值就中止,那么极可能过一段时间资源使用率又会达到阈值,从而再次出发驱逐,如此循环往复……为了处理这种问题,咱们可使用 --eviction-minimum-reclaim解决,这个参数配置每次驱逐至少清理出来多少资源才会中止。
另一个波动状况是这样的:Pod 被驱逐以后并不会今后消失不见,常见的状况是 Kubernetes 会自动生成一个新的 Pod 来取代,并通过调度选择一个节点继续运行。若是不作额外处理,有理由相信 Pod 选择原来节点的可能性比较大(由于调度逻辑没变,而它上次调度选择的就是该节点),之因此说可能而不是绝对会再次选择该节点,是由于集群 Pod 的运行和分布和上次调度时极有可能发生了变化。
不管如何,若是被驱逐的 Pod 再次调度到原来的节点,极可能会再次触发驱逐程序,而后 Pod 再次被调度到当前节点,循环往复…… 这种事情固然是咱们不肯意看到的,虽然看似复杂,但这个问题解决起来很是简单:驱逐发生后,Kubelet 更新节点状态,调度器感知到这一状况,暂时不往该节点调度 Pod 便可。--eviction-pressure-transition-period 参数能够指定 Kubelet 多久才上报节点的状态,由于默认的上报状态周期比较短,频繁更改节点状态会致使驱逐波动。
作一个总结,下面是一个使用了上面多种参数的驱逐配置实例(你应该能看懂它们是什么意思了):
–eviction-soft=memory.available<80%,nodefs.available<2Gi \ –eviction-soft-grace-period=memory.available=1m30s,nodefs.available=1m30s \ –eviction-max-Pod-grace-period=120 \ –eviction-hard=memory.available<500Mi,nodefs.available<1Gi \ –eviction-pressure-transition-period=30s \ --eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"
Kubernetes 的调度器在为 Pod 选择运行节点的时候,只会考虑到调度那个时间点集群的状态,通过一系列的算法选择一个当时最合适的节点。可是集群的状态是不断变化的,用户建立的 Pod 也是动态的,随着时间变化,原来调度到某个节点上的 Pod 如今看来可能有更好的节点能够选择。好比考虑到下面这些状况:
想要解决上述的这些问题,都须要把 Pod 从新进行调度(把 Pod 从当前节点移动到另一个节点)。可是默认状况下,一旦 Pod 被调度到节点上,除非给杀死不然不会移动到另一个节点的。
为此 Kubernetes 社区孵化了一个称为 Descheduler
的项目,专门用来作重调度。重调度的逻辑很简单:找到上面几种状况中已经不是最优的 Pod,把它们驱逐掉(Eviction)。
目前,Descheduler 不会决定驱逐的 Pod 应该调度到哪台机器,而是假定默认的调度器会作出正确的调度抉择。也就是说,之因此 Pod 目前不合适,不是由于调度器的算法有问题,而是由于集群的状况发生了变化。若是让调度器从新选择,调度器如今会把 Pod 放到合适的节点上。这种作法让 Descheduler 逻辑比较简单,并且避免了调度逻辑出如今两个组件中。
Descheduler 执行的逻辑是能够配置的,目前有几种场景:
RemoveDuplicates
:RS、Deployment 中的 Pod 不能同时出如今一台机器上。LowNodeUtilization
:找到资源使用率比较低的 Node,而后驱逐其余资源使用率比较高节点上的 Pod,指望调度器可以从新调度让资源更均衡。RemovePodsViolatingInterPodAntiAffinity
:找到已经违反 Pod Anti Affinity 规则的 Pods 进行驱逐,多是由于反亲和是后面加上去的。RemovePodsViolatingNodeAffinity
:找到违反 Node Affinity 规则的 Pods 进行驱逐,多是由于 Node 后面修改了 Label。固然,为了保证应用的稳定性,Descheduler 并不会随意地驱逐 Pod,仍是会尊重 Pod 运行的规则,包括 Pod 的优先级(不会驱逐 Critical Pod,而且按照优先级顺序进行驱逐)和 PDB(若是违反了 PDB,则不会进行驱逐),而且不会驱逐没有 Deployment、RS、Jobs 的 Pod 不会驱逐,Daemonset Pod 不会驱逐,有 Local storage 的 Pod 也不会驱逐。
Descheduler 不是一个常驻的任务,每次执行完以后会退出,所以推荐使用 CronJob 来运行。
总的来讲,Descheduler 是对原生调度器的补充,用来解决原生调度器的调度决策随着时间会变得失效,或者不够优化的缺陷。
动态调整的思路:应用的实际流量会不断变化,所以使用率也是不断变化的,为了应对应用流量的变化,咱们应用可以自动调整应用的资源。好比在线商品应用在促销的时候访问量会增长,咱们应该自动增长 Pod 运算能力来应对;当促销结束后,有须要自动下降 Pod 的运算能力防止浪费。
运算能力的增减有两种方式:改变单个 Pod 的资源,以及增减 Pod 的数量。这两种方式对应了 Kubernetes 的 HPA 和 VPA。
横向 Pod 自动扩展的思路是这样的:Kubernetes 会运行一个 Controller,周期性地监听 Pod 的资源使用状况,当高于设定的阈值时,会自动增长 Pod 的数量;当低于某个阈值时,会自动减小 Pod 的数量。天然,这里的阈值以及 Pod 的上限和下限的数量都是须要用户配置的。
上面这句话隐藏了一个重要的信息:HPA 只能和 RC、Deployment、RS 这些能够动态修改 Replicas 的对象一块儿使用,而没法用于单个 Pod、Daemonset(由于它控制的 Pod 数量不能随便修改)等对象。
目前官方的监控数据来源是 Metrics Server 项目,能够配置的资源只有 CPU,可是用户可使用自定义的监控数据(好比:Prometheus)。其余资源(好比:Memory)的 HPA 支持也已经在路上了。
####Vertical Pod AutoScaling
和 HPA 的思路类似,只不过 VPA 调整的是单个 Pod 的 Request 值(包括 CPU 和 Memory)。VPA 包括三个组件:
Recommander:消费 Metrics Server 或者其余监控组件的数据,而后计算 Pod 的资源推荐值。
Updater:找到被 VPA 接管的 Pod 中和计算出来的推荐值差距过大的,对其作 Update 操做(目前是 Evict,新建的 Pod 在下面 Admission Controller 中会使用推荐的资源值做为 Request)。
Admission Controller:新建的 Pod 会通过该 Admission Controller,若是 Pod 是被 VPA 接管的,会使用 Recommander 计算出来的推荐值。
能够看到,这三个组件的功能是互相补充的,共同实现了动态修改 Pod 请求资源的功能。相对于 HPA,目前 VPA 还处于 Alpha,而且尚未合并到官方的 Kubernetes Release 中,后续的接口和功能极可能会发生变化。
随着业务的发展,应用会逐渐增多,每一个应用使用的资源也会增长,总会出现集群资源不足的状况。为了动态地应对这一情况,咱们还须要 CLuster Auto Scaler,可以根据整个集群的资源使用状况来增减节点。
对于公有云来讲,Cluster Auto Scaler 就是监控这个集群由于资源不足而 Pending 的 Pod,根据用户配置的阈值调用公有云的接口来申请建立机器或者销毁机器。对于私有云,则须要对接内部的管理平台。
目前 HPA 和 VPA 不兼容,只能选择一个使用,不然二者会相互干扰。并且 VPA 的调整须要重启 Pod,这是由于 Pod 资源的修改是比较大的变化,须要从新走一下 Apiserver、调度的流程,保证整个系统没有问题。目前社区也有计划在作原地升级,也就是说不经过杀死 Pod 再调度新 Pod 的方式,而是直接修改原有 Pod 来更新。
理论上 HPA 和 VPA 是能够共同工做的,HPA 负责瓶颈资源,VPA 负责其余资源。好比对于 CPU 密集型的应用,使用 HPA 监听 CPU 使用率来调整 Pods 个数,而后用 VPA 监听其余资源(Memory、IO)来动态扩展这些资源的 Request 大小便可。固然这只是理想状况,
从前面介绍的各类 Kubernetes 调度和资源管理方案能够看出来,提升应用的资源使用率、保证应用的正常运行、维护调度和集群的公平性是件很是复杂的事情,Kubernetes 并无完美的方法,而是对各类可能的问题不断提出一些针对性的方案。
集群的资源使用并非静态的,而是随着时间不断变化的,目前 Kubernetes 的调度决策都是基于调度时集群的一个静态资源切片进行的,动态地资源调整是经过 Kubelet 的驱逐程序进行的,HPA 和 VPA 等方案也不断提出,相信后面会不断完善这方面的功能,让 Kubernetes 更加智能。
资源管理和调度、应用优先级、监控、镜像中心等不少东西相关,是个很是复杂的领域。在具体的实施和操做的过程当中,经常要考虑到企业内部的具体状况和需求,作出针对性的调整,而且须要开发者、系统管理员、SRE、监控团队等不一样小组一块儿合做。可是这种付出从总体来看是值得的,提高资源的利用率能有效地节约企业的成本,也能让应用更好地发挥出做用。
Kubernetes 官方文档:
其余文档: