短视频seo优化排名,北京官网seo收费,个人怎样申请注册公司,鞍山百度做网站1. 概述
进入 K8s 的世界#xff0c;会发现有很多方便扩展的 Interface#xff0c;包括 CRI, CSI, CNI 等#xff0c;将这些接口抽象出来#xff0c;是为了更好的提供开放、扩展、规范等能力。
K8s CRI(Container Runtime Interface) 是 K8s 定义的一组与容器运行时进行交…1. 概述
进入 K8s 的世界会发现有很多方便扩展的 Interface包括 CRI, CSI, CNI 等将这些接口抽象出来是为了更好的提供开放、扩展、规范等能力。
K8s CRI(Container Runtime Interface) 是 K8s 定义的一组与容器运行时进行交互的接口用于将 K8s 平台与特定的容器运行时实现解耦。CRI 在 Kubernetes 1.5 中引入并充当 kubelet 和容器运行时之间的桥梁。目前实现了 CRI spec 的 Runtime 有 Docker Engine、containerd、CRI-O、Mirantis Container Runtime(Docker 企业版) 等。
2020 年K8s 宣布弃用 dockershim标志着容器运行时正式向 CRI 切换一方面是为了将 kubelet 核心主干代码与 Runtime 相关代码解耦便于更好的维护另一方面则是为了便于生态圈按 CRI spec 实现自己的运行时插件提供个性化的运行时扩展能力以满足对更多 Runtime 的支持提高 K8s 生态的开放性和扩展性。
本文将从 Docker Engine、Kubelet 启动、Pod 创建/删除、Container 创建/删除、CRI RPC 调用等核心流程对 CRI 实现机制进行了解析。 流程概览如下 2. 从 Docker 说起
2.1 Docker Engine
Docker Engine 是用来运行和管理容器的核心软件。通常人们会简单地将其代指为 Docker 或 Docker 平台。
Docker Engine 主要的组件构成Docker 客户端Docker Client、Docker 守护进程Docker daemon、Docker APIs、containerd 以及 runc它们共同负责容器的创建和运行。 2.2 OCI
OCIOpen Container Initiative开放容器计划是在 2015 年由 Docker、CoreOS 等公司共同成立的项目并由 Linux 基金会进行管理致力于 container runtime 标准的制定和 runc 的开发等工作。所谓 container runtime主要负责的是容器的生命周期的管理。OCI 主要分为容器运行时规范(runtime-spec) 和镜像规范(image-spec) 两部分runtime-spec 标准对容器的创建、删除、查看、状态等操作进行了定义image-spec 对镜像格式、打包(Bundle)、存储等进行了定义。
2.3 runc
runc是由 Docker 贡献的对于 OCI 标准的一个参考实现是一个可以用于创建和运行容器的 CLI(command-line interface) 工具。runc 直接与容器所依赖的 Cgroup/OS 等进行交互负责为容器配置 Cgroup/namespace 等启动容器所需的环境创建启动容器的相关进程。为了兼容 OCI 标准Docker 也做了架构调整。将容器运行时相关的程序从 Docker daemon 剥离出来形成了containerd。containerd 向 Docker 提供运行容器的 API二者通过 gRPC 进行交互。containerd 最后会通过 runc 来实际运行容器。 3. CRI
CRIContainer Runtime Interface容器运行时接口是 K8s 定义的一组与容器运行时进行交互的接口用于将 K8s 平台与特定的容器实现解耦。在 K8s 早期的版本中对于容器环境的支持是通过 Dockershim(hard code) 方式直接调用 Docker API 的后来为了支持更多的容器运行时和更精简的容器运行时K8s 在遵循 OCI 基础上提出了CRI。
3.1 dockershim
dockershim 是 Kubernetes 的一个组件主要目的是为了通过 CRI 操作 Docker。Kubernetes 在创建之初便采用Docker 作为它的默认容器进行时后续代码当中包含了很多对 Docker 相关的操作逻辑。后期 Kubernetes 为了能够做解耦兼容更多的容器进行时将操作 Docker 相关逻辑整体独立起来组成了 dockershim。
2020 年K8s 宣布弃用 dockershim标志着容器运行时正式向 CRI 切换以满足对更多 Runtime 的支持提高 K8s 生态的开放性和扩展性。
3.2 CRI shim
当前实现了 CRI 的 remote shim 有如下 containerd由 Docker 公司创建并且在 2017 年捐赠给了 CNCF2019 年毕业。 CRI-O基于 OCI 规范的作为 CRI 和 OCI 之间的一座桥梁。 Docker EngineDocker 运行时的支持由 cri-dockerd 进行实现。 Mirantis Container RuntimeDocker 企业版(Enterprise Edition) 运行时的支持由 Mirantis Container Runtime(MCR) 进行实现。
CRI shim 小结如下 3.3 RuntimeClass
RuntimeClass 是 v1.12 引入的新 API 对象用来支持多个容器运行时可通过 Pod 字段直接指定。 定义一个 RuntimeClass 如下对应的 CRI handler 即为目标容器运行时比如 containerd、crio
apiVersion: node.k8s.io/v1 # RuntimeClass is defined in the node.k8s.io API group
kind: RuntimeClass
metadata:name: myclass # The name the RuntimeClass will be referenced by# RuntimeClass is a non-namespaced resource
handler: myconfiguration # The name of the corresponding CRI configuration
在 Pod 中直接指定对应的 runtimeClassName 即可
apiVersion: v1
kind: Pod
metadata:name: mypod
spec:runtimeClassName: myclass# ...
4. Kubelet 启动
kubelet 在 Node 节点上负责 Pod 的创建、销毁、监控上报等核心流程通过 Cobra 命令行解析参数启动二进制可执行文件。
启动入口如下
// kubernetes/cmd/kubelet/kubelet.go
func main() {command : app.NewKubeletCommand()// kubelet uses a config file and does its own special// parsing of flags and that config file. It initializes// logging after it is done with that. Therefore it does// not use cli.Run like other, simpler commands.code : run(command)os.Exit(code)
}
接着一路往下进行初始化 cmd - Run - PreInitRuntimeService - RunKubelet - createAndInitKubelet - startKubelet - Run 其中 PreInitRuntimeService 会进一步初始化 CRI shim分别初始化 RuntimeService、ImageService 对容器运行时和镜像生命周期进行管理然后启动 gRPC CRI server 监听 client 请求进行具体的操作如 PodSandbox、Container 创建与删除。
kubelet 启动后会负责 Pod、Volume 事件的监听创建/删除对应的 Pod/Volume Node 资源监控与更新、Pod 健康探测(Probe) 及上报 当 Node 资源紧张时还负责 Pod Preemption(抢占) 与 Eviction(驱逐) Metrics 监控采集、Image/Container GC 工作等
其中Pod 事件流程图请看上面概述中的流程概览图。
5. Pod 创建/删除
K8s 中 Pod 的调谐采用 channel 生产者-消费者模型实现具体通过 PLEG(Pod Lifecycle Event Generator) 进行 Pod 生命周期事件管理。
// kubernetes/pkg/kubelet/pleg/pleg.go
// 通过 PLEG 进行 Pod 生命周期事件管理
type PodLifecycleEventGenerator interface {Start() // 通过 relist 获取所有 Pods 并计算事件类型Watch() chan *PodLifecycleEvent // 监听 eventChannel传递给下游消费者Healthy() (bool, error)
}
Pod 事件生产者(producer) - 相关代码
// kubernetes/pkg/kubelet/pleg/generic.go
// 生产者获取所有 Pods 列表计算出对应的事件类型进行 Sync
func (g *GenericPLEG) relist() {klog.V(5).InfoS(GenericPLEG: Relisting)...// 获取当前所有 Pods 列表podList, err : g.runtime.GetPods(true)if err ! nil {klog.ErrorS(err, GenericPLEG: Unable to retrieve pods)return}for pid : range g.podRecords {allContainers : getContainersFromPods(oldPod, pod)for _, container : range allContainers {// 计算事件类型running/exited/unknown/non-existentevents : computeEvents(oldPod, pod, amp;container.ID)for _, e : range events {updateEvents(eventsByPodID, e)}}}// 遍历所有事件for pid, events : range eventsByPodID {for i : range events {// Filter out events that are not reliable and no other components use yet.if events[i].Type ContainerChanged {continue}select {case g.eventChannel - events[i]: // 生产者发送到事件 channel对应监听的 goroutine 会消费default:metrics.PLEGDiscardEvents.Inc()klog.ErrorS(nil, Event channel is full, discard this relist() cycle event)}}}...
}
Pod 事件消费者(Consumer) - 相关代码
// kubernetes/pkg/kubelet/kubelet.go
// 消费者根据 channel 获取的各类事件进行 Pod Sync
func (kl *Kubelet) syncLoopIteration(configCh -chan kubetypes.PodUpdate, handler SyncHandler,syncCh -chan time.Time, housekeepingCh -chan time.Time, plegCh -chan *pleg.PodLifecycleEvent) bool {select {...// 消费者监听 plegCh 的事件case e : -plegCh:if e.Type pleg.ContainerStarted {// 更新容器的最后启动时间kl.lastContainerStartedTime.Add(e.ID, time.Now())}if isSyncPodWorthy(e) {if pod, ok : kl.podManager.GetPodByUID(e.ID); ok {klog.V(2).InfoS(SyncLoop (PLEG): event for pod, pod, klog.KObj(pod), event, e)// 进行相关 Pod 事件的 Synchandler.HandlePodSyncs([]*v1.Pod{pod})} else {// If the pod no longer exists, ignore the event.klog.V(4).InfoS(SyncLoop (PLEG): pod does not exist, ignore irrelevant event, event, e)}}// 容器销毁事件处理清除 Pod 内相关 Containerif e.Type pleg.ContainerDied {if containerID, ok : e.Data.(string); ok {kl.cleanUpContainersInPod(e.ID, containerID)}}...}return true
}
当 kubelet 监听到 Pod 事件时进行对应 Pod 的创建或删除流程如下 kubelet - Run - syncLoop - SyncPodCreate/Kill - UpdatePod - syncPod/syncTerminatingPod - dockershim gRPC - Pod running/teminated 6. Container 创建/删除
当 Pod-Sandbox 创建出来以后首先会创建基础容器 (infra-container也叫 pause 容器)通过 CNI 机制 配置 Pod 网络环境创建临时数据目录(存放 container logs)为下一步 Container 创建做好相关准备工作。
Container 目前分为三种类型 Ephemeral Container临时容器用于 debug 及排障所需的一次性容器。 Init Container初始化容器在正常业务容器之前、按序启动一般做一些准备工作。 Regular Container普通业务容器应用使用的主容器。
创建 Container 的过程主要有 PullImage - CreateContainer - StartContainer - PostStartHook - Container running 当 Pod 内所有 Container 都已创建created 都已启动started 至少有一个容器在运行中runningPod-phase 更新为 Running表示 Pod 正常运行。
Pod-Container 创建流程小结如下 7. CRI RPC 接口
CRI 标准规范接口包含了 ImageService、RuntimeService 两方面的接口 ImageService管理镜像的查询、拉取、删除、统计等操作 RuntimeService管理 PodSandbox 和容器的生命周期包括查询、创建、启动、删除、统计等操作另外还提供版本(Version)、执行命令(Exec)、端口转发(PortForward) 等接口能力 可以看到用户只要按照 CRI RPC 接口规范进行具体实现就可以实现自己的 Runtime 插件提高了 K8s 生态的高扩展性与灵活性。
8. 小结
本文通过分析 K8s 中 Kubelet 启动、Pod 创建/删除、Container 创建/删除、CRI RPC 调用等核心流程对 K8s CRI 实现机制进行了解析。通过源码、图文方式说明了相关流程逻辑以期更好的理解 K8s CRI 实现细节。
K8s CRI 经历了从 in-tree Dockershim 到 CRI remote-shim(out-of-tree) 的 迁移一方面是为了将 kubelet 核心主干代码与 Runtime 相关代码解耦便于更好维护另一方面则是为了便于生态圈按 CRI spec 实现自己的运行时插件提供个性化的运行时扩展能力以期达到容器生态圈的开放共赢。