Docker镜像技术剖析

目录

  • 1、概述
    • 1.1 什么是镜像?
    • 1.2 联合文件系统UnionFS
    • 1.3 bootfs和rootfs
    • 1.4 镜像结构
    • 1.5 镜像的主要技术特点
      • 1.5.1 镜像分层技术
      • 1.5.2 写时复制(copy-on-write)策略
      • 1.5.3 内容寻址存储(content-addressable storage)机制
      • 1.5.4 联合挂载(union mount)技术
  • 2.机制原理

1、概述

1.1 什么是镜像?

    Docker镜像是一种轻量级,可以执行的独立软件包,用于打包软件运行环境,或者基于某种运行环境的软件。一个镜像内包含了某个软件所需要的所有内容,包括了代码,运行时,库,环境变量和配置文件。镜像可以理解为模板,对容器来说开箱即用。

1.2 联合文件系统UnionFS

    它是一种分层,轻量级,并且高性能的文件系统。它将对文件系统的修改作为一次提交来一层层的在原先的文件系统中进行叠加;同时它可以将不同的目录挂载到同一个虚拟文件系统下(unite several directories into to a single virtual filesystem)。
    Union文件系统是Docker镜像的基础。镜像可以通过分层进行继承,基于基础镜像,可以制作各种具体的应用镜像。
    一个镜像文件实际上是由多个文件系统组成,但是镜像为我们提供了一个统一的视角,让我们凭直觉认为:我们操作的是一个整体的文件系统。
    因此,当我们从阿里云仓库中下载centos和tomcat镜像时,并不是下载了一个镜像,而是同时下载了很多分镜像,然后向我们展现了最外层的镜像ID。

1.3 bootfs和rootfs

    通常而言,Linux的操作系统由两类文件系统组成:bootfs(boot file system)和rootfs(root file system),它们分别对应着系统内核与根目录文件。bootfs层主要为系统内核文件,这层的内容是无法修改的。当我们的系统在启动时会加载bootfs,当加载完成后整个内核都会存到内存中,然后系统会将bootfs卸载掉。
    而rootfs层则包含了系统中常见的目录和文件,如/bin,/etc,/proc等等。

在这里插入图片描述
     bootfs(boot file system)主要包含 bootloader 和 Kernel , bootloader 主要是引导加 kernel, Linux刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs 。这一层与我们典型的 Linux/Unix系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs 。
    rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中 的 /dev,/proc,/bin,/etc 等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos 等等。平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?
    对于精简的 OS,rootfs 可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用宿主机的kernel,自己只需要提供 rootfs 就可以了。由此可见对于不同的Linux发行版, bootfs 基本是一致的,rootfs会有差別,因此不同的发行版可以共用 bootfs。
    Docker的镜像技术可以使用宿主机的bootfs层,这使得镜像本身只需要封装rootfs层所需要的文件和工具即可。因此,镜像可以根据需要进行定制化封装,减少占用的存储空间,如部分极精简的镜像只有几MB大小。
    在不同Linux发行版本中,它们之间的主要区别在于rootfs层,比如ubuntu使用apt管理软件,而Centos使用yum方式。而在内核层面,两者的差别并不大。因此,我们可以在一台主机上同时支持不同Linux系统的镜像而不出现报错,如同时启动Centos和Ubuntu的容器。
    但需要注意的是,不管容器使用什么系统的镜像,实际的内核版本都与镜像无关,都为宿主机的内核。如ubuntu16.04 的容器跑在Centos7.x的宿主机上,虽然ubuntu的内核版本是4.x.x,但我们在容器中会看到内核为centos 7.x 的内核,即 3.x.x。如果是对内核版本的要求的程序,可能会因此受到影响。

1.4 镜像结构

在这里插入图片描述
    docker的分层镜像结构如图所示,镜像的最底层必须是一个启动文件系统(bootfs)的镜像层。bootfs的上层镜像称为根镜像(rootfs)或者基础镜像(Base Image),它一般是操作系统,比如centos、debian或者Ubuntu。
    用户的镜像必须构建在基础镜像之上。如图所示, emacs镜像层就是在基础镜像上安装emacs创建出来的镜像,在此基础上安装apache又创建了新的镜像层。利用这个新的镜像层启动的容器里运行的是一个已经安装好emacs和apache的Debian系统。

1.5 镜像的主要技术特点

    为了更好的理解docker镜像的结构,下面介绍一下docker镜像设计上的关键技术。

1.5.1 镜像分层技术

     docker镜像是采用分层的方式构建的,每个镜像都由一系列的"镜像层"组成。分层结构是docker镜像如此轻量的重要原因。当需要修改容器镜像内的某个文件时,只对处于最上方的读写层进行变动,不覆写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本所隐藏。当使用docker commit提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。分层达到了在不的容器同镜像之间共享镜像层的效果。
举个栗子🌰🌰

    查看镜像分层方式可以通过docker image inspect [IMAGEID]命令。其中RootFS部分则是表示了分层信息。

[root@iZbp1bum6107bp8mgzkeunZ ~]# docker image inspect redis
[
   {
       "Id": "sha256:53aa81e8adfa939348cd4c846c0ab682b16dc7641714e36bfc57b764f0b947dc",
       ...
       ...
       "RootFS": {
           "Type": "layers",
           "Layers": [
                 "sha256:ad6562704f3759fb50f0d3de5f80a38f65a85e709b77fd24491253990f30b6be",     
                 "sha256:49cba0f0997b2bb3a24bcfe71c7cbd6e9f6968ef7934e3ad56b0f1f9361b6b91",   
                 "sha256:309498e524b3e2da1f036d00cd5155e0b74cf9e1d964a3636c8ed63ca4a00d43",
                 "sha256:f7c9b429437f7ada2d3d455ac4ea90ff38e0cb7ef2551b08d152264b74116309",        
                 "sha256:4dabdd56bbf16307e2328cb6ed1d42b0bb9b8f40551421271c0b38dc9a685dcc",        
                 "sha256:ea450ad6ef893e998f88a35dc9cc22f952c62b88d58f948344cf4eda1a6264fc"
           ]
       },
  }
]

     所有的Docker镜像都起始于一个基础镜像层,当镜像修改或者新增新的内容时,就会在当前镜像层之上,创建新的镜像层。即在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合。docker通过存储引擎(新版本采用快照机制)的方式实现镜像层堆栈,并保证多个镜像层对外展示为统一的文件系统。示例:
在这里插入图片描述
这个镜像中包含了三个镜像层,第一层有三个文件,第二层也有三个文件,第三层镜像中仅有一个文件,且这个文件是对第二层镜像中的文件5的一个更新版本。在这种情况下,上层镜像层中的文件会覆盖底层镜像层的文件,这样就使得文件的更新版本作为一个新的镜像层添加到镜像当中。
最后docker通过存储引擎将所有镜像层堆叠并合并,对外提供统一的视图。
在这里插入图片描述
Dockerfile中的操作对于镜像分层的影响:在镜像构建过程中需要向镜像写入数据的时候会产生分层,一个写操作指令产生一个分层。

1.5.2 写时复制(copy-on-write)策略

     docker镜像使用了写时复制(copy-on-write)的策略,在多个容器之间共享镜像,每个容器在启动的时候并不需要单独复制一份镜像文件,而是将所有镜像层以只读的方式挂载到一个挂载点,再在上面覆盖一个可读写的容器层。在未更改文件内容时,所有容器共享同一份数据,只有在docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。写时复制配合分层机制减少了镜像对磁盘空间的占用和容器启动时间。

1.5.3 内容寻址存储(content-addressable storage)机制

     在docker 1.10版本后,docker镜像改动较大,其中最重要的特性便是引入了内容寻址存储(content-addressable storage)的机制,根据文件的内容来索引镜像和镜像层。与之前版本对每个镜像层随机生成一个UUID不同,新模型对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值代替之前的UUID作为镜像层的唯一标识。该机制主要提高了镜像的安全性,并在pull、push、load和save操作后检测数据的完整性。另外,基于内容哈希来索引镜像层,在一定程度上减少了ID的冲突并且增强了镜像层的共享。对于来自不同构建的镜像层,主要拥有相同的内容哈希,也能被不同的镜像共享。

1.5.4 联合挂载(union mount)技术

     通俗地讲,联合挂载技术可以在一个挂载点同时挂载多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统将会包含整合之后的各层的文件和目录。实现这种联合挂载技术的文件系统通常被称为联合文件系统(union filesystem)。
     由于初始挂载时读写层为空,所以从用户的角度看,文件系统与底层的rootfs没有差别;然而从内核的角度看,则是显式区分开来的两个层次。当需要修改镜像内的某个文件时,只对处于最上方的读写层进行了变动,不复写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本文件所隐藏,当docker commit这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。联合挂载是用于将多个镜像层的文件系统挂载到一个挂载点来实现一个统一文件系统视图的途径,是下层存储驱动(aufs、overlay等)实现分层合并的方式。所以严格来说,联合挂载并不是docker镜像的必需技术,比如在使用device mapper存储驱动时,其实是使用了快照技术来达到分层的效果。

2.机制原理

在这里插入图片描述

docker本身是没有完整的操作系统的,它需要借助主机,无论是物理机还是虚拟机,docker启动的容器没有独立的操作系统,不需要自己的Bootloader ,所以没有bootfs的,因为主机已经启动起来了,但是它需要rootfs。

以在Linux操作系统主机中启动docker容器为例:

    1. 在Linux操作系统启动后,首先将 rootfs 设置为 readonly, 进行一系列检查, 然后将其切换为 “readwrite”供用户使用。
    1. Docker启动(以unionfs方式加载文件系统),初始化时也是将 rootfs 以 readonly 方式加载并检查;
    1. 接下来,利用 union mount方式将一个readwrite文件系统挂载在readonly 的 rootfs 之上,并且允许再次将下层的 FS(file system) 设定为 readonly 并且向上叠加,这样一组readonly和一个writeable的层级结构就构成了一个 container 的运行时态, 每一个 FS 被称作一个 FS 层。但是在 Docker里,root文件系统永远只能是只读状态。
    1. 这样一层一层堆叠,下面的层永远都是只读的,当所有层级加载完毕之后,它会将最上面的一层变为readwrite。所以针对这个容器的修改,事实上都是在最上面这一层进行的,并不会修改下面的readonly层。Union FS是层层叠加的,可以看到在做镜像构建的时候,差不多每条指令都会作为一个文件层保存下来。
    1. 在docker run具体容器的时候,就会去回放这个镜像,按照层级一级一级的去加载,通过unionfs方式去加载,这会有不同的驱动,会将dockerfile里面的每一层加载,每一层是readonly的层,然后不断的叠加,将下面一层变为readonly,最终将上面变为writeable,这个时候完整的操作系统所需要的文件系统就存在了,rootfs也就存在了,容器就可以去读取这些文件了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C# WPF入门学习主线篇(十五)—— DockPanel布局容器

C# WPF入门学习主线篇(十五)—— DockPanel布局容器 欢迎来到C# WPF入门学习系列的第十五篇。在前几篇文章中,我们探讨了 Canvas、StackPanel 和 WrapPanel 布局容器及其使用方法。本篇博客将介绍另一种强大且常用的布局容器——DockPanel。…

打造成功的人力RPO项目:赢得市场赚取利润

人力资源外包(RPO)项目是当今企业在招聘和人才管理方面越来越倾向的选择。想要通过人力RPO项目赚钱,以下是一些关键的策略和步骤,帮助您进入这个市场并取得成功。 1. 建立专业的人力RPO服务 首先,要想在人力RPO项目中赚钱,必须建立…

HCIA11 网络安全之本地 AAA 配置实验

AAA 提供 Authentication(认证)、Authorization(授权)和 Accounting(计费)三种安全功能。 • 认证:验证用户是否可以获得网络访问权。 • 授权:授权用户可以使用哪些服务。 •…

AOP切面加自定义注解,实现日志记录

AOP切面加自定义注解,实现日志记录 一、AOP二、准备工作三、添加AOP,把日志保存到数据库 一、AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实…

debug调试高级功能 断点、布局 及Android Studio常用快捷按键使用详情

文章目录 debug断点篇:打临时断点(只用一次):alt断点条件断点:在断点上,点击右键,在Condition那里,设置我们需要的值,循环就会自动停到我们设置的那个值那里依赖断点&…

Markdown如何分页操作

Markdown导出分页操作 在平时的文档导出过程中Markdown过程中会出现因为不能分页导致的排版问题。 排版问题在将Markdown文档导出为PDF或其他格式时尤为明显。当文档内容超过一页时,无法自动调整页面布局,导致内容不连续,甚至导致图片或表格…

pve8群晖rr方式安装(编译失败检查网络或磁盘空间error 23:200问题解决)

PVE 篇二:2024年PVE8最新安装使用指南|安装黑群晖|img格式镜像安装_NAS存储_什么值得买 (smzdm.com) 黑群晖 篇五:2023黑群晖最新安装方式|RR新手也可轻松上手_NAS存储_什么值得买 (smzdm.com) 编译引导提示:检查网络或磁盘空间er…

qt dll编写和调用

dll编写 新建项目 头文件 #ifndef LIB1_H #define LIB1_H#include "lib1_global.h"class LIB1_EXPORT Lib1 { public:Lib1(); };//要导出的函数,使用extern "C",否则名称改变将找不到函数extern "C" LIB1_EXPORT int ad…

程序员的核心职业素养:专业、沟通与持续学习

✨作者主页: Mr.Zwq✔️个人简介:一个正在努力学技术的Python领域创作者,擅长爬虫,逆向,全栈方向,专注基础和实战分享,欢迎咨询! 您的点赞、关注、收藏、评论,是对我最大…

单片机第五季-第八课:STM32CubeMx和FreeRTOS

1,FreeRTOS背景介绍 RTOS简介: 实时操作系统,本用于追求实时性的嵌入式系统,典型:ucos/uclinux/vxworks; 特点:中断响应快、一般可嵌套中断、使用实地址、多任务; (实…

中国历年人均发电量统计报告

数据来源于国家统计局,为1978年到2020年我国每年的人均发电量数据。 2020年,我国人均发电量为5512.76千瓦时,比上年增长3.4%。 数据统计单位为:千瓦时 我国人均发电量有多少? 2020年,我国人均发电量为5512…

一键自动粘贴,高效处理邮箱地址,让你的工作效率翻倍提升!

在信息爆炸的时代,邮箱地址已成为我们日常工作和生活中的必备元素。无论是商务沟通、报名注册还是信息传递,邮箱地址都扮演着至关重要的角色。然而,手动复制粘贴邮箱地址的繁琐操作往往让人头疼不已,不仅效率低下,还容…

代码随想录第29天|贪心算法part3

134.加油站 首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈 每个加油站的剩余量rest[i]为gas[i] - cost[i] 从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置 因为我们一直维护的是一个剩余量大…

Linux磁盘格式化与重新分区

1.df -BG查看磁盘挂载情况 2.fdisk -l查看磁盘详细信息 3.sudo mkfs.ext4 /path 格式化磁盘 4.挂载格式化后磁盘 挂载成功

FreeRTOS简单内核实现5 阻塞延时

文章目录 0、思考与回答0.1、思考一0.2、思考二0.3、思考三 1、创建空闲任务2、实现阻塞延时3、修改任务调度策略4、提供延时时基4.1、SysTick4.2、xPortSysTickHandler( )4.3、xTaskIncrementTick( ) 5、实验5.1、测试5.2、待改进 0、思考与回答 0.1、思考一 为什么 FreeRTO…

C++ 47 之 函数调用运算符重载

#include <iostream> #include <string> using namespace std;class MyPrint{ public:// 重载小括号() 重载谁operator后就紧跟谁的符号void operator()(string txt){cout << txt << endl;} };class MyAdd{ public:int operator()(int a, int b){retur…

springboot汽车配件管理系统(源码+sql+论文报告)

绪论 1.1 研究意义和背景 随着我国经济的持续发展&#xff0c;汽车已经逐步进入了家庭。汽车行业的发展&#xff0c;也带动了汽车配件行业的快速发展。 汽车配件行业的迅猛发展&#xff0c; 使得汽配行业的竞争越来越激烈。如何在激烈的竞争中取胜&#xff0c;是每家汽车零部…

Java实现异步开发的方式

1&#xff09;、继承 Thread 2&#xff09;、实现 Runnable 接口 3&#xff09;、实现 Callable 接口 FutureTask &#xff08;可以拿到返回结果&#xff0c;可以处理异常&#xff09; 4&#xff09;、使用线程池 区别&#xff1a;1、2&#xff09;不能得到返回值 …

人工智能对零售业的影响

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、人工智能如何改变零售格局二、利用人工智能实现购物体验自动化三、利用人工智能改善库存管理四、通过人工智能解决方案增强客户服务五、利用人工智能分析消费者行为六、利用 AI 打造个性化…

C++封装TCP类,包括客户端和服务器

头文件 XTcp.h #ifndef XTCP_H #define XTCP_H#ifdef WIN32 #ifdef XSOCKET_EXPORTS #define XSOCKET_API __declspec(dllexport) #else #define XSOCKET_API __declspec(dllimport) #endif #else #define XSOCKET_API #endif#include <string> XSOCKET_API std::string…