0. 前言

Kubernetes:kubelet 源码分析之创建 pod 流程 介绍了 kubelet 创建 pod 的流程,其中介绍了 kubelet 调用 runtime cri 接口创建 pod。containerd 源码分析:启动注册流程 介绍了 containerd 作为一种行业标准的高级运行时的启动注册流程。那么,kubelet 是怎么和 containerd 交互的呢? 本文会带着这个问题分析 kubeletcontainerd 的交互。

1. kubelet 和 containerd 交互

1.1 kubelet

Kubernetes:kubelet 源码分析之创建 pod 流程 分析,kubelet 调用 runtime cri 接口 /runtime.v1.RuntimeService/RunPodSandbox 创建 pod:

// kubernetes/vendor/k8s.io/cri-api/pkg/apis/runtime/v1/api.pb.go
func (c *runtimeServiceClient) RunPodSandbox(ctx context.Context, in *RunPodSandboxRequest, opts ...grpc.CallOption) (*RunPodSandboxResponse, error) {
out := new(RunPodSandboxResponse)
err := c.cc.Invoke(ctx, "/runtime.v1.RuntimeService/RunPodSandbox", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}

kubelet 是 runtime cri 接口调用的客户端,那么容器运行时作为服务端是怎么提供服务的呢?

1.2 kubelet 和 containerd 交互流程

在介绍容器运行时提供的服务之前先看下 cri 架构图

从图中可以看出,containerd 的 CRI 插件提供 image serviceruntime service,负责对接 kubelet runtime cri 的接口调用,并将调用转发给 containerd

继续,查看 containerd 的处理流程。

1.3 containerd

1.3.1 CRI Plugin

根据 cri 架构图, 从 CRI 插件入手查看 id 为 io.containerd.grpc.v1.criCRI 插件。

// containerd/plugins/cri/cri.go
func initCRIService(ic *plugin.InitContext) (interface{}, error) {
...
// Get runtime service.
criRuntimePlugin, err := ic.GetByID(plugins.CRIServicePlugin, "runtime")
if err != nil {
return nil, fmt.Errorf("unable to load CRI runtime service plugin dependency: %w", err)
} // Get image service.
criImagePlugin, err := ic.GetByID(plugins.CRIServicePlugin, "images")
if err != nil {
return nil, fmt.Errorf("unable to load CRI image service plugin dependency: %w", err)
}
...
service := &criGRPCServer{
RuntimeServiceServer: rs,
ImageServiceServer: is,
Closer: s, // TODO: Where is close run?
initializer: s,
} if config.DisableTCPService {
return service, nil
} return criGRPCServerWithTCP{service}, nil
}

插件返回的是 criGRPCServerWithTCP 对象。其中,包括 criGRPCServer 对象。criGRPCServer 对象实现了 grpcService 接口,将调用接口的 Register 注册对象到 grpc server。

// containerd/plugins/cri/cri.go
// Register registers all required services onto a specific grpc server.
// This is used by containerd cri plugin.
func (c *criGRPCServer) Register(s *grpc.Server) error {
return c.register(s)
} func (c *criGRPCServer) register(s *grpc.Server) error {
instrumented := instrument.NewService(c)
runtime.RegisterRuntimeServiceServer(s, instrumented)
runtime.RegisterImageServiceServer(s, instrumented)
return nil
}

criGRPCServer.register 中创建 instrumentedService 对象。

type instrumentedService struct {
c criService
} func NewService(c criService) GRPCServices {
return &instrumentedService{c: c}
}

instrumentedService 包括 criService 对象。实际提供 runtime serviceimage service 的就是 criService 对象。

以注册 runtime service 为例,查看 runtime.RegisterRuntimeServiceServer(s, instrumented) 做了什么。

// containerd/vendor/k8s.io/cri-api/pkg/apis/runtime/v1/api.pb.go
func RegisterRuntimeServiceServer(s *grpc.Server, srv RuntimeServiceServer) {
s.RegisterService(&_RuntimeService_serviceDesc, srv)
} var _RuntimeService_serviceDesc = grpc.ServiceDesc{
ServiceName: "runtime.v1.RuntimeService",
HandlerType: (*RuntimeServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Version",
Handler: _RuntimeService_Version_Handler,
},
{
MethodName: "RunPodSandbox",
Handler: _RuntimeService_RunPodSandbox_Handler,
},
...
},
...
}

可以看到,注册 instrumentedService 到 grpc 中,instrumentedService 提供 runtime.v1.RuntimeService 服务,包括 kubelet 调用的 RunPodSandbox 方法。

继续看,instrumentedServiceRunPodSandbox 做了什么。

// containerd/internal/cri/instrumented_service.go
func (in *instrumentedService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (res *runtime.RunPodSandboxResponse, err error) {
...
res, err = in.c.RunPodSandbox(ctrdutil.WithNamespace(ctx), r)
return res, errdefs.ToGRPC(err)
}

instrumentedService 调用 criGRPCServerRunPodSandbox 方法,实际执行的是 criGRPCServer 中的 criServer 对象:

// containerd/internal/cri/server/sandbox_run.go
func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
...
if err := c.sandboxService.CreateSandbox(ctx, sandboxInfo, sb.WithOptions(config), sb.WithNetNSPath(sandbox.NetNSPath)); err != nil {
return nil, fmt.Errorf("failed to create sandbox %q: %w", id, err)
} ctrl, err := c.sandboxService.StartSandbox(ctx, sandbox.Sandboxer, id)
if err != nil {
...
}
...
}

criService.RunPodSandbox 调用的是 sandboxServiceCreateSandboxStartSandbox 方法。

1.3.2 sanbox Plugin

sandboxServicecri.initCRIService 中实例化:

// containerd/plugins/cri/cri.go
func initCRIService(ic *plugin.InitContext) (interface{}, error) {
...
sbControllers, err := getSandboxControllers(ic)
if err != nil {
return nil, fmt.Errorf("failed to get sandbox controllers from plugins %v", err)
}
...
options := &server.CRIServiceOptions{
RuntimeService: criRuntimePlugin.(server.RuntimeService),
ImageService: criImagePlugin.(server.ImageService),
StreamingConfig: streamingConfig,
NRI: getNRIAPI(ic),
Client: client,
SandboxControllers: sbControllers,
}
...
s, rs, err := server.NewCRIService(options)
...
service := &criGRPCServer{
RuntimeServiceServer: rs,
ImageServiceServer: is,
Closer: s, // TODO: Where is close run?
initializer: s,
}
}

首先,getSandboxControllers 获得 sandbox controllers:

// containerd/plugins/cri/cri.go
func getSandboxControllers(ic *plugin.InitContext) (map[string]sandbox.Controller, error) {
// plugins.SandboxControllerPlugin: "io.containerd.sandbox.controller.v1"
sandboxers, err := ic.GetByType(plugins.SandboxControllerPlugin)
if err != nil {
return nil, err
}
...
return sc, nil
}

sandbox.Controller 是类型为 io.containerd.sandbox.controller.v1 的插件对象。将该对象作为 options 赋给 criServer

// containerd/internal/cri/server/service.go
func NewCRIService(options *CRIServiceOptions) (CRIService, runtime.RuntimeServiceServer, error) {
...
c := &criService{
...
sandboxService: newCriSandboxService(&config, options.SandboxControllers),
}
...
} func newCriSandboxService(config *criconfig.Config, sandboxers map[string]sandbox.Controller) *criSandboxService {
return &criSandboxService{
sandboxControllers: sandboxers,
config: config,
}
}

criService.sandboxService.CreateSandbox 调用的是插件对象 sanbox controllersCreateSandbox 方法,该方法最终调用的是 sandboxClientCreateSandbox

func (c *sandboxClient) CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*CreateSandboxResponse, error) {
out := new(CreateSandboxResponse)
err := c.cc.Invoke(ctx, "/containerd.runtime.sandbox.v1.Sandbox/CreateSandbox", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}

可以看到,在 sandboxClient.CreateSandbox 中调用 containerd 提供的 containerd cri 接口 /containerd.runtime.sandbox.v1.Sandbox/CreateSandbox,该接口用来创建 sandbox,即 pod。

1.4 创建 pod 流程

根据上述分析,这里画出 kubeletcontainerd 的交互流程图如下:

2. 小结

本文在前文 Kubernetes:kubelet 源码分析之创建 pod 流程containerd 源码分析:启动注册流程 的基础上,进一步分析从 kubeletcontainerd 的交互流程,打通了 kubeletcontainerd 这一步。


containerd 源码分析:kubelet 和 containerd 交互的更多相关文章

  1. ifconfig源码分析之与内核交互数据

    <ifconfig源码分析之与内核交互数据>本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝.转载,转载时请保持文档的完整性.参考资料:<Linux设备驱动 ...

  2. heapster源码分析——kubelet的api调用分析

    一.heapster简介 什么是Heapster? Heapster是容器集群监控和性能分析工具,天然的支持Kubernetes和CoreOS.Kubernetes有个出名的监控agent---cAd ...

  3. MongoDB源码分析——mongo与JavaScript交互

    mongo与JavaScript交互 源码版本为MongoDB 2.6分支     之前已经说过mongo是MongoDB提供的一个执行JavaScript脚本的客户端工具,执行js其实就是一个js和 ...

  4. kubelet源码分析——kubelet简介与启动

    kubelet是k8s集群中一个组件,其作为一个agent的角色分布在各个节点上,无论是master还是worker,功能繁多,逻辑复杂.主要功能有 节点状态同步:kublet给api-server同 ...

  5. kubelet源码分析——关闭Pod

    上一篇说到kublet如何启动一个pod,本篇讲述如何关闭一个Pod,引用一段来自官方文档介绍pod的生命周期的话 你使用 kubectl 工具手动删除某个特定的 Pod,而该 Pod 的体面终止限期 ...

  6. kubelet源码分析——监控Pod变更

    前言 前文介绍Pod无论是启动时还是关闭时,处理是由kubelet的主循环syncLoop开始执行逻辑,而syncLoop的入参是一条传递变更Pod的通道,显然syncLoop往后的逻辑属于消费者一方 ...

  7. scheduler源码分析——调度流程

    前言 当api-server处理完一个pod的创建请求后,此时可以通过kubectl把pod get出来,但是pod的状态是Pending.在这个Pod能运行在节点上之前,它还需要经过schedule ...

  8. scheduler源码分析——preempt抢占

    前言 之前探讨scheduler的调度流程时,提及过preempt抢占机制,它发生在预选调度失败的时候,当时由于篇幅限制就没有展开细说. 回顾一下抢占流程的主要逻辑在DefaultPreemption ...

  9. apiserver源码分析——启动流程

    前言 apiserver是k8s控制面的一个组件,在众多组件中唯一一个对接etcd,对外暴露http服务的形式为k8s中各种资源提供增删改查等服务.它是RESTful风格,每个资源的URI都会形如 / ...

  10. apiserver源码分析——处理请求

    前言 上一篇说道k8s-apiserver如何启动,本篇则介绍apiserver启动后,接收到客户端请求的处理流程.如下图所示 认证与授权一般系统都会使用到,认证是鉴别访问apiserver的请求方是 ...

随机推荐

  1. Seaborn时间线图和热图

    lineplot() 绘制与时间相关性的线图. sns.lineplot( x=None, y=None, hue=None, size=None, style=None, data=None, pa ...

  2. HMS Core Insights第九期直播预告——手语服务,助力沟通无障碍

    [导读] 你知道吗?全球有超5%的人群正在遭受听力损失的折磨.这些听障群体由于沟通不便,在日常生活中面对着很多的困难与挑战,建立沟通无障碍环境的需求十分迫切.随着科技的发展,越来越多的人们享受到技术进 ...

  3. HamronyOS自动化测试框架使用指南

      概述 为支撑HarmonyOS操作系统的自动化测试活动开展,我们提供了支持JS/TS语言的单元及UI测试框架,支持开发者针对应用接口进行单元测试,并且可基于UI操作进行UI自动化脚本的编写. 本指 ...

  4. c# 优化代码的一些规则——判断null值得不同写法[六]

    前言 先来看一个例子: 假设updated 是一个委托: if(updated!=null) { updated(); } 请问上面写法在多线程中安全吗?如果不安全会抛出什么异常呢? 正文 上面的答案 ...

  5. C++ 默认参数与引用传递:语法、用法及示例

    C++ 默认参数 默认参数概述 在 C++ 中,函数参数可以拥有默认值.这意味着,在调用函数时,如果省略了某个参数,那么将使用为该参数指定的默认值. 设置默认参数 默认参数值使用等号 = 符号进行设置 ...

  6. ES6中Module以及使用场景

    一.介绍 模块,(Module),是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体). 两个基本的特征:外部特征和内部特征 外部特征是指模块跟外部环境联系的接口(即其他 ...

  7. 鸿蒙HarmonyOS实战-ArkUI组件(Canvas)

    一.Canvas Canvas组件是一种图形渲染组件,它提供了一个画布(canvas),开发者可以在上面绘制各种图形.文本等.Canvas组件通常用于创建游戏.数据可视化等需要动态绘制图形的应用程序. ...

  8. OpenYurt:延伸原生 Kubernetes 到边缘场景下的落地实践

    简介: 随着云原生技术的逐步成熟,阿里云容器服务团队在具体落地实践过程中不断探索云原生技术的应用边界.同时随着物联网和 5G 的迅猛发展,传统的边缘计算架构已经不能满足业务发展的需要. 如何基于云原生 ...

  9. 性能提升40%!阿里云神龙大数据加速引擎获TPCx-BB世界排名第一

    ​简介:神龙大数据加速引擎,针对大数据常用组件,如Spark.Hadoop.Alluxio等,结合阿里云神龙架构的特性,进行软硬一体化优化,形成独一无二的性能优势,最终,使复杂SQL查询场景性能相比社 ...

  10. DataWorks搬站方案:Airflow作业迁移至DataWorks

    简介: DataWorks提供任务搬站功能,支持将开源调度引擎Oozie.Azkaban.Airflow的任务快速迁移至DataWorks.本文主要介绍如何将开源Airflow工作流调度引擎中的作业迁 ...