【Linux】守护进程:containerd的使用教程

news2024/11/13 22:18:15

这里写目录标题

    • 前言
    • 一. ctr
      • 1.1 ctr CLI
      • 1.2 ctr 调试
    • 二、 创建 container
      • 2.1 进入 NewContainer
      • 2.2 ContainerService().Create

在这里插入图片描述
在这里插入图片描述

前言

介绍了 kubelet 通过 cri 接口和 containerd 交互的过程,containerd 源码分析:启动注册流程 介绍了 containerd 作为高级容器运行时的启动流程。通过这三篇文章熟悉了 kubelet 和 containerd 的行为,对于 containerd 如何通过 OCI 接口创建容器 container 并没有涉及。

image

本文将继续介绍 containerd 是如何创建容器 container 的。

一. ctr

在介绍创建容器前,首先简单介绍下 ctr。ctr 是 containerd 的命令行客户端,本文会通过 ctr 进行调试和分析。

1.1 ctr CLI

作为命令行工具 ctr 包括一系列和 containerd 交互的命令。主要命令如下:

COMMANDS:
   plugins, plugin            provides information about containerd plugins
   containers, c, container   manage containers
   images, image, i           manage images
   run                        run a container
   snapshots, snapshot        manage snapshots
   tasks, t, task             manage tasks
   install                    install a new package
   oci                        OCI tools
   shim                       interact with a shim directly
containers|c|container

不同与 Kubernetes 层面的 container,这里 ctr 命令管理的 containers 实际是管理存储在 boltDB 中的 container metadata。

创建 container:

# ctr c create docker.io/library/nginx:alpine nginx1
# ctr c ls
CONTAINER    IMAGE                             RUNTIME
nginx1       docker.io/library/nginx:alpine    io.containerd.runc.v2

通过 boltbrowser 查看 boltDB 存储的 container metadata,container metadata 存储在目录 /var/lib/containerd/io.containerd.metadata.v1.bolt。

image

tasks|t|task

task 是实际启动容器进程的命令,ctr task start 根据创建的 container 启动容器:

# ctr t start nginx1
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
...

run

ctr 的 run 命令,实际是 ctr c create 和 ctr t start 命令的组合。

接下来,使用 ctr run 命令做为调试参数分析完整的创建 container 容器的流程。

1.2 ctr 调试

ctr 代码集中在 containerd 项目中,配置 ctr 的调试参数:

{
   "version": "0.2.0",
   "configurations": [
      {
         "name": "ctr",
         "type": "go",
         "request": "launch",
         "mode": "auto",
         "program": "${fileDirname}",
         "args": ["run", "docker.io/library/nginx:alpine", "nginx1"]
      }
   ]
}

调试 ctr:

image

进入 run.Command 看其中做了什么。

// containerd/cmd/ctr/commands/run/run.go
// Command runs a container
var Command = &cli.Command{
	Name:      "run",
	Usage:     "Run a container",
   ...
   Action: func(context *cli.Context) error {
      ...
      // step1: 创建访问 containerd 的 client
      client, ctx, cancel, err := commands.NewClient(context)
		if err != nil {
			return err
		}
		defer cancel()

      // step2: 创建 container
      container, err := NewContainer(ctx, client, context)
		if err != nil {
			return err
		}
      ...

      opts := tasks.GetNewTaskOpts(context)
		ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))}
      // step3: 创建 task
		task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), context.String("log-uri"), ioOpts, opts...)
		if err != nil {
			return err
		}

      ...
      // step4: 启动 task
      if err := task.Start(ctx); err != nil {
			return err
		}
      ...
   }
}

在 NewContainer 中根据 client 创建 container。接着根据 container 创建 task,然后启动该 task 来启动容器。

二、 创建 container

2.1 进入 NewContainer

// containerd/cmd/ctr/commands/run/run_unix.go
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
   ...
   return client.NewContainer(ctx, id, cOpts...)
}

// containerd/client/client.go
func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
   ...
   container := containers.Container{
		ID: id,
		Runtime: containers.RuntimeInfo{
			Name: c.runtime,
		},
	}
   ...
   // 调用 containerd 接口创建 container
   r, err := c.ContainerService().Create(ctx, container)
	if err != nil {
		return nil, err
	}
	return containerFromRecord(c, r), nil
}

2.2 ContainerService().Create

// containerd/client/containerstore.go
func (r *remoteContainers) Create(ctx context.Context, container containers.Container) (containers.Container, error) {
	created, err := r.client.Create(ctx, &containersapi.CreateContainerRequest{
		Container: containerToProto(&container),
	})
	if err != nil {
		return containers.Container{}, errdefs.FromGRPC(err)
	}

	return containerFromProto(created.Container), nil
}

// containerd/api/services/containers/v1/containers_grpc.pb.go
func (c *containersClient) Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) {
	out := new(CreateContainerResponse)
	err := c.cc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Create", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

调用 /containerd.services.containers.v1.Containers/Create grpc 接口创建 container。container 并不是容器进程,而是存储在数据库中的 container metadata。

/containerd.services.containers.v1.Containers/Create 是由 containerd 的 io.containerd.grpc.v1.containers 插件提供的服务:

// containerd/plugins/services/service.go
func (s *service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error) {
	return s.local.Create(ctx, req)
}

插件实例调用 local 对象的 Create 方法创建 container。查看 local 对象具体指的什么。

// containerd/plugins/services/service.go
func init() {
	registry.Register(&plugin.Registration{
		Type: plugins.GRPCPlugin,
		ID:   "containers",
		Requires: []plugin.Type{
			plugins.ServicePlugin,
		},
		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
         // plugins.ServicePlugin:io.containerd.service.v1
         // services.ContainersService:containers-service
			i, err := ic.GetByID(plugins.ServicePlugin, services.ContainersService)
			if err != nil {
				return nil, err
			}
			return &service{local: i.(api.ContainersClient)}, nil
		},
	})
}

local 对象是 containerd 的 io.containerd.service.v1.containers-service 插件的实例。查看该实例的 Create 方法。

// containerd/plugins/services/containers/local.go
func (l *local) Create(ctx context.Context, req *api.CreateContainerRequest, _ ...grpc.CallOption) (*api.CreateContainerResponse, error) {
	var resp api.CreateContainerResponse

	if err := l.withStoreUpdate(ctx, func(ctx context.Context) error {
		container := containerFromProto(req.Container)

		created, err := l.Store.Create(ctx, container)
		if err != nil {
			return err
		}

		resp.Container = containerToProto(&created)

		return nil
	}); err != nil {
		return &resp, errdefs.ToGRPC(err)
	}
	...

	return &resp, nil
}

local.Create 调用 local.withStoreUpdate 方法创建 container。

// containerd/plugins/services/containers/local.go
func (l *local) withStoreUpdate(ctx context.Context, fn func(ctx context.Context) error) error {
	return l.db.Update(l.withStore(ctx, fn))
}

local.withStoreUpdate 调用 db 对象的 Update 方法创建 container。

// containerd/plugins/services/containers/local.go
func init() {
	registry.Register(&plugin.Registration{
		...
		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
			m, err := ic.GetSingle(plugins.MetadataPlugin)
			if err != nil {
				return nil, err
			}
			ep, err := ic.GetSingle(plugins.EventPlugin)
			if err != nil {
				return nil, err
			}

			db := m.(*metadata.DB)
			return &local{
				Store:     metadata.NewContainerStore(db),
				db:        db,
				publisher: ep.(events.Publisher),
			}, nil
		},
	})
}

db 对象是 io.containerd.metadata.v1 插件的实例,该插件通过 boltDB 提供 metadata 存储服务。

metadata 插件实际调用的是匿名函数 fn 的内容,在 fn 中通过 l.Store.Create(ctx, container) 将 container 的 metadata 信息注册到 boltDB 数据库中。

创建 container 的过程实际是将 container 信息注册到 boltDB 的过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2042107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

屏幕防拍照技术:防止屏幕被拍照的方法有哪些?答案超乎想象!

“防身立命,安内攘外。” 在数字化时代,信息安全已成为企业乃至个人不可忽视的重要议题。 屏幕作为信息展示的主要窗口,其安全性尤为关键。 然而,随着拍照技术的日益普及与精进,屏幕内容被轻易捕捉并泄露的风险也随…

暗区突围辅助攻略:VMOS云手机硬核辅助!农场地图攻略大全!

在《暗区突围》中,了解地图和撤离点的分布对于游戏的胜利至关重要。使用VMOS云手机进行游戏辅助,可以为你带来显著的优势。VMOS云手机专门针对《暗区突围》进行了定制,内置游戏安装包,无需重新下载安装游戏。它能够实现24小时云端…

您看这篇就够了:ComfyUI 新手指南

欢迎来到 ComfyUI 的神奇世界,这是 Stable Diffusion 的一个强大而灵活的图形用户界面。无论你是数字艺术的新手,还是希望将你的图像创作提升到新高度的老手,ComfyUI 都能满足你的需求。在这篇全面的指南中,我们将带你穿越 ComfyU…

初识C++ · IO流

前言: IO流,启动!因笔者对于IO流的理解不是很深刻,所以这里进行简单的介绍即可。 1 IO流 IO流是我们从学习C开始就一直会使用的东西,我们先了解一下CIO流的一套继承体系: 整个IO体系的基类是ios_base&…

ArkTs基础语法

ArkTs基础语法 声明变量声明常量声明自动类型推断 类型基础类型NumberBooleanString 引用类型VoidObjectArrayEnumUnionAliases 语句if语句switch语句条件表达式 ?:for语句for of语句while语句do while语句breakcontinuethrow和try catch finally语句 函数函数声明可选参数Res…

MySQL中的锁事

一、概述 锁是计算机在执行多线程或线程时用于并发访问同一共享资源时的同步机制,MySQL中的锁是在服务器层或者存储引擎层实现的,保证了数据访问的一致性与有效性。 事务的隔离性是由的锁来实现。 二、MySQL并发事务访问的问题 我们已经知道事务并发…

day-39 矩阵中的最大得分

思路 动态规划。利用一个二维数组记录对应位置可以达到的最高得分(位置(0,0)不可能),然后找出最大值即可(如果除了(0,0)上的值其他值都一样,则返回任意一个即可&#xff…

Python 3 入门基础知识 之数据容器及用法【2】 推荐

前面关于python的下载安装、如何定义变量,基本的数据类型,以及if条件语句、for循环语句,部分运算都进行了梳理总结。参考:Python 3 入门基础知识【1】数据类型 安装下载 推荐-CSDN博客 这里回顾一下python的数据容器基础知识&…

智能换热:图扑智慧供热可视化管理平台

图扑搭建智慧供热可视化管理平台,通过实时监控和数据分析提升运营效率,实现智能化管理,保障系统稳定与高效运行。

深挖Redis分布式缓存:你还在为缓存架构感到困惑吗?灵办AI为你揭开文献背后的秘密!

文章目录 1 灵办AI插件2 翻译~外文文献3 解释~文献标题分析4 文档解析~文献总结5 搜索~全网搜索总结6 总体评价~文献代码分析总结 本文将引用 Research and Application of Distributed Cache Based on Redis [1] 外文文献解读为案例进行剥削,进而提高对 Redis的分布…

相机光学(三十五)——三刺激值

0.参考链接 [1]三刺激值 [2]色差仪CIERGB和CIEXYZ光谱三刺激值区别 1.三刺激值的由来 根据杨-亥姆霍兹的三原色理论,色的感觉是由于三种原色光刺激的综合结果。在红、绿,蓝三原色系统中,红。绿、蓝的刺激量分别以R、G、B表示之。由于从实际…

安装开源软件ChatALL(齐叨)来聚合各大人工智能工具

安装开源软件ChatALL(齐叨)来聚合各大人工智能工具 前言 如果你跟我一样无论遇到什么问题都要询问人工智能,并且至少同时用4-5个公司的人工智能,选择一个最中意的结果 以前我都会用Chrome浏览器打开4个窗口,每个问题…

c语言中比较特殊的输入格式

目录 一.%[ ] 格式说明符 1.基本用法 (1)读取字母字符: (2)读取数字字符: (3)读取所有字符直到遇到空格: (4)读取直到换行符: 2.使用范围和组合: 3.^ 取反操作 4.注意事项 (1). 字符范围的正确表示 (2). 避免字符集中的特殊字符冲突 (3).避免空字符集 (4). 输入长…

C++竞赛初阶L1-11-第五单元-for循环(25~26课)510: T454422 均值

题目内容 给出一组样本数据,计算其均值。 输入格式 输入有两行,第一行包含一个整数,表示样本容量 n。 第二行包含 n 个浮点数 ai​,代表各个样本数据。 输出格式 输出一行,包含一个浮点数,表示均值。 …

TikTok爆款盘点-哪些产品会更受欢迎

海外短视频兴起,TikTok成为了国内企业出海最成功的APP。TikTok现在不仅是一个娱乐社交平台,也是一个强大的电商平台。如果出海卖家在选品上好好下功夫,利用TikTok这个营销工具,在海外也能获得不小的收益。 那么,TikTok…

网络协议十 应用层 SPDY / HTTP2 / QUIC / HTTP3

一 HTTP1.1 的不足 二 SPDY 协议 三 HTTP2 也只是需要在 server 端配置,不需要改动 服务器的代码。 HTTP2 可以看成是 将 SPDY 和 HTTP 融合在一起的改动 上面image sprites 的概念 HTTP2的问题--对头阻塞 和 握手延迟问题 引出的 QUIC 技术 由于传输层用的 TC…

node20+版本下hexo部署报错失败的解决办法

最近升级了node.js的版本,升到了最新的稳定版本v20.16.0,结果发现在该版本下hexo部署报错失败。本文记录了node20版本下hexo部署报错失败的解决办法。 一、报错信息 执行hexo的deploy部署命令 hexo d具体报错信息如下 INFO Deploying: git INFO Cl…

【大模型系列】更像人类行为的爬虫框架

随着大规模模型技术的兴起,我们可以看到百模大战、各种智能体、百花齐放的应用场景,那么作为一名前端开发者,以前端的视角,我们应当如何积极做好技术储备,开拓技术视野,在智能体时代保持一定的竞争力呢&…

webpack打包可视化分析之--webpack-bundle-analyzer

在开发一些项目的时候,有时候有些旧的框架项目用的webpacke打包慢,打包出来的包文件大,然而我们想要对它进行优化分析,有些旧的项目可能在不断迭代的过程中,有些模块功能改造或者有些需求变化,有些新需求模…

python的warnings.filterwarnings(“ignore“)

在python中运行代码经常会遇到的情况是——代码可以正常运行但是会提示警告,如果想要去掉这些警告,可以使用python的warnings.filterwarnings(“ignore”)函数。 代码: import warnings warnings.filterwarnings(ignore)看一下这个函数&…