细谈容器化技术实现原理--以Docker为例

news2024/11/26 10:19:29

目录

一、Docker解决了什么

二、容器的发展过程

三、容器基础

3.1. 容器实现的原理:

⚠️原理详解:

3.1.1. Namespace

3.1.2. Cgroups

3.1.3. chroot

四、Volume

4.1. Docker是如何做到把一个宿主机上的目录或者文件,挂载到容器里面去的?

⚠️4.1.1.  挂载技术 bind mount

五、容器化应用的常见问题

六、layer与联合文件系统

6.1. 联合文件系统(Union File System)

layer

总结:容器的实质:


一、Docker解决了什么

最简单的例子就是日常开发中,我们clone下来一个代码想直接运行,会发现很多问题,即本地环境跟远端环境不一致。

docker能将应用打包起来,搬到哪里都可以直接使用;

1. docker build [镜像]

  • 用当前操作系统文件与目录制作一个压缩包
  • 这个包包含了这个应用运行所需的所有依赖
  • 从而保证了本地环境和云端环境的高度一致

2. docker run [镜像]

  • 创建一个“沙盒”来解压这个镜像,然后在“沙盒”中运行自己的应用
  • 这个沙盒就是 Cgroups和Namespace (下文会讲)

二、容器的发展过程

了解docker得结合其诞生的背景

  1. 首先有一大批物理服务器,想要租给用户使用,因此搭建了一个物理集群,向用户售卖计算资源 --> 这就是 IaaS (Infrastructure as a Service) :它提供了基础设施(硬件)作为服务,让用户能够在云端租用虚拟化的计算资源,如虚拟机、存储、网络等
  2. 用户有了云端虚拟机,需要在虚拟机上部署自己的应用。但是本地的开发环境和购买的虚拟机之间有很大不一样,调试、部署很麻烦,应用之间没有隔离。--> Paas(Platform as a Service)出现了提供了一个平台来让开发人员构建、测试、部署和管理他们的应用程序
  3. Paas提供了大规模部署应用的能力,提供了“沙盒”来应对隔离,但是用户发现打包过程很繁琐需要投入大量人力时间与云端Paas进行适配 --> Docker用镜像将整个应用运行所需的环境和依赖打包实现了本地环境和云端高度一致。
  4. 为了对多个容器应用的自动部署、负载均衡、弹性伸缩、服务发现、健康检查、容器间通信如容器A管理B等等 --> 容器编排技术:docker swarm compose、Kubernetes

三、容器基础

3.1. 容器实现的原理

  • 启用 Linux Namespace 配置;
  • 设置指定的 Cgroups 参数;
  • 切换进程的根目录(Change Root)

👇详细讲解其必要性

⚠️原理详解

3.1.1. Namespace

  • Namespace是Linux内核中的另一种机制,它可以创建一个独立的进程环境,包括文件系统、网络、进程ID、用户ID等等。
  • Namespace可以用于隔离容器内的应用程序与主机系统的环境,从而实现容器的隔离和安全性。
  • 通过使用Namespace,容器内的应用程序可以在一个独立的命名空间中运行,而不会影响主机系统或其他容器。

  • 在使用 Docker 的时候,并没有一个真正的“Docker 容器”运行在宿主机里面。Docker 项目帮助用户启动的,还是原来的应用进程,只不过在创建这些进程时,Docker 为它们加上了各种各样的 Namespace 参数。这时,这些进程就会觉得自己是各自 PID Namespace 里的第 1 号进程,只能看到各自 Mount Namespace 里挂载的目录和文件,只能访问到各自 Network Namespace 里的网络设备,就仿佛运行在一个个“容器”里面,与世隔绝。不过,相信你此刻已经会心一笑:这些不过都是“障眼法”罢了。

  • 虽然容器内的第 1 号进程在“障眼法”的干扰下只能看到容器里的情况,但是宿主机上,它作为第 100 号进程与其他所有进程之间依然是平等的竞争关系。这就意味着,虽然第 100 号进程表面上被隔离了起来,但是它所能够使用到的资源(比如 CPU、内存),却是可以随时被宿主机上的其他进程(或者其他容器)占用的。当然,这个 100 号进程自己也可能把所有资源吃光。这些情况,显然都不是一个“沙盒”应该表现出来的合理行为。

3.1.2. Cgroups

  • Cgroups是Linux内核中的一种机制,它可以将进程分组并为每个组分配特定的资源限制,例如CPU、内存、磁盘IO和网络带宽等。

  • 通过Cgroups,系统管理员可以控制进程对资源的访问和使用,从而避免出现资源竞争和滥用的情况。Cgroups可以用于容器化应用程序,以限制容器中的进程对主机资源的访问。

3.1.3. chroot

  • 为容器提供执行环境和依赖的文件系统是如何实现:

  • Mount Namespace
  • 只隔离增量,不隔离存量:原来有的文件不会被隔离,新增的才会
  • 因此开启了隔离还不行,还需要重新进行挂载才会生效。
  1. 使用Mount Namespace在容器进程启动之前重新挂载它的整个根目录“/”(挂在对宿主机不可见)
  2. 使用chroot改变进程的根目录到指定位置
  3. 为了能够让容器的这个根目录看起来更“真实”,我们一般会在这个容器的根目录下挂载一个完整操作系统的文件系统
  4. 挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的“容器镜像”。它还有一个更为专业的名字,叫作:rootfs(根文件系统)

注意⚠️

一个正在运行的 Docker 容器,其实就是一个启用了多个 Linux Namespace 的应用进程,而这个进程能够使用的资源量,则受 Cgroups 配置的限制。这也是容器技术中一个非常重要的概念,即:容器是一个“单进程”模型。

rootfs 只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。所以一台机器上的所有容器共享宿主机操作系统的内核

四、Volume

通过volume机制,可以在容器里面访问宿主机的文件,也可以在宿主机上访问容器里面的文件

4.1. Docker是如何做到把一个宿主机上的目录或者文件,挂载到容器里面去的?

⚠️看懂数据卷挂载可以帮助你加深对容器化尤其是docker的理解,篇幅很长,都是精华:

1. 尽管开启了 Mount Namespace,但是在它执行 chroot(或者 pivot_root)之前,容器进程一直可以看到宿主机上的整个文件系统。

2. 而宿主机上的文件系统,也自然包括了我们要使用的容器镜像。这个镜像的各个层,保存在 /var/lib/docker/aufs/diff 目录下,在容器进程启动后,它们会被联合挂载在 /var/lib/docker/aufs/mnt/ 目录中,这样容器所需的 rootfs 就准备好了。

3. 所以,我们只需要在 rootfs 准备好之后,在执行 chroot 之前,把 Volume 指定的宿主机目录(比如 /home 目录),挂载到指定的容器目录(比如 /test 目录)在宿主机上对应的目录(即 /var/lib/docker/aufs/mnt/[可读写层 ID]/test)上,这个 Volume 的挂载工作就完成了。

4. “容器进程”已经创建了,也就意味着此时 Mount Namespace 已经开启了。所以,这个挂载事件只在这个容器里可见。你在宿主机上,是看不见容器内部的这个挂载点的。这就保证了容器的隔离性不会被 Volume 打破。

⚠️4.1.1.  挂载技术 bind mount

使用到的挂载技术,就是 Linux 的绑定挂载(bind mount)机制。它的主要作用就是,允许你将一个目录或者文件,挂载到一个指定的目录上。并且,这时你在该挂载点上(/test)进行的任何操作,只是发生在被挂载的目录或者文件上(/home),而原挂载点(/test)的内容则会被隐藏起来且不受影响。

绑定挂载实际上是一个 inode 替换的过程

在 Linux 操作系统中,inode 可以理解为存放文件内容的“对象”,而 dentry,也叫目录项,就是访问这个 inode 所使用的“指针”。

 

正如上图所示,mount --bind /home /test,会将 /home 挂载到 /test 上。其实相当于将 /test 的 dentry,重定向到了 /home 的 inode。这样当我们修改 /test 目录时,实际修改的是 /home 目录的 inode。这也就是为何,一旦执行 umount 命令,/test 目录原先的内容就会恢复:因为修改真正发生在的,是 /home 目录里。

因此进程在容器里对这个 /test 目录进行的所有操作,都实际发生在宿主机的对应目录(比如,/home,或者 /var/lib/docker/volumes/[VOLUME_ID]/_data)里,而不会影响容器镜像的内容。

$ docker run -v /test ...(没有显式地声明要映射的宿主机目录,那么docker在宿主机上创建的映射的临时目录的路径为/var/lib/docker/volumes/[VOLUME_ID]/_data)
$ docker run -v /home:/test ... (对应的就是/home)

❓疑问: 

这个 /test 目录里的内容,既然挂载在容器 rootfs 的可读写层,它会不会被 docker commit 提交掉呢?

不会,由于moutn namespace的作用挂载对于宿主机并不可见,在宿主机的眼里 /test的 inode就是原本没有重定向的inode,你所有的修改都不在这个文件下

五、容器化应用的常见问题

/proc Linux 下的 /proc 目录存储的是记录当前内核运行状态的一系列特殊文件

用户可以通过访问这些文件,查看系统以及当前正在运行的进程的信息,比如 CPU 使用情况、内存占用率等,这些文件也是 top 指令查看系统信息的主要数据来源。

但是,如果在容器里执行 top 指令,就会发现,它显示的信息居然是宿主机的 CPU 和内存数据,而不是当前容器的数据。

造成这个问题的原因就是,/proc 文件系统并不知道用户通过 Cgroups 给这个容器做了什么样的资源限制,即:/proc 文件系统不了解 Cgroups 限制的存在。在生产环境中,这个问题必须进行修正,否则应用程序在容器里读取到的 CPU 核数、可用内存等信息都是宿主机上的数据,这会给应用的运行带来非常大的困惑和风险

这也是在企业中,容器化应用碰到的一个常见问题,也是容器相较于虚拟机另一个不尽如人意的地方。

扩展命令:

docker exec :

        一个进程的每种 Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上。

        docker exec的原理就是使用linux系统指令setns将进程加入到一个容器进程的namespace,从而“进入”容器;

docker volume ls

        查看docker所有挂载的数据卷

docker inspect

        命令查看容器的 IP 地址

lxcfs

六、layer与联合文件系统

Docker 在镜像的设计中,引入了层(layer)的概念。

也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs。

6.1. 联合文件系统(Union File System)

层的概念用到了一种叫作联合文件系统(Union File System)的能力。

最主要的功能是将多个不同位置的目录联合挂载(union mount)到同一个目录下(数据卷挂载的实现原理。比如,我现在有两个目录 A 和 B,它们分别有两个文件:

$ tree
.
├── A
│ ├── a
│ └── x
└── B
├── b
└── x

然后,我使用联合挂载的方式,将这两个目录挂载到一个公共的目录 C 上:

$ mkdir C
$ mount -t aufs -o dirs=./A:./B none ./C

这时,我再查看目录 C 的内容,就能看到目录 A 和 B 下的文件被合并到了一起:

$ tree ./C
./C
├── a
├── b
└── x
在这个合并后的目录 C 里,有 a、b、x 三个文件,并且 x 文件只有一份。这,就是“合并”的含义。此外,如果你在目录 C 里对 a、b、x 文件做修改,这些修改也会在对应的目录 A、B 中生效。(数据卷挂载的实现原理

layer

一个完整的docker镜像是由多个层组成的,docker用ufs将多个层联合挂载到一个目录中

分层可以方便用户制作自己定制的镜像。

其中以ubuntu镜像为例包含如下:

  • 第一部分,只读层。它是这个容器的 rootfs 最下面的五层,对应的正是 ubuntu:latest 镜像的五层。可以看到,它们的挂载方式都是只读的(ro+wh,即 readonly+whiteout,至于什么是 whiteout,我下面马上会讲到)。

  • 第二部分,可读写层。它是这个容器的 rootfs 最上面的一层(6e3be5d2ecccae7cc),它的挂载方式为:rw,即 read write。在没有写入文件之前,这个目录是空的。而一旦在容器里做了写操作,你修改产生的内容就会以增量的方式出现在这个层中

    • 1. 删除只读层的文件呢?AuFS 会在可读写层创建一个 whiteout 文件,把只读层里的文件“遮挡”起来。(所以删除文件还会增加镜像的文件大小)

    • 2. 可读写层的作用,就是专门用来存放你修改 rootfs 后产生的增量,无论是增、删、改,都发生在这里。而当我们使用完了这个被修改过的容器之后,还可以使用 docker commit 和 push 指令,保存这个被修改过的可读写层,并上传到 Docker Hub 上,供其他人使用;而与此同时,原先的只读层里的内容则不会有任何变化。这,就是增量 rootfs 的好处。

  • 第三部分,Init 层。用户拉取镜像到本地需要在启动容器时写入一些指定的值比如 hostname,这些修改往往只对当前的容器有效,因此将存放 /etc/hosts、/etc/resolv.conf 等信息的单独提取出一个层,在用户执行commit时不包含该层。

总结:容器的实质:

  • 一个“容器”,实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境
  • 一个正在运行的 Linux 容器,其实可以被“一分为二”地看待:一组联合挂载在 /var/lib/docker/aufs/mnt 上的 rootfs,这一部分我们称为“容器镜像”(Container Image),是容器的静态视图;
  • 一个由 Namespace+Cgroups 构成的隔离环境,这一部分我们称为“容器运行时”(Container Runtime),是容器的动态视图。

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

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

相关文章

4. 数组更新检测

4.1 v-for更新监测 当v-for遍历的目标结构改变, Vue触发v-for的更新 情况1: 数组翻转 情况2: 数组截取 情况3: 更新值 口诀: 数组变更方法, 就会导致v-for更新, 页面更新 数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set() 这些方法会触发数组改…

基于深度学习的高精度鸽子检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要:基于深度学习的高精度鸽子检测识别系统可用于日常生活中或野外来检测与定位鸽子目标,利用深度学习算法可实现图片、视频、摄像头等方式的鸽子目标检测识别,另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

XSS注入——反射性XSS

xss注入的攻击步骤: 1.查找可能存在的注入点(搜索框,留言板,注册) 2.观察输出显示位置: html: 尖括号外部, 尖括号内部: 引号内部》闭合&#xff0…

Django | 基于pycharm的django配置52张全流程截图,红星给你一步一步的男妈妈式教学

演示版本:【windows系统】python3.10pycharm2023.1.2django4.2.2 (本教程全程在虚拟机中演示,读者无需配置虚拟机,直接按教程安装即可) 目录 1.搞到必要的安装包 2.事先准备 3.安装chrome浏览器(也可以…

国产MCU-CW32F030开发学习--按键检测

国产MCU-CW32F030开发学习–按键检测 bsp_key 按键驱动程序用于扫描独立按键,具有软件滤波机制,采用 FIFO 机制保存键值。可以检测 如下事件: 按键按下。 按键弹起。 长按键。 长按时自动连发。 我们将按键驱动分为两个部分来介绍&#xff…

C语言学习笔记:顺序结构

✨博文作者:烟雨孤舟 💖 喜欢的可以 点赞 收藏 关注哦~~ ✍️ 作者简介: 一个热爱大数据的学习者 ✍️ 笔记简介:作为大数据爱好者,以下是个人总结的学习笔记,如有错误,请多多指教! 目录 程序与…

《面试1v1》Spring基础

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

浅谈微前端

本文呢是我梳理的一个扫盲文,由于最近团队准备使用微前端对项目进行改造,所以我呢就先浅了解一下: 微前端到底是什么? 为什么要使用微前端? 都有哪些微前端方案? 微前端有什么不好的地方吗? 通过…

48 最佳实践-性能最佳实践-Guest-Idle-Haltpoll

文章目录 48 最佳实践-性能最佳实践-Guest-Idle-Haltpoll48.1 概述48.2 操作指导 48 最佳实践-性能最佳实践-Guest-Idle-Haltpoll 48.1 概述 为了保证公平性及降低功耗,当虚拟机vCPU空闲时,虚拟机将执行WFx/HLT指令退出到宿主机中,并触发上…

计算机视觉 - 基于黄金模板比较技术的缺陷检测

一、黄金模板比较概述 基于黄金模板比对的检测是一种常见的视觉应用。当进行缺陷检查而其他缺陷检测方法是不可行的时候,使用金模板比较。另外当物体的表面或物体的形状非常复杂时,此技术特别有用。 虽然说黄金模板比较的技术的思路很简单,但是真正落地实施确不是一件十分容…

广告数仓:数仓搭建(二)

系列文章目录 广告数仓:采集通道创建 广告数仓:数仓搭建 广告数仓:数仓搭建(二) 文章目录 系列文章目录前言DWD层创建1.建表广告事件事实表 2.数据装载初步解析日志解析IP和UA标注无效流量编写脚本 总结 前言 这次我们完成数仓剩下的内容 D…

Web服务器群集:Web基础与HTTP协议

目录 一、理论 1.Web基础 2.HTTP协议 二、实验 1.浏览本地HTML页面 三、总结 一、理论 1.Web基础 (1)域名和DNS ① 域名 网络是基于TCP/IP 协议进行通信和连接的,每一台主机都有一个唯一的标识(固定的IP地 址&#xff0…

【Java面试】什么是SpringMVC?它的工作流程是什么样子的?

文章目录 什么是MVC?MVC组件组件前端控制器DispatcherServlet处理器映射器HandlerMapping处理器适配器HandlAdapter视图解析器ViewResolver处理器Handler视图View 工作原理具体执行流程 什么是MVC? M:model,模型层,包…

搅拌机打蒜机不转维修

打蒜机不转维修:打蒜机用的18650电池,霍尔传感器,Dp0206场效应管。故障为按一下开关显示红灯:电池电压低!按下启动按钮电机动一下就再不动了。如果给电池两边加一个5伏电源,打蒜机电机运行正常。那么我把充…

【028】C++ 类和对象的 构造函数、析构函数、拷贝构造、初始化列表 详解(最全讲解)

C类和对象的构造函数、析构函数、拷贝构造、初始化列表详解 引言一、构造函数1.1、数据初始化和清理1.2、构造函数概述1.3、构造函数的定义1.4、提供构造函数的影响 二、析构函数三、拷贝构造函数3.1、拷贝构造的定义3.2、拷贝构造、无参构造、有参构造 三者的关系3.3、拷贝构造…

【群智能算法改进】一种改进的浣熊优化算法 改进长鼻浣熊优化算法 改进后的ICOA[1]算法【Matlab代码#41】

文章目录 【获取资源请见文章第5节:资源获取】1. 原始COA算法1.1 开发阶段1.2 探索阶段 2. 改进后的ICOA算法2.1 Circle映射种群初始化2.2 Levy飞行策略2.3 透镜成像折射反向学习策略 3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1…

05-DataFrame的数据清洗

数据清洗 import pandas as pd df pd.read_excel("学生成绩.xlsx") df删除重复列 df.drop_duplicates(inplaceTrue) df删除数学成绩 df.drop([数学成绩],axis1, inplaceTrue) df重命名列名字 df.rename(columns{"生活":"自然"},inplaceTrue…

什么是Redis的BigKey,如何处理BigKey?

一、什么是BigKey BigKey通常以Key的大小和Key中成员的数量来综合判定,例如: Key本身的数据量过大:一个String类型的Key,它的值为5 MB。Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10,000个。Ke…

Linux之多线程(下)——线程控制

文章目录 前言一、POSIX线程库1.概念2.pthread线程库是应用层的原生线程库3.错误的检查 二、线程控制1.创建线程——pthread_createpthread_create函数例子创建一个新线程主线程创建一批新线程 2.获取线程ID——pthread_self3.线程等待——pthread_join4.线程终止——return、p…

SSH服务器详解

文章目录 文字接口连接服务器:SSH服务器连接加密技术简介启动SSH服务SSH客户端连接程序SSH:直接登录远程主机的指令使用案例 服务器公钥记录文件:~/.ssh/known_hosts报错解决 模拟FTP的文件传输方式:SFTP使用案例 文件异地直接复制…