pod的意义

    容器设计本身是一种 “单进程” 模型. 由于容器的应用等于进程. 所以只能去管理pid=1的进程, 其他再起来的应用属于托管状态.  应用本身一般是不具备 管理容器里其他进程的能力.  如果通过exec 或 ssh在容器中创建的其他进程 异常退出时 很容易变成孤儿进程

    pod 相当于一个进程组的概念

pod内的资源共享设置:

  共享网络 : 

    k8s会在pod中 额外起一个 Infra container 容器来共享整个pod的network namespace . 

      Infra container 是永远处于 “pause”状态的容器

    其他容器会通过 join namespace的方式加入到Infra容器的 network namespace中的 共享网络

     所以 一个pod里面 多个容器看到的网络是一样的 (设备, ip地址, mac地址等)

  共享存储:

    volume 变成了 Pod level.  所有容器. 属于同一个pod的容器 共享所有的volume

静态pod: 

    不通过apiserver 管理。无法与 rc, deploy, daemonset 进行关联。直接由kubelete创建管理。kubelet无法对他们做健康检查

kubelet 参数中用 --config 指定配置文件目录。kubelet定期扫描 并根据  .yaml 或 .json 创建  配置文件的内容和普通pod yaml文件一样

在 master上尝试删除 pod会变成pending  在pod所在机器 删除对应配置文件即可

    --manifest-url : kubelet 会定期从这个url下载配置文件创建

    静态pod无法使用configmap

DNS

    1.11 版本开始 使用 CoreDNS 提供   COREdns 的配置

    d678a834e4839abb751261cd071eb31d.png

POD级别dns配置 :

    通过 spec.dnsplicy 字段设置dns策略.

        default : 继承pod所在的宿主机的dns设置

        clusterfirst : 优先使用k8s环境dns服务 (如 coredns) . 无法解析的域名 用宿主机的dns配置

        clusterFirstWithHostNet : 与 ClusterFirst 相同. 对于以 hostNetwork模式运行的Pod . 明确指定使用该策略

        None : 忽略k8s环境dns配置. 通过 spec.dnsConfig自定义dns配置.  1.14版本新特性

            自定义配置 :  这三个自定义配置是可以同时使用的

                nameservers : 一组dns服务器的列表. 最多可以设置三个

                searches : 一组用于域名搜索的dns域名后缀. 最多可以设置6个

                options : 配置其他可选dns参数. 如 ndots, timeout等. k/v形式存储

Init Container 初始化容器 在 spec.initcontainers 中定义    

    用于在启动应用容器之前启动一个初始化容器 完成所需要的预置条件 , 并且必须在成功执行完成后 才能执行下一个容器. 多个init container 按顺序逐个进行. init 也可以资源限制. volume 等

        多个init 都定义资源限制时 取最大值

        pod的资源限制 取 1.所有应用容器资源限制和 2. init的有效限制值 最大值

        调度算法基于pod的资源限制进行计算. 即 init 可以作为初始化操作预留系统资源. 即使后续应用无需使用这些资源

        pod的有效qos等级适用  init 和应用容器

        资源配额限制将根据pod有效资源配额计算

        init 不能设置readinessprobe探针. 

        pod重新启动时 init 将重新运行. 常见pod重启场景如下 :

            1.init镜像被更新时. init 将会重新运行.导致pod重启. 仅更新应用容器镜像只会使得应用容器被重启

       2.pod的 infrastructure 容器更新时 pod 重启. (疑问: 什么是infrastructure)

          3. pod的所有应用容器全部终止. 并且 restartpolicy = always . 则pod会重启

POD 的生命周期和重启策略

c985bef7548369845c5c11968d695ad6.png

Pending apiserver 已经创建该pod . 但pod内还有一个或多个容器的镜像没有创建. 包括处于正在下载镜像的过程
Running pod内容器均已创建. 且至少有一个容器处于运行状态. 正在启动或正在重启状态
Succeeded pod内所有容器均成功执行后退出. 且不会再重启
Failed pod内所有容器均已退出 . 但至少有一个容器退出为失败状态
Unknown 由于某种原因无法获取该pod状态. 可能由于网络不通

POD的重启策略 :

Always 当容器失效时  由kubelet 自动重启该容器
OnFailure 当容器终止运行且退出码 不为0 时 由kubelet 重启
Never 不论运行状态如何 都不会重启

    kubelet 重启失效的容器的时间间隔以 sync-frequency乘以 2n来继续算. 最长延迟5min. 并且在成功重启后的10min后重置该时间

各控制器对Pod的重启策略要求

rc & daemonset必须设置为 always . 需要保证容器持续运行
jobonfailure 或never . 确保容器执行完成后不会重启
kubelet ?在pod失效时自动重启 不论将restartpolicy设置为什么值. 都不会对pod进行健康检查

POD 健康检查和服务可用性检查

liveness判断是否会触发重启策略 , 会触发重启策略
readiness就绪性指针  — faild 不会接流量
readinessGates将自定义的readinessprobe探测方式设置在pod上.  k8s将在判断全部readinessGates条件为True时 才设置pod为服务可用状态   — 1.14 发布的新特性

探测结果

success
failure没有通过检查
Unknown当前的判断机制 没有执行完成. 如超时等. 此时 liveness / readiness 都不会做操作. 等待下次一判断

pod的调度策略

  • pod &pod的关系

      PodAffinity  与 PodAntAffinity   亲和/反亲和e07778624323d61edf8fa5d1563967a3.png

  • pod与Node的关系调度

    NodeSelector

        常规性调度, 无法满足优先调度的策略

    NodeAffinity   支持 In, NotIn, Exists, DoesNotExist, Gt, Lt 等

        设置pod的节点亲和性    c7af5d38df965411242e71912bb57386.png

RequiredDuringSchedulingIgnoredDuringExecution必须满足制定的规则才可以调度pod到node上
PreferredDuringSchedulingIgnoredDuringExecution强调有限满足制定规则.  不强求. 多个优先级可以设置权重.  以定义执行的先后顺序
IgnoredDuringExecution如果一个Pod所在的节点在pod运行期间标签发生了变化. 不再符合该Pod的节点亲和性需求. 则系统忽略label的变化. 该pod能继续在该节点运行

如果同时定义了nodeSelector 和 nodeAffinity 必须两个条件都能满足. Pod才能运行在node上

如果定义了多个nodeSelectorTerms. 其中一个能够匹配即可

如果在 nodeSelectorTerms中有多个matchExpressions. 一个节点必须满足所有matchExpressions 才能运行该pod

  • Taint & toleration

kubectl taint nodes =:NoSchedule

55611322dcdef2e122ea5b9f54d8bb6f.png

NoSchedule
PreferNoSchedule尽量不调度到该node节点
NoExecute如果pod已经在该节点运行. 则会被驱逐. 如果没有运行. 则不会再被调度

    Toleration声明中的key和effect需要与Taint的设置保持一致. 且满足以下条件:

      operator的值必须是Exists (无需制定 value)

      operator的值是Equal 并且value相等. 如果不制定operator 默认为quial

  特例:

    空的key配合exists操作能够符合所有的k/v

    effect 设为空 容忍所有污点

node 允许设置多个taint, pod允许设置多个Toleration, k8s处理多个taint的逻辑顺序为 :

    1.列出所有的taint. 忽略pod的toleration能够匹配的部分. 剩下的没有忽略的taint 就是对pod的效果

    tolerationSeconds : 表明pod可以在taint添加到node之后还能再该node上运行多久

pod的调度

扩展资源的限制 只能填 int  如GPU

基础调度

    三个Qos级别

Guaranteed 

CPU/Memory 必须 request == limit

其余资源可不相

BurstableCPU/Memory request != limit
BestEffort所有资源都不填写 request/limit

三个级别的调度表现

调度器调度时只会根据 request的情况调度, limit的值不会影响调度行为

    Cpu根据request进行调度

    Memory 按 QoS划分 OOMScore

        Guaranteed  == -998

        Burstable 2~999    根据内存设计的大小和节点关系来分配具体分数

        BestEffort 1000

    Eviction  — 驱逐策略

        优先BestEffort

OOM 计分系统

    计分规则 : 系统发生OOM时, OOM killler you 先杀掉 OOM积分更高的进程

        进程占用内存在系统中占的百分比 %10 作为基础分 +

        进程的 OOM_SCORE_ADJ 

        如果进程的OOM_SOCRE_ADJ 为999 这类进程不会被 OOM

本地磁盘是 BestEffort 资源 : 

    有Imagefs情况

        如果nodefs触发. kubelet根据nodefs使用情况对pod进行排序

        如果imagefs触发 . kubelet 根据pod中所有容器消耗的可写入层的使用空间进行排序

    没有imagefs情况

        如果nodefs触发. kubelet对每个pod中所有容器的总体磁盘消耗 (本地卷+ 日志+容器写入层占用空间) 进行排序

        nodefs文件系统达到了阈值. kubelet按照以下顺序清理空间

        删除死掉的Pod, 容器

        删除所有无用的镜像

kubelt的参数 cpu-manager-policy=static 时 对于Guaranteed, 如果CPUrequest整数? 会对该级别pod做cpu的绑定

高级调度 — 集群资源不够时的策略

默认开启的特性

e0214898d5bf13db8e95452d60838287.png

    ebdd1a1783c14921669ff0760c5ce509.png

通过以下维度定义 pod 重要程度

    Priority : 优先级

    Qos : 服务质量等级

    系统定义的其他度量指标

 优先级抢占调度策略的核心行为 分别是 驱逐 Eviction 与 抢占 Preemption .  使用场景不同  但效果相同

Eviction : kubelet 的行为. 当一个node发生资源不足时. 执行驱逐.  根据pod优先级, 资源申请量与实际使用量等信息计算那些pod需要被驱逐. 相同优先级pod需要被驱逐时. 实际使用资源量超过申请量最大倍数的首先驱逐. 

Preemption : scheduler行为. 当一个pod因为资源无法满足不能被调度时, scheduler 选择驱逐部分低优先级pod满足pod的调度目标

优先级调度

        Priority

        Preemption

    在Pod的待调度队列中 Schedule 会优先安排调度高优先级的Pod到Node上

    优先级抢占

daafb97abdd12078d3074f97ee8946fc.png

    优先级抢占策略

544a1a678079fc7e13f087c3f0a9d7ec.png

Pod的驱逐机制 -- 资源紧缺时的pod的驱逐机制

    kubelet 持续监控主机资源使用情况. 一旦出现资源紧缺现象. 会主动终止一个或多个Pod运行.  当Pod被终止时. 其中所有容器会全部停止. Pod的状态会被设置为Failed

    驱逐信号 : kubelet以来这些信号作为决策依据来触发驱逐行为

eb67461a9c4ecd26db4d6bed90a8aedc.png

驱逐方式

软驱逐

由驱逐阈值 和 设定的宽限期 共同定义 

如果定义了 Pod的宽限期. kubelet 用 pod.Spec.TerminationGracePeriodSeconds 和 最大宽限期 的 最小值进行宽限

    软阈值定义相关参数 

      —eviction-soft : 描述驱逐阈值 如 memory.available<1.5GiB 

      —eviction-soft-grace-period : 驱逐宽限期 . 达到软阈值后持续时间超过多久才进行驱逐

    —eviction-max-pod-grace-period : 到达软阈值后 终止Pod的最大宽限期线 

硬驱逐

到达阈值 kubelet 立即杀掉Pod进行回收

--eviction-hard=memory.available<1.5GiB

驱逐监控频率 : --housekeeping-interval

26e78c32567304d60c54af4bdcfb20cb.png

kubelet 对每种资源定义 minimum-reclaim . 一旦开始驱逐. 回收不少于这个参数定义的资源数量

—eviction-pressure-transition-period  默认为五分钟 . kubelete设置脱离压力状态之前需要等待的时间 . 防止 节点在软阈值上下抖动. 又没超过宽限期的情况

节点资源紧缺情况下的系统行为 

    node资源紧缺时. node向master报告情况. scheduler 不再继续向该节点调度新的pod

        对应行为 : MemoryPressure , DiskPressure, Node 的 OOM 行为

    目前的缺陷 

        kubelet无法及时观测到内存压力. kubelet 从 cAdvisor 定时获取内存期间内 发生快速增长. 且 kubelet无法观察到MemoryPressure . 则可能会出发OOMKiller

主动驱逐保护

    PodDisruptionBudget 资源对象.  通过labelselector 作用于同一组被同一个控制器管理的Pod .  作用是保重被驱逐的Pod 在同一时间不会停掉太多Pod 保证服务的稳定性 

    主要使用场景 

        节点的维护或升级

        对应用的自动缩容操作

    使用方法 

        创建了 PodDisruptionBudget 后 通过 Pod的驱逐Api 手动curl 来完成 . 如果Pod数量少于这个资源指定的数量的时候再进行删除命令 会返回错误

Requests 和 Limits 的背后机制 :

    spec.container[].resources.requests.cpu : 转化为 core 数 .* 1024 结果作为 --cpu-shares参数传递给docker run 命令

    spec.container[].resources.limits.cpu : 转化为millicore数. *100000 再除以1000 作为 --cpu-quota传递给docker run. 

        run命令中另一个参数 --cpu-period 默认设置为 100000 .表示docker 重新计量和分配cpu的使用时间间隔为 100000μs (100ms)

    --cpu-quota 和 --cpu-period 参数一起配合完成对 容器cpu的使用限制 :

    例 : 如limits 为 0.1. --cpu-quota 为 10000, --cpu-period为 1000000. 那么 在 100ms内最多给该容器分配 10ms*core的计算资源用量

    kubelet 参数 --cpu-cfs-quota 默认为true . 强制要求所有pod 必须配置 cpu limits . ( 如果pod没有配置. 则集群提供默认配置也可以 )

    spec.container[].resources.requests.memory : 只提供给调度器作为管理和调度的依据 . 不作为任何参数传递给docker

    spec.container[].reousrces.limits.memory :  转发为 bytes单位整数 作为 --memory 参数传递给docker

资源管理 :

    没有为 pod 设定 cpu request 与memory request时 k8s会默认认为 该pod 所需要的资源很少. 并可以将其调度到任何可用的node上 . 并且pod 可以使用所有可用 cpu和内存

    spec.container.reousrces. 资源限制 是针对容器级别

    k8s提供了 对pod级别的 Requests / Limits 配置 . 定义为pod之下容器资源使用总和

    如果 一个node上 运行的pod的实际使用资源很低. 但是 pod的Reuqests 总和很高. 再加上 待调度的Pod的Requests值. 如果超过node提供的资源上限 不会往这上面调度

资源配额管理

ResourceQuota — 限制 namespace的资源用量

    c97f8cfab59a8b0dfccac6b0601a261e.png

如果某ns资源使用超过限制. 在 之后创建&更新的请求会返回 403 并携带详细的出错原因

资源配额 与 集群资源总量 是完全独立的关系 . 增加/减少节点 不影响资源配额的配置

配置 Resource Quotas时 . 必须设置 Requests 或 Limits. 否则配额系统可能会直接拒绝Pod创建. 也可以使用LimitRange机制来为没有配置资源的Pod 提供默认资源管理 

使用注意事项 :

    如果集群总资源小于各ns资源配额总和 造成资源竞争时 . k8s遵循先到先得

在 kube-apiserver 的 --admission-control参数值中添加ResourceQuota参数开启. 在某个ns定义中存在ResourceQuota. 对于该ns 资源配额就是开启的 . 一个ns 可以拥有多个资源配额配置项

资源配额配置项 :

    计算资源类

            6e65b76b6fc7411ae23c2d121193a084.png

        如果配额指定了 {requests | limits}.{cpu | memory} 则 强制要求每个容器都制定配置自己的限制. 或使用 LimitRange 提供的默认值

    存储类 

            a203602d29a8ba3e7ae658d6d1481129.png

    对象数量配额 

            ecb06cb967ebbcb83270b961da5cdbc5.png

资源配额作用域 

    每项配额可单独配置作用于. 仅对作用于内的资源进行计量和限制

            501be4e13e296beca7538f1d7f3a5825.png

    BestEffort 通过限定资源配额追踪pod资源使用

    其他三种用于追踪以下几种资源使用 :

        计算资源类 和 Pods的使用

Requests 和 Limits 针对不同资源类型的限制机制和差异

    可压缩资源 : CPU

        Pod 没有超过 Limits 限制. 但 Node 上宿主机资源不够时 :

            剩余CPU资源 pod 按照设定比值 进行分配

        Pod 超出了 Limits 配置 . cgroups 会对pod中的容器cpu进行限流. 如果没有Limits 会尝试抢占所有空闲CPU资源. k8s 默认开启 --cpu-cfs-quota . 因此默认情况下必须配置 Limits 

    不可压缩资源 : 内存

        如果 Pod 内存用量超过了 Limits 设置. kernel 会杀掉 Pod中 占用内存最多的一个. 直到内存不会超过 Limits

        Pod A 使用了超过 Requests 不到 Limits 的内存量.  同一个Node B目前使用远少于Requests的内存. 但是突增内存需求 . 向node申请时可能会杀掉PodA

        Pod A 使用了超过 Requests 不到 Limits 的内存量. 新调度的Pod 需要内存时 也可能会杀掉Pod A 来释放内存资源

pod 的调度过程

创建pod的大致流程

    491d11a9b9fd8a85a4f85a4e7ab3042e.png

    yaml提交到apiser, apiserver 通过webhook 给 Controllers 进行校验, 生成pod, 此时生成的pod, nodename为空, phase是pending状态. Scheduler watch到 pod的nodename为空后, 通过一系列调度算法, 决定node后会更新pod的nodename, kubelet watch到pod是属于自己节点后 把pod拿到自己节点进行操作,  包括创建容器storage 及 network等, 且把pod状态更新为 Running

kubelet 创建 修改pod步骤

    为pod创建数据目录

    从apiserver读取该pod清单

    为pod挂载外部卷

    下载pod用到的secret 

    检查已经运行在节点的pod . 如果pod没有容器或pause容器没有启动. 则先停止pod所有容器的进程. 如果pod中有需要删除的容器. 则删除容器

    用pause镜像为每个pod创建一个容器. 

    为pod中的每个容器做以下处理

        计算容器hash值. 去查询对应docker容器hash值. 若值不一样. 停止容器进程.停止关联的pause容器进程

        若容器终止. 且没指定restartPolicy 不做任何处理

        调用docker client 下载容器镜像

Scheduler 打分标准

    node要满足 pod的资源需求

    满足pod的一些特殊关系要求

    满足node的一些限制要求

    整个集群资源的合理利用

43e7b4e8c39743386fb0465d0f9596e5.png

    调度算法流程

        通过Informer去watch需要等待的pod数据, 放到队列,  通过调度算法流程, 循环从队列拿数据, 经过调度流水线

    调度器从队列拿到Pod进入Schedule Thread流程, 通过 Pre Filter —Filter —Post Filter — Score — Reserve 

    基本流程结束后把任务提交给 Wait Thread 及 Bind Thread, 然后Schedule Theread 继续执行流程. 做下一个调度

    调度完成后, 会去更新 Schedule Cache 

        如Pod数据缓存, 更新Node数据

    调度流水线的组成

        调度器的调度流程

        wait流程

        bind流程

fd1ae2a68fe77760557e33b7e5b5f2fa.png

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐