docker架构速看(2)-镜像

news2024/11/26 12:49:48

docker架构细看(2)-镜像

​ 上一章讲了Docker服务端的启动,这一章我们来看Docker中的镜像,需要对容器镜像分层存储,容器存储驱动有一定了解,参考

容器技术原理(一):从根本上认识容器镜像

​ Docker篇之镜像存储-OverlayFS和联合挂载技术

手撕docker文件结构 —— overlayFS,image,container文件结构详解

​ 先给出Docker中镜像存储相关架构图,接下来一一介绍架构图中各部分。
在这里插入图片描述

镜像打包存储格式

​ Docker镜像打包存储格式目前为Docker image v2,这是兼容OCI (Open Container Initiative) Image Specification的,所以Docker打包的镜像可以和符合OCI标准的容器运行时一起工作,Docker的容器运行时为containerd,比较常见的还有OCI-R。containerd可以加载 Docker打包的镜像运行,并导出OCI image spec格式打包的镜像。不过我用Docker加载OCI image spec格式打包的镜像,可以加载,但是缺失一些信息(如容器名等),另外也运行不了。

OCI image-spec

​ 好了,让我们先看一看Docker中镜像打包后的存储格式,使用docker image save -o path [image],可以将镜像打包为tar格式,这里保存了一个mysql:8的镜像,解包tar文件后,目录格式如下:

# 每个目录代表镜像的一层(layer),目录名为层的sha256
0a8df9b6baa9a...41cabb1147bae0d5366b52a/
	-json //该层设置
	-layer.tar //该层内容的归档文件
	-VERSION
33c2db18737d9...52df5fe7b59230a25ed51fd/
4261e1b5e194d...65082b7f399f202e3c6efa6/
5d011c2d54d47...6ab4401cb753e97d9a6e789/
7fb59873cff82...a51b0de05109ddbe2823f03/
8690ce19e0a42...5708640d81b533aa86d9e9e/
8e0a3834fee65...9aa228469d7164996b2b7d8/
a995401364fdc...edabbef15d8ca6f82cc556d/
ad8a3181e4cd9...5aab16b354a738cbbc75ae5/
f53d394a7abb4...46ac8911a23e5ad72cdf9cd/
ff702031cdc68...5d94addbe4dc88fa6629eda/

# 镜像设置文件,Docker可以通过该文件初始化一个image(描述镜像的结构体)
b939d379d46e3...14ad27b7d9da27f26774965610.json

# 镜像元数据,保存了镜像运行平台,镜像设置文件(即上面的Json文件),镜像包含哪些层(上面那些层目录中的layer.tar文件)
manifest.json

repositories。

​ 打包文件存储了所有的层,所有层联合挂载即可提供镜像文件系统。值得注意的是各层之间是有父子关系的,子层是基于父层做了一些修改(文件,目录的增删改),在各层目录的json文件中记录了该层的父层。而manifest中记录了所有层,且排列顺序为各层按父子关系连成链的顺序,另外manifest中还记录了镜像配置文件名称,镜像配置文件中记录了rootfs信息,其中记录了组成镜像每层的diffID,diffID为layer.tar内容的SHA256签名,也代表了该层与父层的差异。下图展示了层之间的关系,以及各种ID。这些ID在Docker中会用到。

在这里插入图片描述

​ 接下来,解析Docker加载镜像打包文件,创建新镜像的过程,主要加载函数为image/tarexport/load.go::Load(inTar io.ReadCloser),函数主要流程如下:

  1. 解包镜像打包文件,读取manifest.json文件,获取镜像配置文件路径和组成镜像所有层的路径。

  2. 读取镜像配置文件到img(存放镜像配置信息的结构体)中,读取其中Rootfs信息,获取所有层的diffID。

  3. Rootfs中的diffID和manifest.json中的层路径都是按父->子的关系排列好的,所以两者一一对应。之后就是注意检查每一层是否在Docker中已存储,若没有则从layer.tar加载到Docker存储中。其过程类似于不断扩展一条链,按拓扑序遍历层:

    1. 将该层diffID加入diffID列表,根据列表中diffID计算chainID。
    2. 查询LayerStore中是否存储有chainID代表的链。有,直接跳到d。没有进行c。
    3. 从layerPath加载缺失的层到LayerStore。
    4. 修改内存数据结构,回到a处理下一层
    for i, diffID := range img.RootFS.DiffIDs {
        // 获取layer.tar所在路径
        layerPath, err := safePath(tmpDir, m.Layers[i])
    	
        //不断加入diffID,计算当前已加入的层组成的链的chainID
        //查询docker是否已存有当前链(在这里等价于当前层)
    	r.Append(diffID)
    	newLayer, err := l.lss.Get(r.ChainID())
        
        //docker没有存储,则从layer.tar加载当前层
    	if err != nil {
            newLayer, err = l.loadLayer(layerPath)
    	}
    	
        //为内存中的img的rootfs添加diffID
       	rootFS.Append(diffID)
    }
    
  4. 根据镜像配置文件,在ImageStore内创建镜像文件,imgID为config文件的SHA256签名。

    		imgID, err := l.is.Create(config)
    
  5. todo: 镜像引用,标签

​ 以上就是Docker加载打包镜像的大体流程。从中可以看到几点,镜像实际上是由一条layer链和镜像配置文件组成的,值得注意的是,layer(层)是Docker镜像存储中基本单位,但链才是检索的基本单位,在Load函数中可以看到,是通过查询以层结尾的链是否存在来确定是否加载该层。另外在加载过程中也可以窥见LayerStore,ImageStore的部分内容,下面会详细介绍。

LayerStore 和 ImageStore

​ 先放上图,对两者有一个初步认识,也方便下面论述。
在这里插入图片描述

LayerStore

​ LayerStore是Docker中存储层的结构,当然,后面会讲到,实际它只存储了层的元数据,可以通过元数据索引到实际层存储位置。先看一下LayerStore内存数据结构。

type LayerStore struct {
	store       *fileMetadataStore
	// 存储驱动,常用overlay2
	driver      graphdriver.Driver
	useTarSplit bool
	
	// 记录了Docker存储的所有层,使用chainID索引,也可以说所有链。
	layerMap map[ChainID]*roLayer
	layerL   sync.Mutex

	mounts map[string]*mountedLayer
	mountL sync.Mutex

	// protect *RWLayer() methods from operating on the same name/id
	locker *locker.Locker
}

// readOnlyLayer 代表一个只读层
type roLayer struct {
	//从根layer到当前layer的链的 链ID
	chainID    ChainID
	// layer.tar文件 sha256
	diffID     DiffID
	parent     *roLayer
	cacheID    string
	size       int64
	LayerStore *LayerStore
	descriptor distribution.Descriptor

	referenceCount int
	references     map[Layer]struct{}
}

​ 接下来进一步解析上面load()函数中用到的loadlayer(layerPath string)函数。通过理解把layer加载进LayerStore的过程,进一步理解LayerStore。

  1. 调用LayerStore.driver(存储驱动),创建层的存储目录,目录名称为层的cacheID。

    ls.driver.Create(layer.cacheID);
    
  2. 将layer.tar解包,并写入步骤1创建目录中的diff目录下,即该层和父层的差异。

    ls.driver.ApplyDiff(layer.cacheID, parent, rdr)
    
  3. 将层的元数据存入LayerStore。

    storeLayer(layer)
    
  4. 修改内存数据结构

    ls.layerMap[layer.chainID] = layer
    

​ 光说可能不是很明白,我们可以看一看这些目录。我是用win基于wsl运行Docker,Docker数据存储位置为\\wsl$\docker-desktop-data\data\docker,Docker使用overlay2作为存储驱动。则其中image目录是ImageStore和LayerStore的存储位置,而overlay2目录则是层的实际存储位置。

image\overlay2\layerdb\sha256目录为层元数据存储目录,其下也包含一批目录,目录名称为层的chainID,其中存储着cacheID,diffID,parent等信息。
在这里插入图片描述

overlay2目录为层实际存储目录,其下也保存了一批目录,目录名称为cacheID。

在这里插入图片描述

​ 所以读取层的元数据信息,就可以获得parent层元数据目录,即可获取一整条链的元数据。同时也可以获取层的cacheID,从而锁定层的实际存储目录,这就将层的元数据和层关联了起来。

​ 层的元数据(LayerStore)已经和层(overlay2)关联起来了,接下来介绍image是如何和层关联起来的。

ImageStore

​ ImageStore就简单多了,实际上只存储了镜像的配置信息。

​ 以下为ImageStore相关数据结构,可以看到ImageStore在内存中记录了imageID和imageMeta的映射关系,而imageMeta中记录了组成镜像的层链中的最后一层,可以通过沿parent爬获取整条链。

type store struct {
	sync.RWMutex
	lss       LayerGetReleaser
	images    map[ID]*imageMeta
	fs        StoreBackend
	digestSet *digestset.Set
}

type imageMeta struct {
	layer    layer.Layer	
	children map[ID]struct{}
}

imageID: image config sha256
实际上镜像就是config,记录了层信息

// Image stores the image configuration
// 可以用镜像配置信息初始化
type Image struct {
	V1Image

	// Parent is the ID of the parent image.
	//
	// Depending on how the image was created, this field may be empty and
	// is only set for images that were built/created locally. This field
	// is empty if the image was pulled from an image registry.
	Parent ID `json:"parent,omitempty"` //nolint:govet

	// RootFS contains information about the image's RootFS, including the
	// layer IDs.
	RootFS  *RootFS   `json:"rootfs,omitempty"`
	History []History `json:"history,omitempty"`

	// OsVersion is the version of the Operating System the image is built to
	// run on (especially for Windows).
	OSVersion  string   `json:"os.version,omitempty"`
	OSFeatures []string `json:"os.features,omitempty"`

	// rawJSON caches the immutable JSON associated with this image.
	rawJSON []byte

	// computedID is the ID computed from the hash of the image config.
	// Not to be confused with the legacy V1 ID in V1Image.
	computedID ID
}

​ 存储的实际目录为image\overlay2\imagedb\content\sha256,目录下存储了一批文件,文件名称为对应镜像的imageID,也就是配置文件的sha256签名。文件中存储的即镜像配置信息和打包镜像文件中的镜像配置信息一致。

​ 最后,解释一下store初始化时,从磁盘读取已存储的镜像,如何通过镜像配置文件和组成镜像的层联系起来:镜像配置文件中记录有rootfs结构,其中记录了每一层的diffID,由此可以计算出组成镜像的层链的chainID,通过chainID。而存储层元信息的目录名称就是chainID,SO,镜像就和层联系起来了。

总结

​ 本章细看了Docker中的镜像存储,下一章将会细看容器部分。

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

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

相关文章

JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO

JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO 这是一篇总结文章。 文章目录 JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO1、学习技术的梳理1.1、jdbc的引入1.2、ResultSet[结果集]1.3API小结 2、数据库连接池3、Apache公司的DBUtils工具-BasicDAO…

在页面上画一个三角形然后点击内部触发事件

在HTML页面上创建一个canvas元素。使用JavaScript绘制三角形并将其填充。您可以使用canvas的API来绘制形状,例如beginPath()和lineTo()等。将一个事件监听器绑定到canvas元素上,以便在单击三角形时触发事件…

Rust每日一练(Leetday0017) 字母异位词分组、幂函数、N皇后

目录 49. 字母异位词分组 Group Anagrams 🌟🌟 50. 幂函数 Pow(x, n) 🌟🌟 51. N 皇后 N-Queens 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Rust每日一练 专栏 Golang每日…

chatgpt赋能python:Python代码输出666——让你快速入门Python编程

Python代码输出666——让你快速入门Python编程 引言 Python是一种面向对象、解释型计算机程序设计语言。Python广泛应用于Web开发、科学计算、数据分析及人工智能等领域,具有优雅、明确、简单的特点,而且学习起来十分容易。如果你是一名初学者&#xf…

chatgpt赋能python:Python修改信息的方法和应用

Python修改信息的方法和应用 Python是一种简单易学、高效、功能强大的编程语言,被广泛应用于各种领域。其中,修改信息是Python最常用的功能之一。无论是在数据分析、网络爬虫还是Web开发中,我们都需要通过Python对数据进行修改、更新和删除等…

Jetson nano之ROS入门 - - 机器人建模与仿真

文章目录 前言一、URDF建模1. URDF语法详解a. robotb. linkc. joint 2. URDF机器人建模实操 二、Xacro宏优化1、 Xacro宏语法详解2、 Xacro建模实操 三、Rviz与Gazebo仿真1、Gazebo集成URDF建模语法基础2、Gazebo集成URDF实操 总结 前言 在ROS中,机器人建模和仿真是…

Spring(四)基于xml的自动装配

自动装配:根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。 首先我们来熟悉三层架构的创建过程: 三层架构为controller层,service层,dao层。 在service层里面创建ser…

ShareX_一款好用的截图工具安装- Window

择心】向大家介绍and安装ShareX ShareX 免费、开源、轻量多区域截图无缝处理截图屏幕录制、文件共享各种实用工具(如拾色器,屏幕拾色器,尺子,图像编辑器,图像合并,图像分割器,生成图像缩略图&am…

mysql学+练

从开始到放弃! 开始 mysql -uroot -p123456退出命令 exit 或者 quit注释 # show databases; 单行注释 -- show databases; 单行注释 /* 多行注释 show databases; */DDL操作数据库 创建 /* 方式1 直接指定数据库名进行创建 */ CREATE DATABASE db1; /* 方式2 …

TerminalWorks TSPrint/TSScan/TSWebCam Crack

/ 远程桌面打印软件,TerminalWorks TSPrint Server/Client 从远程服务器打印到本地打印机的 简单方法 TSPrint 为您提供了一个简单的远程桌面打印软件,以及使 Windows 终端服务操作更容易的附加工具。有选择地启用或禁用功能,以便您可以完全…

Unity刚体

1、Dynamic:动态类型 受重力和力的影响移动和旋转 Material: 物理材质,在刚体上设置了物理材质,如果子物体有碰撞器但是没有设置材质则会通用刚体的物理材质 如果不设置,将使用在Physics 2D窗口中设置的默认材质(Physi…

python day1 函数

文章目录 前言一、python函数二、定义函数三、函数返回值四、实例五、变量作用域六、参数扩展1、默认值参数2、顺序参数,关键词参数3、可变参数4、可变关键词参数5、解包操作 七、函数类型的参数八、高阶函数九、匿名函数十、递归 前言 看深度学习的代码时&#xff…

SO21434 持续进行的网络安全(五)

目录 一、概要 二、目标 三、网络安全监控 3.1 输入 3.1.1 先决条件 3.1.2 进一步支持信息 3.2 要求和建议 3.3 输出 四、网络安全事件评估 4.1 输入 4.1.1 先决条件 4.1.2 进一步支持信息 4.2 要求和建议 4.3 输出 五、漏洞分析 5.1 输入 5.1.1 先决条件 5.…

Sentinel如何使用滑动窗口进行限流和降级,请看这篇文章分享

前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章将详细介绍如何Sentinel如何使用滑动窗口进行限流和降级,后续文章将详细介绍其他知识。 如果文章有什么需要改进的地方还请大佬不吝赐教👏…

环境感知算法——3.PSMNet基于Kitti数据集训练

1. 前言 PSMNet的核心思想是通过金字塔结构来捕捉不同尺度的特征信息,从而提高视差估计的精度。其亮点在于:(1)使用了金字塔形的卷积神经(SPP module)网络来提取不同尺度的特征信息;&#xff0…

【UR机械臂ros通讯前的示教器网络配置】

1. 前言 欢迎大家阅读2345VOR的博客【D435i深度相机YOLO V5结合实现目标检测】🥳🥳🥳2345VOR鹏鹏主页: 已获得CSDN《嵌入式领域优质创作者》称号👻👻👻,座右铭:脚踏实地&…

《新程序员005:开源深度指南新金融背后的科技力量》

各位CSDN的uu们你们好呀,今天,小雅兰来写书评啦,尽管再忙,也不能不读书,下面,就让小雅兰来带你们走进《新程序员005:开源深度指南&新金融背后的科技力量》这本书的世界吧!&#…

安科瑞医用隔离电源系统在浙江某医院项目中的应用

【摘要】介绍该三级乙等综合医院采用安科瑞医用隔离电源系统,使用分体配电柜安装方式,从而实现将TN系统转化为IT系统,以及系统绝缘情况监测。 【关键词】医用隔离电源系统;IT系统;绝缘情况监测;三级乙等综合…

华为OD机试真题 Java 实现【最差产品奖】【2023Q1 200分】

一、题目描述 A公司准备对他下面的N个产品评选最差奖,评选的方式是首先对每个产品进行评分,然后根据评分区间计算相邻几个产品中最差的产品。评选的标准是依次找到从当前产品开始前M个产品中最差的产品,请给出最差产品的评分序列。 二、输入描述 第一行,数字M,表示评分…

chatgpt赋能python:用Python编写软件能够带来哪些SEO优势?

用 Python 编写软件能够带来哪些 SEO 优势? Python 是一种高级编程语言,它的简洁性和易学性让 Python 成为现今最受欢迎的编程语言之一,适用于各种类型的编程任务,并且可以轻松地生成各种种类的软件。在构建网站和应用程序时&…