- kubernetes学习系列快捷链接
- Kubernetes架构原则和对象设计(一)
- Kubernetes架构原则和对象设计(二)
- Kubernetes架构原则和对象设计(三)
- Kubernetes控制平面组件:etcd(一)
- Kubernetes控制平面组件:etcd(二)
- Kubernetes控制平面组件:API Server详解(一)
- Kubernetes控制平面组件:API Server详解(二)
- Kubernetes控制平面组件:调度器Scheduler(一)
- Kubernetes控制平面组件:调度器Scheduler(二)
- Kubernetes控制平面组件:Controller Manager详解
- Kubernetes控制平面组件:Controller Manager 之 内置Controller详解
- Kubernetes控制平面组件:Controller Manager 之 NamespaceController 全方位讲解
- Kubernetes控制平面组件:Kubelet详解(一):API接口层介绍
- Kubernetes控制平面组件:Kubelet详解(二):核心功能层
本文是 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 核心组件解耦。
- 在早期 Kubernetes 中,直接依赖 Docker 的 API(通过
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方法如下:
// 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 协议和接口规范。
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方法、请求体、响应体
// 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 服务端和客户端的底层代码:例如
RuntimeServiceServer
和RuntimeServiceClient
接口,定义了如何通过 gRPC 协议发送/接收请求和响应。 - 数据结构的序列化/反序列化逻辑:将 Go 结构体(如
CreateContainerRequest
)与 Protocol Buffers 二进制格式互转。 - gRPC 通信的底层实现:包括网络传输、错误处理、元数据传递等。
- gRPC 服务端和客户端的底层代码:例如
- 由
代码示例(片段):
// 自动生成的 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
的所有方法
代码示例(片段):
// 定义 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.代码树状结构
- github项目地址:https://github.com/kubernetes/cri-client.git
- cri-client 是一个基于 cri-api 实现的 客户端工具或库,用于直接与容器运行时(如 containerd、CRI-O)交互
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,实现对 criservice.go
文件所有接口及方法的调用细节封装,使得使用方可以直接使用 cri-client 实现对 cri-api 的操作 - 代码片段示例:
- remoteImageService 携带
api.pb.go
的 ImageServiceClient - remoteImageService 实现了
service.go
文件中 ImageManagerService 接口的 ListImages 方法。实现细节:使用ImageServiceClient发起grpc调用,list images
- remoteImageService 携带
// 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,实现对 criservice.go
文件所有接口及方法的调用细节封装,使得使用方可以直接使用 cri-client 实现对 cri-api 的操作 - 代码片段示例:
- remoteRuntimeService 携带
api.pb.go
的 RuntimeServiceClient - remoteRuntimeService 实现了
service.go
文件中 RuntimeManagerService 接口的 Version 方法。实现细节:使用RuntimeServiceClient发起grpc调用,获取版本
- remoteRuntimeService 携带
// 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
}