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

这篇具有很好参考价值的文章主要介绍了细谈容器化技术实现原理--以Docker为例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、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 所使用的“指针”。

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

 

正如上图所示,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镜像为例包含如下:

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

  • 第一部分,只读层。它是这个容器的 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时不包含该层。文章来源地址https://www.toymoban.com/news/detail-492628.html

总结:容器的实质:

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

到了这里,关于细谈容器化技术实现原理--以Docker为例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Docker技术--Docker容器管理

    1.Docker 容器相关的指令(单个容器操)        我们之前在Docker中部署了一个实际应用的案例wordpress,其中使用到了一些相关于容器的指令,那么下面我们一起来总结使用。 Docker指令的语法规则如下所示: Docker + 命令 [+参数选项] -1 类:关闭、开启、重启、开启自启 systemct

    2024年02月11日
    浏览(44)
  • 容器和云原生(二):Docker容器化技术

      目录 Docker容器的使用 Docker容器关键技术 Namespace  Cgroups UnionFS         首先直观地了解docker如何安装使用,并快速启动mysql服务的,启动时候绑定主机上的3306端口,查找mysql容器的ip,使用mysql -h containerIP 或者127.0.0.1就可以直接访问mysql服务,暂不考虑mysql的存储卷。    

    2024年02月12日
    浏览(46)
  • 容器技术—docker stack

    在之前的几篇文章中,我们介绍了Docker Compose,它是用来进行一个完整的应用程序相互依赖的多个容器的编排的,但是缺点是不能在分布式多机器上使用;我们也介绍了Docker swarm,它构建了docker集群,并且可以通过docker service在不同集群节点上运行容器服务,但是缺点是不能同

    2024年02月05日
    浏览(43)
  • Docker容器技术详解(四)

    本文章将介绍docker-compose技术,如果想要了解docker前置内容,可以查看以下两篇文章. Docker容器技术详解(一) Docker容器技术详解(二) Docker容器技术详解(三) 注意,要想使用docker-compose命令的话需要在有docker-compose.yml文件的目录才能使用docker-compose命令 启动docker-compose.yml文件 查看

    2024年02月08日
    浏览(43)
  • 容器化技术-Docker

    一、引言 1.1 什么是Docker(重要 - 部署)? Docker 是一个开源的应用 容器 引擎,基于 Go 语言 并遵从Apache2.0协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是完全使用沙

    2024年02月04日
    浏览(35)
  • docker容器技术

    什么是docker Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于

    2024年02月06日
    浏览(37)
  • 容器技术(Docker)

    容器技术的核心就是通过对资源的 限制 和 隔离 把进程运行在一个沙盒中。并且这个沙盒可以被打包成容器镜像(Image),移植到另一台机器上可以直接运行,不需要任何的多余配置。其中docker是容器技术的事实标准。 使用容器部署有什么优势? 容器具有强一致性,由同一

    2024年02月10日
    浏览(46)
  • Docker容器技术之镜像制作

    上篇文章说到过,作为用户是不需要制作镜像的,因为几乎所有常用的数据局库、应用软件、中间件在Docker Hub 里面可以找到,但是如果到了万不得已的时候我们还是要自己制作镜像,比如给自己编写的软件制作镜像。 制作镜像有两种方法: 1.docker commit命令 2.编写Dockerfile利

    2024年02月05日
    浏览(52)
  • Docker容器技术|最强王者篇

    🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开兴好久好久😎 📚系列专栏:Java全栈,计算机系列(火速更新中) 💭 格言:种一棵树最好的时间是十年前,其次是现在 🏡动动小手,点个关注不迷路,感

    2024年02月06日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包