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
}
相关推荐
斤斤计较5 小时前
Docker 环境安装(2025最新版)
运维·docker·容器
小锋学长生活大爆炸5 小时前
【教程】Docker方式本地部署Overleaf
运维·docker·容器
Hfc.5 小时前
ubuntu20.04系统搭建k8s1.28集群-docker作为容器运行时
ubuntu·kubernetes
欧先生^_^5 小时前
Docker 的各种网络模式
网络·docker·容器
掘金者说5 小时前
docker系列-DockerDesktop报错信息(Windows Hypervisor is not present)
运维·docker·容器
我不是秃头sheep10 小时前
Ubuntu 安装 Docker(镜像加速)完整教程
linux·ubuntu·docker
alden_ygq12 小时前
Kubernetes Horizontal Pod Autosscaler(HPA)核心机制解析
云原生·容器·kubernetes
dadeity16 小时前
Linux 离线安装 Docker 和 Docker Compose 最新版 的完整指南
linux·docker·docker-compose
东方未明010821 小时前
docker(一)前言:高并发服务端技术架构的演进
docker·性能优化·高并发·虚拟化·容器化