k8s-容器运行时接口分析

1、为了什么需要 CRI ?

在 k8s v1.5 之前,Docker 作为第一代的容器运行时, kubelet 通过内嵌其中的 DockerShim 操作 Docker API 来操作容器。在 Kubernetes 1.5 中引入了 CRI,可以解耦了kubelet与容器运行时,该插件接口让 Kubernetes 无需重新编译就可以支持更多的容器运行时。

kubelet 将通过 CRI 接口来跟第三方容器运行时进行通信(如Rkt或Hyper等),来操作容器与镜像。CRI 包含 Protocol Buffers、gRPC API、以及运行库支持,还有尚在开发的标准规范和工具。

2、CRI 概览

实现 CRI 接口的容器运行时称为 CRI shim , 它作为 gRPC 服务端,监听在本地的 Unix socket 上;而 Kubelet 作为 gRPC 客户端来调用 CRI 接口,进行 Pod 、容器、镜像的生命周期管理。

Protocol Buffers API 包含两个 gRPC 服务 ImageServiceRuntimeService

  • ImageService 提供从仓库拉取镜像、查看和移除镜像等功能;
  • RuntimeService 提供对 Pod 和容器的生命周期管理、和容器的交互,创建和启动容器、删除容器等功能;

另外安装时,需要在每台宿主机上单独安装一个负责响应 CRI 的组件称作 CRI shim。顾名思义,CRI shim 的工作,就是扮演 kubelet 与容器项目之间的"垫片"(shim)。所以它的作用非常单一,那就是实现 CRI 规定的每个接口,然后把具体的 CRI 请求"翻译"成对后端容器项目的请求或者操作。

3、Pod 和容器的生命周期管理

go 复制代码
service RuntimeService {
    // Sandbox operations.
    rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}
    rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}
    rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}
    rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}
    rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
    // Container operations.
    rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}
    rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}
    rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}
    rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}
    rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}
    rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
    ...
}

Pod 由一组应用程序容器组成,位于具有资源约束的隔离环境中。在CRI中,这种环境称为PodSandbox。我们有意为容器运行时留出一些空间,以便根据它们在内部的运行方式以不同的方式解释 PodSandbox。对于基于虚拟机管理程序的运行时,PodSandbox 可能代表虚拟机。对于其他人,例如Docker,它可能是Linux命名空间。PodSandbox 必须遵守 Pod 资源规范。在 v1alpha1 API 中,这是通过启动 kubelet 创建并传递给运行时的 pod 级 cgroup 中的所有进程来实现的。

在启动 pod 之前,kubelet 会调用 RuntimeService.RunPodSandbox 来创建环境。这包括为 Pod 设置网络(例如,分配 IP)。一旦 PodSandbox 处于活动状态,就可以独立创建/启动/停止/删除单个容器。要删除 pod,kubelet 会在停止和移除 PodSandbox 之前停止并移除容器。

Kubelet 的职责在于通过 RPC 管理容器的生命周期,实现容器生命周期的钩子,以及存活和健康监测,执行 Pod 的重启策略等。

4、Exec/attach/port-forward 请求

go 复制代码
service RuntimeService {
    ...
    // ExecSync runs a command in a container synchronously.
    rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {}
    // Exec prepares a streaming endpoint to execute a command in the container.
    rpc Exec(ExecRequest) returns (ExecResponse) {}
    // Attach prepares a streaming endpoint to attach to a running container.
    rpc Attach(AttachRequest) returns (AttachResponse) {}
    // PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
    rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}
    ...
}

Kubernetes 为用户提供了和 Pod 以及其中的容器进行交互的能力(kubectl exec/attach/port-forward)。Kubelet 目前支持两种方式来支持这些功能:调用容器的本地方法,或者使用 Node 上的工具(例如 nsenter 以及 socat)。因为多数工具假设 Pod 利用 Linux namespace 做了隔离,因此使用 Node 上的工具并不是一个可移植的方案。在 CRI 中,我们显式的定义这些调用,让运行时可以做特定实现。

当下还有一个潜在问题是,Kubelet 处理所有的请求连接,所以他有成为 Node 通信瓶颈的可能。在设计 CRI 的时候,我们采纳了一些反馈,让运行时能够排除中间人。容器运行时可以启动一个单独的流服务器处理请求(还能为 Pod 的资源使用进行记录),并把服务器地址返回给 Kubelet。这样 Kubelet 就能反馈信息给 API Server,使之可以直接连接到容器运行时的服务,并连接到客户端。

相关推荐
Golinie36 分钟前
Docker底层原理浅析 | namespace+cgroups+文件系统
docker·容器·文件系统·cgroups·unionfs
樽酒ﻬق1 小时前
深度解析 Kubernetes 配置管理:如何安全使用 ConfigMap 和 Secret
安全·贪心算法·kubernetes
FreeBuf_1 小时前
新型恶意软件采用独特混淆技术劫持Docker镜像
运维·docker·容器
阿里云云原生2 小时前
API 即 MCP|Higress 发布 MCP Marketplace,加速存量 API 跨入 MCP 时代
云原生
李菠菜4 小时前
CentOS系统指定版本Docker与Docker-Compose在线安装教程
docker·容器·centos
爱吃龙利鱼5 小时前
rocky9.4部署k8s群集v1.28.2版本(containerd)(纯命令)
云原生·容器·kubernetes
Serverless社区6 小时前
MCP云托管最优解,揭秘国内最大MCP中文社区背后的运行时
阿里云·云原生·serverless·函数计算
掘金-我是哪吒6 小时前
分布式微服务系统架构第120集:专业WebSocket鉴权
分布式·websocket·微服务·云原生·架构
哈哈幸运7 小时前
Linux Awk 深度解析:10个生产级自动化与云原生场景
linux·云原生·自动化·awk·三剑客
lswzw7 小时前
Ubuntu K8s集群安全加固方案
安全·ubuntu·kubernetes