docker架构速看(2)-镜像

news/2024/10/17 20:32:47/

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.jsonrepositories。

​ 打包文件存储了所有的层,所有层联合挂载即可提供镜像文件系统。值得注意的是各层之间是有父子关系的,子层是基于父层做了一些修改(文件,目录的增删改),在各层目录的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添加diffIDrootFS.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// 存储驱动,常用overlay2driver      graphdriver.DriveruseTarSplit bool// 记录了Docker存储的所有层,使用chainID索引,也可以说所有链。layerMap map[ChainID]*roLayerlayerL   sync.Mutexmounts map[string]*mountedLayermountL sync.Mutex// protect *RWLayer() methods from operating on the same name/idlocker *locker.Locker
}// readOnlyLayer 代表一个只读层
type roLayer struct {//从根layer到当前layer的链的 链IDchainID    ChainID// layer.tar文件 sha256diffID     DiffIDparent     *roLayercacheID    stringsize       int64LayerStore *LayerStoredescriptor distribution.DescriptorreferenceCount intreferences     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.RWMutexlss       LayerGetReleaserimages    map[ID]*imageMetafs        StoreBackenddigestSet *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.ppmy.cn/news/133702.html

相关文章

投影仪与计算机连接方式,电脑怎么接投影仪教程 简单三步教你搞定

在现如今的生活中,随着科技的不断进步人们在日常生活中几乎是离不开智能设备的陪伴。而近年智能投影更是火爆了朋友圈,以往在大众的印象中投影仪是露天影院和课堂上才会出现的设备。但如今却摇身一变走进了寻常人的家中,凭借百吋大屏和4K的超清画质成为了年轻人中的宠儿。 虽…

计算机无法投影,电脑无法识别投影仪-电脑为什么检测不到投影仪,应该怎么安装...

电脑无法识别投影仪-电脑为什么检测不到投影仪,应该怎么安装 投影仪销量排行

台式计算机与投影仪连接,投影仪与笔记本电脑台式电脑连接方法

投影仪与笔记本电脑台式电脑连接方法 2019年07月16日 14:50作者:黄页编辑:黄页 分享 若想用投影仪来观看笔记本电脑中的电影资源,该怎么操作?近年来,随着投影仪的广泛普及,越来越多的消费者都步入了“投影时代”&…

win7计算机无法连接投影仪,Win7电脑如何连接投影仪?投影仪连接电脑用什么线连接?...

Win7电脑如何连接投影仪?初次使用投影仪的用户不要着急,笔者通过了解得知在学校或工作上只需要直接连接打开电源就可以使用了,若是用户需要自己安装并连接投影仪则会比较麻烦,请阅读下文了解更多详情。 Win7电脑如何连接投影仪&am…

台式计算机连接投影仪无信号,投影仪连接电脑后无信号咋解决?

场景说明 陪孩子去上课,老师说投影仪连接笔记本后无信号,让帮着看看。简单的看了下HDMI线,看了看投影仪基本的设置,没弄好,由于要上课,就暂时没继续整理,但是回来后,我还是觉得要整理…

投影仪与计算机连接方式,投影仪和电脑怎么连接?简单的图文教程

若想用投影仪来观看笔记本电脑中的电影资源,该怎么操作?近年来,随着投影仪的广泛普及,越来越多的消费者都步入了“投影时代”,并将其投影功能运用于生活中的方方面面。下面,小编就教大家如何将电脑和投影仪进行连接&a…

android 投影pc,手机高清投影到PC方案

在平常的工作中有时需要进行App Demo或时要向与会者展示App功能,经常遇到由于手机的频幕小无法直接进行很好的演示,常用的方式就是将手机频幕投影到PC电脑再通过PC电脑连接投影仪的方式进行大频幕展示,下面将详细介绍这种方式的实用方案即手机…

高清投影仪什么品牌好?投影仪维修师傅透露了现在投影仪的品牌有哪些

选择投影仪之前,了解自己的真正需求,看自己更注重什么方面。所以高清投影仪什么品牌好?这个问题的重点在高清上,而且现在因为投影仪的市场各种品牌,各种类型的投影仪,错综复杂。不经过对比,很难…