kubernetes中的容器建立无疑是个复杂的过程,涉及内部各类组件的统一协做,还有对接外部的CRI运行时,本文尝试初探一下容器建立流程中的各类细节,了解其各类组件协做流程,从而在后续出现问题的时候,也好能大概有点排查方向api
kubelet中的线程模型属于master/worker模型,经过单master来监听各类事件源,并为每一个Pod建立一个goroutine来进行Pod业务逻辑的处理,master和worker之间经过一个状态管道来进行通讯网络
在经过yaml建立Pod以后,kubernetes会根据当前的事件和当前的Pod状态,来不断进行调整,从而达到最终目标状态的一致性ide
kubelet的结构体声明就高达300多行代码,可见其复杂程度,可是咱们按照容器建立这个流程,咱们去观察其核心流程,其实主要能够归纳为三部分:kubelet、containerRuntime、CRI容器运行时性能
kubelet的事件源主要包含两个部分:静态Pod和Apiserver,咱们这里只考虑普通的Pod,则会直接将Pod加入到PodManager来进行管理,而且进行准入检查spa
准入检查主要包含两个关键的控制器:驱逐管理与预选检查
驱逐管理主要是根据当前的资源压力,检测对应的Pod是否容忍当前的资源压力;
预选检查则是根据当前活跃的容器和当前节点的信息来检查是否知足当前Pod的基础运行环境,例如亲和性检查,同时若是当前的Pod的优先级特别高或者是静态Pod,则会尝试为其进行资源抢占,会按照QOS等级逐级来进行抢占从而知足其运行环境插件
kubelet接收到一个新建立的Pod首先会为其建立一个事件管道,而且启动一个容器管理的主线程消费管道里面的事件,而且会基于最后同步时间来等待当前kubelet中最新发生的事件(从本地的podCache中获取),若是是一个新建的Pod,则主要是经过PLEG中更新时间操做,广播的默认空状态来做为最新的状态线程
当从本地的podCache中获取到最新的状态信息和从事件源获取的Pod信息后,会结合当前当前statusManager和probeManager里面的Pod里面的容器状态来更新,从而获取当前感知到的最新的Pod状态日志
以前的准入检查是Pod运行的资源硬性限制的检查,而这里的准入检查则是软状态即容器运行时和版本的一些软件运行环境检查,若是这里检查失败,则会讲对应的容器状态设置为Blockedserver
在经过准入检查以后,会调用statusManager来进行POd最新状态的同步,此处可能会同步给apiserverblog
在更新完成状态以后会启动一个PodCOntainerManager主要做用则是为对应的Pod根据其QOS等级来进行Cgroup配置的更新
接下来kubelet会为Pod的建立准备基础的环境,包括Pod数据目录的建立、镜像秘钥的获取、等待volume挂载完成等操做
建立Pod的数据目录主要是建立 Pod运行所须要的Pod、插件、Volume目录,而且会经过Pod配置的镜像拉取秘钥生成秘钥信息,到此kubelet建立容器的工做就已经基本完成
前面咱们提到过针对Pod的操做,最终都是基于事件和状态的同步而完成,在containerRUntime并不会区分对应的事件是建立仍是更新操做,只是根据当前的Pod的信息与目标状态来进行对比,从而构建出对应的操做,达到目标状态
计算容器变动主要包括:Pod的sandbox是否变动、短声明周期容器、初始化容器是否完成、业务容器是否已经完成,相应的咱们会获得一个几个对应的容器列表:须要被kill掉的容器列表、须要启动的容器列表,注意若是咱们的初始化容器未完成,则不会进行将要运行的业务容器加入到须要启动的容器列表,能够看到这个地方是两个阶段
若是以前检测到以前的初始化容器失败,则会检查当前Pod的全部容器和sandbox关联的容器若是有在运行的容器,会所有进行Kill操做,而且等待操做完成
当一些Pod的容器已经运行,可是其状态仍然是Unknow的时候,在这个地方会进行统一的处理,所有kill掉,从而为接下来的从新启动作清理操做,此处和3.2只会进行一个分支,但核心的目标都是清理那些运行失败或者没法获取状态的容器
在启动Pod的容器以前,首先会为其建立一个sandbox容器,当前Pod的全部容器都和Pod对应的sandbox共享同一个namespace从而共享一个namespace里面的资源,建立Sandbox比较复杂,后续会继续介绍
Pod的容器目前分为三大类:短生命周期容器、初始化容器、业务容器,启动顺序也是从左到右依次进行,若是对于的容器建立失败,则会经过backoff机制来延缓容器的建立,这里咱们顺便介绍下containerRuntime启动容器的流程
镜像的拉取首先会进行对应容器镜像的拼接,而后将以前获取的拉取的秘钥信息和镜像信息,一块儿交给CRI运行时来进行底层容器镜像的拉取,固然这里也会各类backoff机制,从而避免频繁拉取失败影响kubelet的性能
建立容器配置主要是为了容器的运行建立对应的配置数据,主要包括:Pod的主机名、域名、挂载的volume、configMap、secret、环境变量、挂载的设备信息、要挂载的目录信息、端口映射信息、根据环境生成执行的命令、日志目录等信息
调用runtimeService传递容器的配置信息,调用CRI,而且最终调用容器的建立接口完成容器的状态
经过以前建立容器返回的容器ID,来进行对应的容器的启动,而且会为容器建立对应的日志目录
若是容器配置了PostStart钩子,则会在此处进行对应钩子的执行,若是钩子的类型是Exec类则会调用CNI的EXec接口完成在容器内的执行
首先会拉取sandbox镜像
在建立容器以前会先根据SecurityContext里面的配资信息,来进行容器SecurityContext的配置,主要包括特权等级、只读目录、运行帐户组等信息
除了应用SecurityContext还会进行断开、OOMScoreAdj、Cgroup驱动等信息的映射
根据上面的各类配置信息来进行容器的建立
checkpoint主要是将当前sandbox的配置信息进行序列化,而且存储其当前的快照信息
启动sandbox容器则会直接调用StartContainer同时传入以前建立容器返回的ID完成容器的启动,而且此时会重写覆盖容器的dns配置文件
容器的网络配置主要是调用CNI插件来完成容器网络的配置,这里就先不展开了
kubelet是容器管理的核心大管家,其负责各类准入控制、状态管理、探测管理、volume管理、QOS管理、CSI对接的统一调度,而且为Runtime运行时准备基础的数据和并反馈Pod当前的最新状态
Runtime层则将kubelet组装的数据,按照CRI运行时的目标配置和kubelet管理的资源配置信息来进行资源的重组,而且根据Pod的容器的状态来决策容器的启停、建立等操做,并完成容器的基础配置环境的构建,并最终调用CRI完成容器的建立,而CRI运行时,则会讲传递过来的各类数据进行进一步的组合,并应用到主机和对应的namespace资源限制,并根据本身的容器服务组织数据,调用容器服务完成容器的最终建立
本文是一个基础版本,后续会在该版本上,继续叠加各类细节,感兴趣的朋友能够帮忙转发关注下,谢谢你们
k8s源码阅读电子书地址: https://www.yuque.com/baxiaoshi/tyado3