计算机系统应用教程网站

网站首页 > 技术文章 正文

一文读懂 Kubernetes 是如何调度 Pod 的

btikc 2024-12-02 15:34:29 技术文章 19 ℃ 0 评论

随着云原生的不断发展,kubernetes 在我们的应用场景中越来越重要。

对于用过 kubernetes 的小伙伴都知道, Pod 是一组容器的集合,这些容器共享网络、UTS、命名空间,并且可以直接进行通信。

如果是 Pod 与 Pod 之间的通信 还得考虑是同一节点内的还是不同节点的,这个就不展开聊了,今天主要跟大家讨论下Pod的调度问题。

我们都知道 kubernetes 中 Pod可以通过设置Label、指定节点、设置亲和/反亲和等方式来进行调度,那么Pod调度的细节是什么样的?Pod的生命周期kubernetes是如何感知的?


Pod 相位

在介绍Pod 创建流程之前,我们先看一下Pod的相位状态,主要有5种。

  • Pending:API Server 创建了Pod资源对象并已存入etcd 中,但它尚未被调度完成,或者仍处于从仓库下载镜像的过程中。
  • Running:Pod 已经被调度至某节点,并且所有容器都已经被 kubelet创建完成。
  • Succeeded:Pod 中的所有容器都已经成功终止并且不会被重启。
  • Failed:所有容器都已经终止,但至少有一个容器终止失败,退出状态或已经被系统终止。
  • Unknown:API Server无法正常获取到Pod对象的状态信息,通常是由于其无法与所在工作节点的kubelet通信所致。


Pod 创建

我们可以从下图中很直观地看出 Pod 的一个创建流程。

  1. 用户通过 kubectl 或其他API客户端提交 Pod Spec 给 API Server。
  2. API Server 尝试着将Pod对象的相关信息存入 etcd 中,待写入操作执行完成,API Server即会返回确认信息至客户端。
  3. API Server 开始反映 etcd 中的状态变化。
  4. 所有的Kubernetes组件均使用“watch”机制来跟踪检查API Server上的相关变动。
  5. kube-scheduler(调度器)通过其“watcher”觉察到API Server创建了新的Pod对象但尚未绑定至任何工作节点。
  6. kube-scheduler 为Pod对象挑选一个工作节点并将结果信息更新至API Server
  7. 调度结果信息由API Server更新至 etcd 存储系统,而且 API Server 也开始反映此Pod对象的调度结果
  8. Pod 被调度到的目标工作节点上的 kubelet (watch到了)尝试在当前节点上调用Docker启动容器,并将容器的结果状态回送至 API Server。
  9. API Server 将 Pod 状态信息存入 etcd 系统中。
  10. 在etcd 确认写入操作成功完成后,API Server 将确认信息发送至相关的 kubelet,事件将通过它被接受。


其中创建流程第6步骤,kube-scheduler 通过Label、NodeName、Affinity(亲和性),并结合Pod的requests、limits以及节点的kubelet 设置来判断将 Pod 调度到哪一个Node 上。

我们从流程中还可以看出来,最核心的就是 Apiserver 的watch机制(实际是Etcd的watch),Scheduler 以及kubelet 都是通过 watch 机制去感知 Pod 的调度情况的。

这时就有小伙伴问了,kubernetes 控制面组件中的 kube-contreller-manager 怎么没有在流程中体现呢?

因为 kube-contreller-manager 包含了很多的控制器,管理的是更高级的一些资源对象。比如Deployment、StatefullSet、DaemonSet等等,如果 Pod 是通过这些对象去创建的话,这个时候kube-contreller-manager 派上用场了,它也会通过 watch 资源对象的的最新状态,并不断进行调谐从而达到用户(yaml)所期望的 Pod 状态。


Pod 终止

我们再来说下 Pod 的终止流程。

  1. 用户发送删除Pod对象的命令。
  2. API 服务器中的Pod对象会随着时间的推移而更新,在宽限期内(默认为30秒),Pod被视为“dead”。
  3. 将Pod标记为“Terminating”状态。
  4. (与第 3 步同时运行)kubelet在监控到Pod对象转为“Terminating”状态的同时启动Pod关闭过程
  5. (与第 3 步同时运行)端点控制器监控到Pod对象的关闭行为时将其从所有匹配到此端点的Service资源的端点列表中移除
  6. 如果当前Pod对象定义了preStop钩子处理器,则在其标记为“terminating”后即会以同步的方式启动执行;如若宽限期结束后,preStop仍未执行结束,则第2步会被重新执行并额外获取一个时长为2秒的小宽限期。
  7. Pod对象中的容器进程收到TERM信号
  8. 宽限期结束后,若存在任何一个仍在运行的进程,那么Pod对象 即会收到SIGKILL信号
  9. Kubelet请求API Server将此Pod资源的宽限期设置为0从而完成删除操作,它变得对用户不再可见 。


钩子函数

在终止流程中,kubelet 会执行 preStop 钩子函数,该函数常常用于让容器中的服务优雅地关闭或者是通知其他服务等场景。

同样的,可以在 Pod 中设置 preStop 钩子函数之,还可以设置 postStart 函数,该函数将于容器创建后立即运行,常常用于一些服务组件注册、环境部署等。

除了 钩子函数,Pod 的生命周期中还有其他一些重要行为,比如 初始化容器(init container )与 探针。


初始化容器

即应用程序 的主容器启动之前要运行的容器,常用于为主容器执行一些预置操作。

比如初始化配置,将pod或者主容器注册到配置中心等。


容器探测

探针主要分类三种,分别是livenessProbe、readinessProbe与startupProbe,其中 startupProbe 是在1.16版本后加的探针方式。

  • livenessProbe,用于判定容器是否处于“运行”( Running )状态。

livenessProbe检测未通过,kubelet 将杀死容器并根据其 restartPolicy 决定是否将其重启。

  • readinessProbe,用于判断容器是否准备就绪并可对外提供服务。

readinessProbe未通过检测的容器意味着其尚未准备就绪,端点控制器(如Service 对象)会将其IP从所有匹配到此Pod对象的Service对象的端点列表中移除。

  • startupProbe,用于判断容器中的应用是否已经启动。

startupProbe 如果设置了,则在此探针成功之前,其他探针都会被禁用。


资源需求与限制

  • requests:资源定义的确保可用值,即容器运行可能用不到这些额度的资源,但用到时必须要确保有如此多的资源可用。
  • limits:用于限制资源可用的最大值。

Pod 中可以对container 进行 requests 和 limits 的设置。通过这两个资源参数进行内存或者CPU的限制,从而达到对容器资源消耗的控制。示例如下:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

上述提到 kube-scheduler 在选择节点进行 Pod 调度跟 Pod 的 requests 与 limits 也有关系,为什么呢?

试想下,如果在进行 Pod 调度的时候不考虑这个因素,那么一个节点上的Pod 资源上限总和可能会超出节点的资源总量,从而会干扰业务的正常运行。

如果某一个节点上 Pod 资源总和已达到上限,刚刚好又调度了一个 Pod 到该节点,此时会出现什么情况呢?

这个时候就不得不提到 QOS 了。

pod服务质量类别,即 QOS(Quality of Service)。在内存资源紧缺时,应该以何种次序先后终止哪些Pod对象?Kubemetes无法自行对此做出决策,它需要借助于 Pod 对象的优先级完成判定。根据 Pod 对象的 requests 和 limits 属性,Kubemetes 将 Pod 对象归类到 BestEffort 、 Burstable 和 Guaranteed 三个服务质量。

  • Guaranteed:每个容器都为CPU资源设置了具有相同值的requests和limits属性,以及每个容器都为内存资源设置了具有相同值的requests和limits属性的Pod资源会自动归属于此类别,这类Pod资源具有最高优先级。
  • Burstable:至少有一个容器设置了CPU或内存资源的requests属性,但不满足Guaranteed类别要求的Pod资源将自动归属于此类别,它们具有中等优先级。
  • BestEffort:未为任何一个容器设置requests或limits属性的Pod资源将自动归属于此类别,它们的优先级为最低级别。

对于QOS 如何打分,大家可以在源码中kubernetes/pkg/apis/core/v1/helper/qos/qos.go 开始看起。


参考资料

https://kubernetes.io/


往期推荐

浅谈云原生 | 推荐三款开源容器云平台项目

教你如何申请永久免费 SSL 证书

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表