Kubernetes - CRI 概述
2023-11-07 Cloud Native Kubernetes 读代码
概述
CRI(Container Runtime Interface, 容器运行时接口)是 Kubernetes 用于管理容器运行时和容器镜像的 gRPC 协议接口集合,kubelet 可以通过 CRI 来使用不同的容器运行时,而无需重新编译集群组件。
为了保证 CRI 可以正常工作,Kubernetes 集群中的每个节点都需要有一个实现了 CRI 的具体容器运行时 (例如 containerd),这样 kubelet 可以直接对 Pod 及其内部容器进行操作。
下面以 containerd 为例进行说明,containerd 是来自 Docker 的高级容器运行时,实现了 CRI 规范。
宏观接口模型
因为 Pod 是 Kubernetes 中应用运行的最小单位,此外容器运行时除了运行、重启、停止 Pod 操作外,还有容器对应的镜像拉取、删除等操作, 结合两者,我们可以给出一个粗糙的 CRI 模型示例图:
然后就可以直接将上述接口模型转化成伪代码 (Go 语言实现):
type CRI interface {
PullImage(name string) // 拉取容器镜像
RemoveImage(name string) // 删除容器镜像
StartPod(name string) // 启动 Pod
StopPod(name string) // 停止 Pod
RemovePod(name string) // 删除 Pod
StartContainer(name string) // 启动 Pod
StopContainer(name string) // 停止 Pod
RemoveContainer(name string) // 删除 Pod
}
// Containerd (模拟) 实现了 CRI 接口
type Containerd struct {
}
func (c *Containerd) PullImage(name string) {}
func (c *Containerd) RemoveImage(name string) {}
func (c *Containerd) StartPod(name string) {}
...
func (c *Containerd) RemoveContainer(name string) {}
// Rktlet (模拟) 实现了 CRI 接口
type Rktlet struct {
}
func (r *Rktlet) PullImage(name string) {}
func (r *Rktlet) RemoveImage(name string) {}
func (r *Rktlet) StartPod(name string) {}
...
func (d *Rktlet) RemoveContainer(name string) {}
// CRI-O 实现了 CRI 接口
type CRI-O struct {
}
func (c *CRI-O) PullImage(name string) {}
func (c *CRI-O) RemoveImage(name string) {}
func (c *CRI-O) StartPod(name string) {}
...
func (c *CRI-O) RemoveContainer(name string) {}
通过上面的伪代码,我们可以对 CRI 有一个基础的认识,下面我们一起看看 Kubernetes 中 CRI 的代码,学习一下成熟的方案和设计思路。
Kubernetes 中的 CRI
Kubernetes 中的 CRI 功能对应的源代码位于 Kubernetes 项目的 staging/src/k8s.io/cri-api
目录,本文以 Kubernetes v1.28
版本源代码进行分析。
CRI 定义和实现使用的是 gRPC 协议,在对应的 gRPC 定义文件中,可以看到两个 Service:
- RuntimeService: 定义了容器运行时的相关操作 API
- ImageService: 定义了容器镜像管理的相关操作 API
// cri-api/pkg/apis/runtime/v1/api.proto
service RuntimeService {
rpc Version(VersionRequest) returns (VersionResponse) {}
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}
rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}
rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}
...
}
service ImageService {
rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
...
}
上面的 Service 对应的 Go 代码如下:
// cri-api/pkg/apis/services.go
type RuntimeVersioner interface {
Version(ctx context.Context, apiVersion string) (*runtimeapi.VersionResponse, error)
}
// 容器管理接口
type ContainerManager interface {
CreateContainer(ctx context.Context, podSandboxID string, ...) (string, error)
StartContainer(ctx context.Context, containerID string) error
StopContainer(ctx context.Context, containerID string, timeout int64) error
RemoveContainer(ctx context.Context, containerID string) error
ListContainers(ctx context.Context, filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error)
...
}
// Pod 管理接口
type PodSandboxManager interface {
RunPodSandbox(ctx context.Context, ...) (string, error)
StopPodSandbox(pctx context.Context, odSandboxID string) error
RemovePodSandbox(ctx context.Context, podSandboxID string) error
...
}
// 通过内嵌上述几个接口,形成一个 CRI 主要接口
type RuntimeService interface {
RuntimeVersioner
ContainerManager
PodSandboxManager
UpdateRuntimeConfig(ctx context.Context, runtimeConfig *runtimeapi.RuntimeConfig) error
Status(ctx context.Context, verbose bool) (*runtimeapi.StatusResponse, error)
RuntimeConfig(ctx context.Context) (*runtimeapi.RuntimeConfigResponse, error)
}
// 镜像管理接口
type ImageManagerService interface {
PullImage(ctx context.Context, image *runtimeapi.ImageSpec, ...) (string, error)
RemoveImage(ctx context.Context, image *runtimeapi.ImageSpec) error
...
}
containerd
这里以 containerd 为例,看看其内部关于 CRI 接口的具体代码结构,时间所限,这里只留下相关代码骨架截图,具体的源代码不再进行分析。