Kubernetes 集群架构和Pod创建流程
架构概念
Kubernetes 集群由一个控制平面和一组工作机器组成,称为节点、运行容器化应用程序,每个集群至少需要一个工作节点才能运行Pod。
工作节点托管作为应用程序工作负载组件的Pod。控制平面管理集群中的工作节点和Pod。生产环境中,控制平面通常跨多台计算机和集群运行运行多个节点,提供容错和可用性。
控制平面组件
对集群做出全局决策(调度资源),以及检测和响应集群时间(但Deployment的字段不满足时,启动新的pod)。控制平面组件可以在集群中的任何计算机上运行。如何构建高可用集群请阅读Creating Highly Available Clusters with kubeadm | Kubernetes
kube-api 服务器
API服务器是Kubernetes的一个组件 ,控制平面中公开的API,相当于控制平面的前端。
Kubernetes API服务器的主要实现是kube-apiserver。kube-apiserver旨在水平扩展,通过部署更多实例来扩展。可以运行多个kube-apiserver实例,并在这些实例之间平衡流量。
etcd
一致且高可用的键值存储,用作所有集群数据的Kubernetes支持存储。
kube-apiserver 是 Kubernetes 集群与所有外部交互的中央枢纽和唯一入口。它扮演着集群"前门"的角色,所有内部组件、外部用户、客户端工具(如 kubectl)甚至其他集群的通信都必须通过它。以下是深度解析:
kube-scheduler
监视新创建的控制平面组件pod,为它们选择一个节点继续运行。
调度决策时考虑的因素包括: 个人和集体资源需求、硬件/软件/政策 约束、亲和力和反亲和力规范、数据局部性、 工作负载间干扰和截止日期。
kube-controller-manager
运行的控制平面组件控制器过程。
从逻辑上讲,每个控制器是一个单独的进程,但为了降低复杂性,它们都编译成一个二进制文件并在单个进程中运行。
有许多不同类型的控制器。其中一些例子是:
- 节点控制器:负责在节点宕机时进行注意和响应。
- 作业控制器:监视表示一次性任务的作业对象,然后创建 Pod 来运行这些任务直至完成。
- EndpointSlice 控制器:填充 EndpointSlice 对象(以提供 Service 和 Pod 之间的链接)。
- ServiceAccount 控制器:为新命名空间创建默认 ServiceAccount。
以上不是详尽的列表。
cloud-controller-manager
A Kubernetes控制平面元件 嵌入特定于云的控制逻辑。云控制器管理器允许您链接您的 集群到云提供商的 API 中,并将交互的组件分开 使用仅与集互的组件的云平台。
cloud-controller-manager 仅运行特定于您的云提供商的控制器。 如果您在自己的本地或内部的学习环境中运行 Kubernetes。 自有 PC,集群没有云控制器管理器。
与 kube-controller-manager 一样,cloud-controller-manager 在逻辑上结合了几个 独立控制循环到单个二进制文件中,作为单个进程运行。您可以缩放 水平(运行多个副本)以提高性能或帮助容忍故障。
以下控制器可以具有云提供商依赖项:
- 节点控制器:用于检查云提供商以确定节点是否已 停止响应后在云中删除
- 路由控制器:用于在底层云基础设施中设置路由
- 服务控制器:用于创建、更新和删除云提供商负载均衡器
Cloud Controller Manager 本质上是 Kubernetes 的"云驱动程序",使集群能够:
-
动态响应云基础设施变化
-
自动配置云原生网络和存储
-
无缝集成云平台高级服务
这种设计既保持了 Kubernetes 核心的云中立性,又充分释放了各云平台的独特能力。
类比概念 | 说明 | CCM 对应实现 |
---|---|---|
操作系统驱动程序 | 让操作系统能控制特定硬件 | 让 K8s 能控制特定云平台 |
手机基带芯片 | 连接手机与移动网络的通信模块 | 连接 K8s 集群与云 API 的通信层 |
支付接口网关 | 统一对接不同支付渠道 | 统一对接不同云服务商的 API |
关键价值:通过 CCM,Kubernetes 无需关心底层是 AWS、Azure 还是阿里云,都能以统一方式管理基础设施资源。
工作原理解析:API 调用转换器
Node节点
每个节点维护正在运行的Pod并提供k8s运行时的环境。
Node与容器的关系
一个Node上可以运行多个容器:
-
1个 Node ➔ 多个 Pod(通常数十到数百个)
-
1个 Pod ➔ 1个或多个容器(Sidecar 模式常见多容器)
-
1个容器 ➔ 1个应用进程
Node与Pod的区别
Node
-
本质:物理机或虚拟机(如 AWS EC2、Azure VM)
物理/虚拟主机:每个 Node 对应一台物理服务器、虚拟机或云实例(如 AWS EC2)
资源池:Node 将其全部硬件资源抽象为可分配的计算单元
资源隔离边界:Node 间资源严格隔离(除非使用特殊技术如 DPDK)
资源划分的层次结构
节点总容量 vs 可分配资源
资源类型 | Node 总容量 | 系统预留 | K8s 组件预留 | 可分配资源(Allocatable) |
---|---|---|---|---|
CPU | 16 cores | 0.5 core | 1 core | 14.5 cores |
内存 | 64GiB | 1GiB | 2GiB | 61GiB |
本地存储 | 500GB | 20GB | - | 480GB |
计算公式:
Allocatable = NodeCapacity - SystemReserved - KubeReserved - EvictionThreshold
-
职责:
-
提供计算资源(CPU/内存/存储)
-
运行容器运行时(如 Docker、containerd)
-
托管 kubelet 和 kube-proxy
-
常见问题解答
Q:Node 资源会被非 Kubernetes 进程占用吗?
A:是的,需要配置 systemReserved
保留资源给系统进程(如 SSH、监控代理)
Q:如何防止单个 Pod 耗尽节点资源?
A:通过 ResourceQuota
和 LimitRange
实施限制
Q:云节点的 "vCPU" 如何换算?
A:通常 1 vCPU = 1 超线程核,但实际性能受云厂商物理机负载影响
Pod
-
最小调度单元:Kubernetes 不直接管理容器,而是调度 Pod
-
特点:
-
共享网络命名空间(同一 Pod 内容器通过
localhost
通信) -
共享存储卷(Pod 级别挂载,容器内可见)
-
生命周期一致(同时创建/销毁)
-
容器
-
隔离的进程组:基于 cgroups/namespaces 实现隔离
-
镜像实例:每个容器运行一个镜像的实例
Node 类型 | Pod 数量 | 容器数量 | 典型场景 |
---|---|---|---|
小型开发节点 | 5-10 | 8-15 | 本地测试环境 |
生产标准节点 | 30-50 | 50-100 | 常规微服务部署 |
高密度计算节点 | 100+ | 200+ | 批处理任务/Serverless 后端 |
错误认知 | 实际情况 |
---|---|
"Node 就是容器" | Node 是运行容器的宿主机器,容器是 Node 上的进程 |
"一个 Node 只能一个 Pod" | 单个 Node 通常运行多个 Pod(除非资源限制或人为约束) |
"容器直接绑定到 Node" | 容器属于 Pod,Pod 才属于 Node,是两层抽象关系 |
"Pod 就是容器" | Pod 是逻辑包装单元,可能包含多个协同容器(如主容器+Sidecar) |
架构设计意义
-
弹性调度:Pod 可动态迁移到不同 Node
-
资源利用率:多容器共享节点资源
-
故障隔离:一个 Node 宕机只影响部分服务
-
混合部署:不同优先级的 Pod 可共存(通过 QoS 类)
设计哲学:Kubernetes 通过 Node-Pod-Container 的三层抽象,实现了:
基础设施与应用的解耦
灵活的调度能力
高效的资源利用
Container Runtime(容器运行时)
使 Kubernetes 能够有效运行容器的基本组件。 它负责管理 Kubernetes 环境中容器的执行和生命周期。Container Runtime(容器运行时)与 Node(节点)是分层协作关系,二者共同构成工作节点的核心能力。
容器运行时是多租户场景下资源协调与隔离的实际执行者。不同运行时通过各自的技术栈实现:
-
📊 资源共享:通过 cgroups/namespaces 划分资源池
-
💾 存储管理:基于 volume plugin 体系对接多种存储后端
-
🔒 安全隔离:从轻量级命名空间到完整虚拟化不等
组件 | Node(节点) | Container Runtime(容器运行时) |
---|---|---|
本质 | 物理机/虚拟机(资源提供者) | 软件引擎(容器管理工具) |
职责 | 提供计算资源(CPU/内存/存储/网络) | 在节点资源内创建/运行容器 |
抽象层级 | 基础设施层 | 容器虚拟化层 |
K8s 视角 | 集群的工作单元 | Pod 中容器的实际执行者 |
组件间协作的流程
交互接口
CRI(Container Runtime Interface)
# kubelet 配置指定运行时端点
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
containerRuntimeEndpoint: "unix:///run/containerd/containerd.sock"
运行时与节点资源的绑定
资源类型 | 运行时管理方式 | 节点资源可见性 |
---|---|---|
CPU | 通过 cgroups 分配份额 | /proc/cpuinfo 仍显示全部核心 |
内存 | 设置 memory.limit_in_bytes | free -m 显示全部内存 |
存储 | 使用 overlay2 驱动挂载卷 | df -h 显示实际磁盘使用 |
网络 | 创建 veth pair 连接容器与主机网络 | ip addr 显示新增网络设备 |
故障排查路径
# 检查节点状态
kubectl get nodes -o wide# 查看kubelet日志
journalctl -u kubelet# 检查运行时状态
sudo crictl ps -a# 直接与运行时交互(containerd示例)
sudo ctr -n k8s.io containers list
故障现象 | 可能关联层 | 诊断命令 |
---|---|---|
Pod 卡在 ContainerCreating | 运行时服务异常 | systemctl status containerd |
容器频繁重启 | 资源超限(OOMKilled) | kubectl describe pod |
网络不可达 | CNI 插件未与运行时集成 | ip link show |
运行时的隔离
隔离机制 | 实现方式 | 节点可见性 |
---|---|---|
进程隔离 | PID namespace | ps aux 仍可见 |
文件系统隔离 | Mount namespace | ls /proc/<pid>/root |
用户隔离 | User namespace | 主机 UID 映射 |
设备隔离 | Device cgroup | 主机设备文件仍存在 |
层级化协作模型
-
Node 是资源载体:提供计算、存储、网络等物理资源
-
Runtime 是执行引擎:将节点资源转化为可运行的容器环境
-
kubelet 是协调者:通过 CRI 标准接口将二者衔接
这种分层设计使 Kubernetes 能够:
-
灵活支持不同底层基础设施
-
保持容器管理的标准化
-
实现资源的高效调度与隔离
多租户如何共享资源
关键实现技术
-
cgroups v2:划分CPU/内存/IO资源池
-
Linux Namespaces:隔离进程/网络/挂载点等
-
实时配额调整:通过
RuntimeClass
动态分配资源
运行时的处理差异
-
常规运行时:通过 cgroups 隔离 CPU/内存
-
GPU 增强运行时:额外调用
nvidia-container-runtime
隔离 GPU 设备
安全隔离方案对比
1. 隔离层级深度
运行时类型 | 隔离强度 | 性能损耗 | 适用场景 |
---|---|---|---|
传统运行时 | 中 | 低 | 常规企业应用 |
Kata | 高 | 中 | 金融/政务云 |
gVisor | 高 | 较高 | 不可信代码执行 |
Pod的创建流程
执行以下命令
$ kubectl run --image=nginx --replicas=3
一、kubectl
验证和生成器
API版本协商与API组
客户端身份认证
最后发送HTTP请求。一旦请求发送之后获得成功的响应
二、kube-apiserver和etcd
认证-授权-准入控制
kube-apiserver 是客户端和系统组件用来保存和检索集群状态的主要接口。为了执行相应的功能,kube-apiserver 需要能够验证请求者是合法的,这个过程被称为认证。
每次收到请求时,apiserver 都会 通过令牌链进行认证,直到某一个认证成功为止:
- x509 处理程序将验证 HTTP 请求是否是由 CA 根证书签名的 TLS 密钥进行编码的。
- bearer token 处理程序将验证
--token-auth-file
参数提供的 token 文件是否存在。 - 基本认证处理程序确保 HTTP 请求的基本认证凭证与本地的状态匹配。
如果 认证失败,则请求失败并返回相应的错误信息;如果验证成功,则将请求中的 Authorization
请求头删除,并 将用户信息添加到其上下文中。这给后续的授权和准入控制器提供了访问之前建立的用户身份的能力。
kube-apiserver 处理授权的方式与处理身份验证的方式相似:通过 kube-apiserver 的启动参数 --authorization_mode
参数设置。它将组合一系列授权者,这些授权者将针对每个传入的请求进行授权。如果所有授权者都拒绝该请求,则该请求会被禁止响应并且 不会再继续响应。如果某个授权者批准了该请求,则请求继续。
kube-apiserver 目前支持以下几种授权方法:
- webhook: 它与集群外的 HTTP(S) 服务交互。
- ABAC: 它执行静态文件中定义的策略。
- RBAC: 它使用
rbac.authorization.k8s.io
API Group实现授权决策,允许管理员通过 Kubernetes API 动态配置策略。 - Node: 它确保 kubelet 只能访问自己节点上的资源。
准入控制器会拦截请求以确保它符合集群的更广泛的期望和规则。它们是资源对象保存到 etcd
之前的最后一个堡垒,封装了一系列额外的检查以确保操作不会产生意外或负面结果。不同于授权和认证只关心请求的用户和操作,准入控制还处理请求的内容,并且仅对创建、更新、删除或连接(如代理)等有效,而对读操作无效。
准入控制器的工作方式与授权者和验证者的工作方式类似,但有一点区别:与验证链和授权链不同,如果某个准入控制器检查不通过,则整个链会中断,整个请求将立即被拒绝并且返回一个错误给终端用户。
准入控制器设计的重点在于提高可扩展性,某个控制器都作为一个插件存储在 plugin/pkg/admission
目录中,并且与某一个接口相匹配,最后被编译到 kube-apiserver 二进制文件中。
大部分准入控制器都比较容易理解,接下来着重介绍 SecurityContextDeny
、ResourceQuota
及 LimitRanger
这三个准入控制器。
- SecurityContextDeny 该插件将禁止创建设置了 Security Context 的 Pod。
- ResourceQuota 不仅能限制某个 Namespace 中创建资源的数量,而且能限制某个 Namespace 中被 Pod 所请求的资源总量。该准入控制器和资源对象
ResourceQuota
一起实现了资源配额管理。 - LimitRanger 作用类似于上面的 ResourceQuota 控制器,针对 Namespace 资源的每个个体(Pod 与 Container 等)的资源配额。该插件和资源对象
LimitRange
一起实现资源配额管理。
Etcd
kube-apiserver 将对 HTTP 请求进行反序列化,然后利用得到的结果构建运行时对象(有点像 kubectl 生成器的逆过程),并保存到 etcd
中。
资源对象被持久化到数据存储之后,还需要进行一系列初始化操作。Initializers是一种与资源类型相关联的控制器,它会在资源对外可用之前执行某些逻辑。如果某个资源类型没Initializers,就会跳过此初始化步骤立即使资源对外可见。
控制循环
K8s内置的Controller完成层级结构创建的资源。Controller是一个用于将系统状态从“当前状态”修正到“期望状态”的异步脚本。通过kube-controller-manager组件运行,每种controller负责一种具体的控制流。
将 Deployment 记录存储到 etcd 并初始化后,就可以通过 kube-apiserver 使其可见,然后 Deployment Controller
就会检测到它(它的工作就是负责监听 Deployment 记录的更改),g就会将该资源对象添加到内部工作队列,未检测到ReplicaSet 或Pod记录后,该Controller开始执行弹性伸缩流程:创建ReplicaSet资源。该Deployment的状态更新,重新进入之前的循环,等待Deployment与期望的状态相匹配。
ReplicaSet Controller 的工作是监视 ReplicaSets 及其相关资源(Pod)的生命周期。和大多数其他 Controller 一样,它通过触发某些事件的处理器来实现此目的。
当创建 ReplicaSet 时(由 Deployment Controller 创建),RS Controller 检查新 ReplicaSet 的状态,并检查当前状态与期望状态之间存在的偏差,然后通过 调整 Pod 的副本数来达到期望的状态。
Pod 的创建也是批量进行的,从 SlowStartInitialBatchSize
开始,然后在每次成功的迭代中以一种 slow start
操作加倍。这样做的目的是在大量 Pod 启动失败时(例如,由于资源配额),可以减轻 kube-apiserver 被大量不必要的 HTTP 请求吞没的风险。如果创建失败,最好能够优雅地失败,并且对其他的系统组件造成的影响最小!
Informers是一种模式,它允许Controller 查找缓存在本地内存中的数据,访问和修改这些资源对象,不同的Controller之间以线程安全的方式进行交互。
Scheduler(调度器)
目前为止,所有Controller正常运行后,etcd中就会保存一个Deployment 、一个ReplicaSet和三个Pod资源记录,并且通过kube-apiserver查看。Pod资源现在还处于Pending状态。
Scheduler
作为一个独立的组件运行在集群控制平面上,工作方式与其他 Controller 相同:监听实际并将系统状态调整到期望的状态。具体来说,Scheduler 的作用是将待调度的 Pod 按照特定的算法和调度策略绑定(Binding)到集群中某个合适的 Node 上,并将绑定信息写入 etcd 中(它会过滤其 PodSpec 中 NodeName
字段为空的 Pod),默认的调度算法的工作方式如下:
-
当 Scheduler 启动时,会 注册一个默认的预选策略链,这些
预选策略
会对备选节点进行评估,判断备选节点是否 满足备选 Pod 的需求。例如,如果 PodSpec 字段限制了 CPU 和内存资源,那么当备选节点的资源容量不满足备选 Pod 的需求时,备选 Pod 就不会被调度到该节点上(资源容量=备选节点资源总量-节点中已存在 Pod 的所有容器的需求资源(CPU 和内存)的总和) -
一旦筛选出符合要求的候选节点,就会采用
优选策略
计算出每个候选节点的积分,然后对这些候选节点进行排序,积分最高者胜出。例如,为了在整个系统中分摊工作负载,这些优选策略会从备选节点列表中选出资源消耗最小的节点。每个节点通过优选策略时都会算出一个得分,计算各项得分,最终选出分值大的节点作为优选的结果。
一旦找到了合适的节点,Scheduler 就会创建一个 Binding
对象,该对象的 Name
和 Uid
与 Pod 相匹配,并且其 ObjectReference
字段包含所选节点的名称,然后通过 POST
请求 发送给 apiserver。
当 kube-apiserver 接收到此 Binding 对象时,注册吧会将该对象反序列化并更新 Pod 资源中的以下字段:
- 将
NodeName
的值设置为 ObjectReference 中的 NodeName。 - 添加相关的注释。
- 将
PodScheduled
的status
值设置为 True。
一旦Pod调度到某个节点上,该节点的Kubelet就会接管该Pod并开始部署。
三、kubelet创建容器流程
在kubernetes集群中,每个Node节点上都会启动一个Kubelet服务进程,该进程用于处理Scheduler下发到本节点的任务,管理Pod的生命周期,包括挂载卷,容器日志记录,垃圾站以及其他相关事件。(同步Pod列表)
首先进行准入检查,主要包含两个关键的控制器:驱逐管理与预选检查
驱逐管理主要是根据当前的资源压力,检测对应的Pod是否容忍当前的资源压力;(串行执行一系列Pod同步处理器,每个处理器检查Pod是否应该运行在该节点上。状态值(Phase)就会变成PodFailed,在超过失败重试时间(spec.activeDeadlineSeconds)并且将该Pod从该节点上驱逐出去).
接下来Phase值由init容器和应用容器的状态共同来决定。因为目前容器还没有启动,容器被视为处于等待阶段,Phase值为Pending。
最后,Pod 的 Condition
字段由 Pod 内所有容器的状态决定。现在我们的容器还没有被容器运行时创建,所以 PodReady 的状态被设置为 False。
接下来运行一系列准入处理器来确保该 Pod 是否具有相应的权限(包括强制执行 AppArmor 配置文件和 NO_NEW_PRIVS),被准入控制器拒绝的 Pod 将一直保持 Pending
状态。
预选检查则是根据当前活跃的容器和当前节点的信息来检查是否满足当前Pod的基础运行环境,例如亲和性检查,同时如果当前的Pod的优先级特别高或者是静态Pod,则会尝试为其进行资源抢占,会按照QOS等级逐级来进行抢占从而满足其运行环境。
创建事件管道与容器管理主线程
kubelet接收到一个新创建的Pod首先会为其创建一个事件管道,并且启动一个容器管理的主线程消费管道里面的事件,并且会基于最后同步时间来等待当前kubelet中最新发生的事件(从本地的podCache中获取),如果是一个新建的Pod,则主要是通过PLEG中更新时间操作,广播的默认空状态来作为最新的状态。
同步最新状态
当从本地的podCache中获取到最新的状态信息和从事件源获取的Pod信息后,会结合当前当前statusManager和probeManager里面的Pod里面的容器状态来更新,从而获取当前感知到的最新的Pod状态。
准入控制检查
之前的准入检查是Pod运行的资源硬性限制(api-server)的检查,而这里的准入检查则是软状态即容器运行时和版本的一些软件运行环境检查,如果这里检查失败,则会将对应的容器状态设置为Blocked。
更新容器状态
在通过准入检查之后,会调用statusManager来进行POd最新状态的同步,此处可能会同步给apiserver。生成 PodStatus 之后(Pod 中的 status
字段),Kubelet 就会将它发送到 Pod 的状态管理器,该管理器的任务是通过 apiserver 异步更新 etcd 中的记录。
Cgroup配置
Kubelet 启动时指定了 cgroups-per-qos
参数,Kubelet 就会为该 Pod 创建 cgroup
并进行相应的资源限制。这是为了更方便地对 Pod 进行服务质量(QoS)管理。在更新完成状态之后会启动一个PodCOntainerManager主要作用则是为对应的Pod根据其QOS等级来进行Cgroup配置的更新
Pod基础运行环境准备
接下来kubelet会为Pod的创建准备基础的环境,包括Pod数据目录的创建、镜像秘钥的获取、等待volume挂载完成等操作
创建Pod的数据目录主要是创建 Pod运行所需要的Pod、插件、Volume目录,并且会通过Pod配置的镜像拉取秘钥生成秘钥信息,到此kubelet创建容器的工作就已经基本完成。
最后通过容器运行时接口(CRI)开始启动容器
四、ContainerRuntime
计算Pod容器变更
计算容器变更主要包括:Pod的sandbox是否变更、短声明周期容器、初始化容器是否完成、业务容器是否已经完成,相应的我们会得到一个几个对应的容器列表:需要被kill掉的容器列表、需要启动的容器列表,注意如果我们的初始化容器未完成,则不会进行将要运行的业务容器加入到需要启动的容器列表,可以看到这个地方是两个阶段。
初始化失败尝试终止
如果之前检测到之前的初始化容器失败,则会检查当前Pod的所有容器和sandbox关联的容器如果有在运行的容器,会全部进行Kill操作,并且等待操作完成
未知状态容器补偿
当一些Pod的容器已经运行,但是其状态仍然是Unknow的时候,在这个地方会进行统一的处理,全部kill掉,从而为接下来的重新启动做清理操作,此处和3.2只会进行一个分支,但核心的目标都是清理那些运行失败或者无法获取状态的容器。
pause
容器
创建 sandbox 时首先创建的是 pause
容器。pause 容器作为同一个 Pod 中所有其他容器的基础容器,它为 Pod 中的每个业务容器提供了大量的 Pod 级别资源,这些资源都是 Linux 命名空间(包括网络命名空间,IPC 命名空间和 PID 命名空间)。
pause 容器提供了一种方法来管理所有这些命名空间并允许业务容器共享它们,在同一个网络命名空间中的好处是:同一个 Pod 中的容器可以使用 localhost
来相互通信。pause 容器的第二个功能与 PID 命名空间的工作方式相关,在 PID 命名空间中,进程之间形成一个树状结构,一旦某个子进程由于父进程的错误而变成了“孤儿进程”,其便会被 init
进程进行收养并最终回收资源。
创建容器沙箱
在启动Pod的容器之前,首先会为其创建一个sandbox容器,当前Pod的所有容器都和Pod对应的sandbox共享同一个namespace从而共享一个namespace里面的资源,创建Sandbox比较复杂,后续会继续介绍。Kubelet 会通过 Remote Procedure Command
(RPC) 协议调用 RunPodSandbox。sandbox
用于描述一组容器,例如在 Kubernetes 中它表示的是 Pod。sandbox
是一个很宽泛的概念,所以对于其他没有使用容器的运行时仍然是有意义的(比如在一个基于 hypervisor
的运行时中,sandbox 可能指的就是虚拟机)
启动Pod相关容器
Pod的容器目前分为三大类:短生命周期容器、初始化容器、业务容器,启动顺序也是从左到右依次进行,如果对于的容器创建失败,则会通过backoff机制来延缓容器的创建,这里我们顺便介绍下containerRuntime启动容器的流程:
1、检查容器镜像是否拉取
镜像的拉取首先会进行对应容器镜像的拼接,然后将之前获取的拉取的秘钥信息和镜像信息,一起交给CRI运行时来进行底层容器镜像的拉取,当然这里也会各种backoff机制,从而避免频繁拉取失败影响kubelet的性能。
2、 创建容器配置
创建容器配置主要是为了容器的运行创建对应的配置数据,主要包括:Pod的主机名、域名、挂载的volume、configMap、secret、环境变量、挂载的设备信息、要挂载的目录信息、端口映射信息、根据环境生成执行的命令、日志目录等信息。
3、调用runtimeService完成容器的创建
调用runtimeService传递容器的配置信息,调用CRI,并且最终调用容器的创建接口完成容器的状态。Kubelet 向 PodSpec 中填充了一个 ContainerConfig
数据结构(在其中定义了命令,镜像,标签,挂载卷,设备,环境变量等待),然后通过 protobufs
发送给 CRI 接口。对于 Docker 来说,它会将这些信息反序列化并填充到自己的配置信息中,然后再发送给 Dockerd
守护进程。在这个过程中,它会将一些元数据标签(例如容器类型,日志路径,dandbox ID 等待)添加到容器中。
4、调用runtimeService启动容器
通过之前创建容器返回的容器ID,来进行对应的容器的启动,并且会为容器创建对应的日志目录。
5、执行容器的回调钩子(Hook)
如果容器配置了PostStart钩子,则会在此处进行对应钩子的执行,如果钩子的类型是Exec类则会调用CNI的EXec接口完成在容器内的执行。如果PostStart Hook启动的时间过长、挂起或者失败,容器将永远不会变成running的状态
五、运行沙箱容器
1、 拉取sandbox镜像
首先会拉取sandbox镜像
2、 创建沙箱容器
应用SecurityContext
在创建容器之前会先根据SecurityContext里面的配资信息,来进行容器SecurityContext的配置,主要包括特权等级、只读目录、运行账户组等信息
其余基础信息
除了应用SecurityContext还会进行断开、OOMScoreAdj、Cgroup驱动等信息的映射。
3、创建容器
根据上面的各种配置信息来进行容器的创建
5、创建checkpoint
checkpoint主要是将当前sandbox的配置信息进行序列化,并且存储其当前的快照信息
6、 启动sandbox容器
启动sandbox容器则会直接调用StartContainer同时传入之前创建容器返回的ID完成容器的启动,并且此时会重写覆盖容器的dns配置文件
7、 容器网络设置
容器的网络配置主要是调用CNI插件来完成容器网络的配置。允许不同的网络提供商为容器提供不同的网络实现。通过将 json 配置文件(默认在 /etc/cni/net.d
路径下)中的数据传送到相关的 CNI 二进制文件(默认在 /opt/cni/bin
路径下)中,cni 插件可以给 pause 容器配置相关的网络,然后 Pod 中其他的容器都使用 pause 容器的网络。CNI插件yi操作完毕会以json数据的格式返回给Kubelet。
跨主机容器网络通信(overlay网络插件)
文章参考:
集群架构 |Kubernetes
008.Pod创建流程
GitHub - jamiehannaford/what-happens-when-k8s: 🤔 What happens when I type kubectl run?
kubectl 创建 Pod 背后到底发生了什么? · 云原生实验室