Kubernetes控制平面组件:Kubelet详解(三):CRI 容器运行时接口层

云原生学习路线导航页(持续更新中)

本文是 kubernetes 的控制面组件 kubelet 系列文章第三篇,主要讲解了CRI容器运行时接口相关内容,包括CRI是什么、核心功能、高级/低级运行时、如何选择运行时、cai-api/cri-client源码目录梳理等

  • 希望大家多多 点赞 关注 评论 收藏,作者会更有动力继续编写技术文章
  • 在前面两节中,我们已经对 kubelet 做了简要介绍,给出了kubelet架构,并对API接口层、核心功能层 做了介绍,本文将对 kubelet 的 CRI 容器运行时接口层 做详细讲解
  • API 接口层
    • kubelet API
    • cAdvisor API
    • 只读API
    • 健康检查 API
  • 核心功能层 ,可分为3个模块:
    • 核心管理模块:PLEG、cAdvisor、GPUManager、OOMWatcher、ProbeManager、DiskSpaceManager、EvictionManager
    • 运行时协调模块:syncLoop、PodWorker
    • 容器生命周期管理模块:StatusManager、VolumeManager、ImageGC、ContainerGC、ImageManager、CertificateManager
  • CRI 接口层
    • 容器执行引擎接口,作为grpc client 与真正的容器运行时(Dockershim/rkt/containerd)交互

1.容器运行时(CR)是什么

1.1.CRI 简介

  • CRI 是 Kubernetes 中用于与容器运行时(如 Docker、containerd、CRI-O 等)交互的接口标准。
  • CRI位于 kubelet 与 container 之间,它抽象了 Kubernetes 对容器生命周期的管理操作,允许 Kubernetes 支持多种容器运行时,而无需修改核心代码。
  • 发展历史
    • 在早期 Kubernetes 中,直接依赖 Docker 的 API(通过 dockershim),导致与 Docker 强耦合,难以支持其他容器运行时。
    • 为了解决这一问题,Kubernetes 在 1.5 版本 引入了 CRI,将容器运行时与 Kubernetes 核心组件解耦。

1.2.CRI 的核心功能

  • 如上图,kubelet会启动一个grpc-client,CRI的具体实现会启动一个grpc-server,二者通过protobuf协议传输数据,CRI的具体实现会通过调用oci runtime,实现对容器的操作

1.3.运行时的层级

  • 高层级运行时:dockershim、containerd、cri-o等
    • 负责启动grpc-server,对上层提供具体的容器、镜像服务,主要用于为kubelet提供cri接口实现
  • 低层级运行时:runc、kata-runtime、gVisor等
    • 负责容器真正的服务,比如设置ns、设置cgroups等
    • runc是容器运行时;kata-runtime是虚拟机运行时,通过启动一个虚拟机然后再虚机中再启动容器,隔离性好但是消耗大;gVisor是谷歌提供的安全运行时

1.4.容器运行时的选择

1.5.CRI 提供的方法

  • CRI 支持的rpc方法如下:
go 复制代码
// Runtime service defines the public APIs for remote container runtimes
service RuntimeService {
    rpc Version(VersionRequest) returns (VersionResponse) {}
    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) {}
    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) {}
    rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}
    rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}
    rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {}
    rpc Exec(ExecRequest) returns (ExecResponse) {}
    rpc Attach(AttachRequest) returns (AttachResponse) {}
    rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}
    rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse) {}
    rpc ListContainerStats(ListContainerStatsRequest) returns (ListContainerStatsResponse) {}
    rpc PodSandboxStats(PodSandboxStatsRequest) returns (PodSandboxStatsResponse) {}
    rpc ListPodSandboxStats(ListPodSandboxStatsRequest) returns (ListPodSandboxStatsResponse) {}
    rpc UpdateRuntimeConfig(UpdateRuntimeConfigRequest) returns (UpdateRuntimeConfigResponse) {}
    rpc Status(StatusRequest) returns (StatusResponse) {}
    rpc CheckpointContainer(CheckpointContainerRequest) returns (CheckpointContainerResponse) {}
    rpc GetContainerEvents(GetEventsRequest) returns (stream ContainerEventResponse) {}
    rpc ListMetricDescriptors(ListMetricDescriptorsRequest) returns (ListMetricDescriptorsResponse) {}
    rpc ListPodSandboxMetrics(ListPodSandboxMetricsRequest) returns (ListPodSandboxMetricsResponse) {}
    rpc RuntimeConfig(RuntimeConfigRequest) returns (RuntimeConfigResponse) {}
}

// ImageService defines the public APIs for managing images.
service ImageService {
    rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
    rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
    rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
    rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}
    rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
}

2.开源运行时的比较

通过对比开源运行时的实现,可以知道为什么kubenetes抛弃了docker

2.1.三种运行时对比

  • 三种运行时对比:docker、containerd、cri-o

  • docker 和 containerd 差异细节

  • 早期内置dockershim,kubelet调用dockershim后,需要依次经过dockershim、docker daemon、containerd之后,才能调用到oci runtime,非常厚重,而且docker本身包含很多kubernetes用不到的功能组件(如storage、networking等),由于代码内置在kubernetes中而不得不携带

  • 后来谷歌带头为cr定义了cri规范之后,docker不得不将container和image的部分抽取出来成为独立的containerd组件,去除了kubelet调用 dockershim和docker daemon 的两层,效率大大提升

  • cri-o是专为 Kubernetes 优化的轻量级运行时,仅实现 CRI 接口。支持 OCI 标准镜像,与 Docker 镜像兼容。

2.2.多种运行时性能比较

  • 运行时优劣对比

  • 虽然在架构上cri-o比containerd更加轻量,但是性能比较上发现containerd反而更优秀,且containerd是从docker切出来的,兼容性更好,维护人员技能过渡也更平滑,所以决定使用containerd作为运行时

3.CRI项目目录

3.1. cri-api

3.1.1.代码树状结构

  • github项目地址:https://github.com/kubernetes/cri-api
  • cri-api 是 Kubernetes 官方维护的 容器运行时接口(CRI) 的核心定义库,它定义了 Kubernetes 与容器运行时交互的 gRPC 协议和接口规范。
bash 复制代码
cri-api]# tree
.
├── code-of-conduct.md
├── CONTRIBUTING.md
├── doc.go
├── go.mod
├── go.sum
├── LICENSE
├── OWNERS
├── pkg
│   ├── apis
│   │   ├── runtime
│   │   │   └── v1
│   │   │       ├── api.pb.go  # 根据api.proto,使用protoc-gen-gogo工具生成的 Go 协议缓冲区代码,提供 gRPC 客户端和服务端的实现基础
│   │   │       ├── api.proto  # Protocol Buffers 接口定义文件(支持的grpc方法、请求体、响应体)
│   │   │       └── constants.go  # cri定义的所有常量
│   │   ├── services.go  # 
│   │   └── testing
│   │       ├── fake_image_service.go
│   │       ├── fake_runtime_service.go
│   │       └── utils.go
│   └── errors
│       ├── doc.go
│       ├── errors.go
│       └── errors_test.go
├── README.md
└── SECURITY_CONTACTS

6 directories, 19 files

3.1.2.核心代码文件

3.1.2.1.api.proto 接口定义文件
  • 作用:Protocol Buffers 接口定义文件,定义CRI 支持的grpc方法、请求体、响应体
go 复制代码
// Runtime service defines the public APIs for remote container runtimes
service RuntimeService {
    rpc Version(VersionRequest) returns (VersionResponse) {}
    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) {}
    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) {}
    rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}
    rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}
    rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {}
    rpc Exec(ExecRequest) returns (ExecResponse) {}
    rpc Attach(AttachRequest) returns (AttachResponse) {}
    rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}
    rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse) {}
    rpc ListContainerStats(ListContainerStatsRequest) returns (ListContainerStatsResponse) {}
    rpc PodSandboxStats(PodSandboxStatsRequest) returns (PodSandboxStatsResponse) {}
    rpc ListPodSandboxStats(ListPodSandboxStatsRequest) returns (ListPodSandboxStatsResponse) {}
    rpc UpdateRuntimeConfig(UpdateRuntimeConfigRequest) returns (UpdateRuntimeConfigResponse) {}
    rpc Status(StatusRequest) returns (StatusResponse) {}
    rpc CheckpointContainer(CheckpointContainerRequest) returns (CheckpointContainerResponse) {}
    rpc GetContainerEvents(GetEventsRequest) returns (stream ContainerEventResponse) {}
    rpc ListMetricDescriptors(ListMetricDescriptorsRequest) returns (ListMetricDescriptorsResponse) {}
    rpc ListPodSandboxMetrics(ListPodSandboxMetricsRequest) returns (ListPodSandboxMetricsResponse) {}
    rpc RuntimeConfig(RuntimeConfigRequest) returns (RuntimeConfigResponse) {}
}

// ImageService defines the public APIs for managing images.
service ImageService {
    rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
    rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
    rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
    rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}
    rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
}
2.1.2.1.api.pb.go:自动生成的 gRPC 协议代码
  • 作用

    • protoc 编译器根据 api.proto 文件自动生成,包含以下内容:
      • gRPC 服务端和客户端的底层代码 :例如 RuntimeServiceServerRuntimeServiceClient 接口,定义了如何通过 gRPC 协议发送/接收请求和响应。
      • 数据结构的序列化/反序列化逻辑 :将 Go 结构体(如 CreateContainerRequest)与 Protocol Buffers 二进制格式互转。
      • gRPC 通信的底层实现:包括网络传输、错误处理、元数据传递等。
  • 代码示例(片段):

    go 复制代码
    // 自动生成的 gRPC 客户端接口(用于调用容器运行时)
    type RuntimeServiceClient interface {
        RunPodSandbox(ctx context.Context, in *RunPodSandboxRequest, opts ...grpc.CallOption) (*RunPodSandboxResponse, error)
        CreateContainer(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error)
        // ...其他方法
    }
2.1.2.3.services.go:CRI Client接口层
  • 作用

    • 定义 CRI Client 具备的所有方法,供client调用
    • api.pb.go 中自动生成的gRPC 服务端和客户端的底层代码,实现了services.go的所有方法
  • 代码示例(片段):

    go 复制代码
    // 定义 Kubernetes 内部使用的 CRI 接口(与 gRPC 解耦)
    type RuntimeService interface {
        RunPodSandbox(config *v1.PodSandboxConfig) (string, error)
        CreateContainer(podSandboxID string, config *v1.ContainerConfig, sandboxConfig *v1.PodSandboxConfig) (string, error)
        // ...其他方法
    }
2.1.2.4.代码文件调用关系
复制代码
+------------------------+
|   Kubernetes 内部模块    | 使用 grpc-client 调用 services.go 的接口
+------------------------+
             ↓
+------------------------+
|      services.go        | 定义对外暴露的接口 及 方法
+------------------------+
             ↓
+------------------------+
|      api.pb.go          | 自动生成的 gRPC 通信层,实现了 services.go 所有接口方法
+------------------------+
             ↓
+------------------------+
|  容器运行时(如 containerd) | 通过 gRPC 接收请求
+------------------------+

3.2.cri-client

3.2.1.代码树状结构

bash 复制代码
cri-client]# tree
.
├── code-of-conduct.md
├── CONTRIBUTING.md
├── go.mod
├── go.sum
├── LICENSE
├── OWNERS
├── pkg
│   ├── doc.go
│   ├── fake
│   │   ├── doc.go
│   │   ├── endpoint.go
│   │   ├── endpoint_windows.go
│   │   ├── fake_image_service.go
│   │   └── fake_runtime.go
│   ├── internal
│   │   └── log.go
│   ├── logs
│   │   ├── logs.go
│   │   ├── logs_other.go
│   │   ├── logs_test.go
│   │   ├── logs_windows.go
│   │   ├── tail.go
│   │   └── tail_test.go
│   ├── remote_image.go
│   ├── remote_image_test.go
│   ├── remote_runtime.go
│   ├── remote_runtime_test.go
│   ├── util
│   │   ├── util_unix.go
│   │   ├── util_unix_test.go
│   │   ├── util_unsupported.go
│   │   ├── util_windows.go
│   │   └── util_windows_test.go
│   ├── utils.go
│   └── utils_test.go
├── README.md
└── SECURITY_CONTACTS

5 directories, 32 files

3.2.2.核心代码文件

3.2.2.1.remote_image.go 远程调用 image 的 client
  • 封装 cri-api api.pb.go 的 底层client,实现对 cri service.go 文件所有接口及方法的调用细节封装,使得使用方可以直接使用 cri-client 实现对 cri-api 的操作
  • 代码片段示例:
    • remoteImageService 携带 api.pb.go 的 ImageServiceClient
    • remoteImageService 实现了service.go 文件中 ImageManagerService 接口的 ListImages 方法。实现细节:使用ImageServiceClient发起grpc调用,list images
go 复制代码
// remoteImageService is a gRPC implementation of internalapi.ImageManagerService.
type remoteImageService struct {
	timeout     time.Duration
	imageClient runtimeapi.ImageServiceClient
	logger      *klog.Logger
}

// ListImages lists available images.
func (r *remoteImageService) ListImages(ctx context.Context, filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
	ctx, cancel := context.WithTimeout(ctx, r.timeout)
	defer cancel()

	return r.listImagesV1(ctx, filter)
}

func (r *remoteImageService) listImagesV1(ctx context.Context, filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
	resp, err := r.imageClient.ListImages(ctx, &runtimeapi.ListImagesRequest{
		Filter: filter,
	})
	if err != nil {
		r.logErr(err, "ListImages with filter from image service failed", "filter", filter)
		return nil, err
	}

	return resp.Images, nil
}
3.2.2.2.remote_runtime.go 远程调用 runtime 的 client
  • 封装 cri-api api.pb.go 的 底层client,实现对 cri service.go 文件所有接口及方法的调用细节封装,使得使用方可以直接使用 cri-client 实现对 cri-api 的操作
  • 代码片段示例:
    • remoteRuntimeService 携带 api.pb.go 的 RuntimeServiceClient
    • remoteRuntimeService 实现了service.go 文件中 RuntimeManagerService 接口的 Version 方法。实现细节:使用RuntimeServiceClient发起grpc调用,获取版本
go 复制代码
// remoteRuntimeService is a gRPC implementation of internalapi.RuntimeService.
type remoteRuntimeService struct {
	timeout       time.Duration
	runtimeClient runtimeapi.RuntimeServiceClient
	// Cache last per-container error message to reduce log spam
	logReduction *logreduction.LogReduction
	logger       *klog.Logger
}

// Version returns the runtime name, runtime version and runtime API version.
func (r *remoteRuntimeService) Version(ctx context.Context, apiVersion string) (*runtimeapi.VersionResponse, error) {
	r.log(10, "[RemoteRuntimeService] Version", "apiVersion", apiVersion, "timeout", r.timeout)

	ctx, cancel := context.WithTimeout(ctx, r.timeout)
	defer cancel()

	return r.versionV1(ctx, apiVersion)
}

func (r *remoteRuntimeService) versionV1(ctx context.Context, apiVersion string) (*runtimeapi.VersionResponse, error) {
	typedVersion, err := r.runtimeClient.Version(ctx, &runtimeapi.VersionRequest{
		Version: apiVersion,
	})
	if err != nil {
		r.logErr(err, "Version from runtime service failed")
		return nil, err
	}

	r.log(10, "[RemoteRuntimeService] Version Response", "apiVersion", typedVersion)

	if typedVersion.Version == "" || typedVersion.RuntimeName == "" || typedVersion.RuntimeApiVersion == "" || typedVersion.RuntimeVersion == "" {
		return nil, fmt.Errorf("not all fields are set in VersionResponse (%q)", *typedVersion)
	}

	return typedVersion, err
}
相关推荐
IvanCodes2 小时前
一、Docker:一场颠覆应用部署与运维的容器革命
docker·容器
栗子~~3 小时前
Milvus docker-compose 部署
docker·容器·milvus
椰汁菠萝4 小时前
ubuntu下免sudo执行docker
ubuntu·docker·免sudo
没有名字的小羊4 小时前
2.安装Docker
运维·docker·容器
xiezhr4 小时前
50 个常用 Docker 命令
运维·docker·容器
退役小学生呀10 天前
三、kubectl使用详解
云原生·容器·kubernetes·k8s
API开发10 天前
苹果芯片macOS安装版Homebrew(亲测) ,一键安装node、python、vscode等,比绿色软件还干净、无污染
vscode·python·docker·nodejs·openssl·brew·homebrew
程序员小潘10 天前
Kubernetes多容器Pod实战
云原生·容器·kubernetes
进击的码码码码N10 天前
Docker 镜像加速
运维·docker·容器