想学习好一点技术,最好的方法就是学习这项技术的工作原理。一直很好奇docker是如何实现互不影响的,很是神奇。
通过学习查阅一些资料,慢慢的了解了底层是如何实现的,特此记录一下。
从本质上说,docker容器其实就是一个沙盒技术。就好像把一个应用隔离在一个盒子内,让其运行。因为盒子有边界的存在,应用于应用之间不会存在相互干扰。
实现原理
实现容器的核心,其实就是要生成限制应用运行时的边界。编译后的可执行代码加上数据,叫做程序。
而把程序运行起来后,就变成了进程,也就是所谓的应用了。如果在启动的时候,加上一个边界,是不是就实现期待的沙盒了?
在Linux中,实现容器的边界,主要有两种技术,Cgroups
和 Namespace
。
Namespace
Namespace是用于分离进程树、网络接口、挂载点以及进程间通信等资源的方法。通俗点讲,就是将容器隔离起来,实现边界。
在日常使用Linux 和 macOS时,并没有必要运行多个完全分离的服务器需要。但是如果在服务器上启动多个服务,这些服务其实会相互影响的。
每个服务都能看到其他服务的进程,也可以访问宿主机上的任意文件,这是很多时候不愿意看到的,我们希望他们能做到完全隔离,就像运行在不同机器上一样。
在这种情况下,一旦服务器上一个服务被入侵,其他服务也就被入侵了。
而Docker就是通过namespace对不同容器实现的隔离。
Cgroups
通过Namespace
技术,我们实现了容器和容器之间,容器和宿主机之间的隔离。
但这还不够,想象一下一种场景。宿主机上运行着两个容器,虽然在容器之间相互隔离,但在宿主机的视角来看,只是两个特殊的进程。
而进程之间自然存在着竞争关系,自然可以将系统资源吃光,这是一定要避免的。
Cgroups就是Linux内核中用来为进程设置资源的一个技术。
“
Linux Cgroups 全称是 Linux Control Group,主要作用限制进程组使用的资源上限。例如CPU、磁盘I/O、内存、和网络带宽。还可以对进程进行优先级设置,审计,挂起和恢复等操作。
”
容器的文件系统:rootfs
现在知道容器的核心技术就是通过 Namespace 限制了容器看到的视野
, 通过Cgroup限制容器可访问的资源
。
在容器内,可以看到完全独立的文件系统,而且不会收到宿主机以及其他容器的影响。
这个独立的文件系统,叫做容器镜像
, 专业的名字叫 rootfs
, rootfs
中包含了一个操作系统所需要的文件,配置和目录,但不包含系统内核。
因为在Linux中,文件和内核是分开存放的,操作系统只有在开启启动时才会加载指定的内核。这就说明,所有的容器都会共享宿主机上操作系统的内核
。
有了rootfs
之后,在镜像内,打包的不仅仅是应用,还有所需要的依赖,都被封装在一起。这就解决无论是在哪,应用都可以很好运行的原因。
不光是这样,rootfs 还解决了可重用性的问题。
想象一下,你通过 rootfs
打包一个包含php的centos镜像,别人需要安装一个Nginx服务,是否还需要从头开始搭建php服务呢?
docker 在解决这个问题的时候,引入一个层(layer)
的概念,每次对 rootfs
的更改,都只保留增量的内容,而不是fork一个新镜像。
层级的想法,同样来自于 Linux,一个叫 union file system (联合文件系统)
。
它最主要的功能就是将不同位置的目录联合挂载到同一个目录下。对应在 Docker 里面,不同的环境则使用了不同的联合文件系统。
Image、Container Layer定义
Image 定义
镜像(Image)就是一堆只读层的统一视角,也许这个定义有些难以理解,看张图理解下。
从左边看到多个只读层,他们重叠在一起。除了最下面的一层,其它层都会有一个指向下一层的指针。
这些层是Docker内部的实现细节,并能在运行Docker机器的文件系统上访问到。
统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就能隐藏错层的存在,在用户的角度看来,只存在一个文件系统。在在右边的图上可以看到这种形式
Container 定义
容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器最上面的那一层是可读可写的。
这里容器的定义并没有提及到容器是否运行,运行时的容器是怎么样的呢?
Running Container 定义
一个运行态的容器(running container)被定义为一个可读写的统一文件系统加上隔离的进程空间和包含其中的进程
。
文件系统隔离技术促使了Docker成为了一个前途无量的技术。一个容器中的进程可能会对文件进行修改、删除、创建,这些改变都将作用于可读可写层(read-write layer)。
Image Layer 定义
为了将零星的数据整合起来,提出了镜像层(image layer)的概念。一个镜像层不仅仅包含文件系统的改变,他还能包含一些其他重要的信息。
元数据(metadata)就是关于这个层的额外信息,他不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。需要注意的是,只读层和读写层都包含元数据。
除此之外,每一层都有一个指向父级层的指针,这点和mvcc中版本链是一样的。如果没有这个指针,说明他是最底层。
Docker命令详解
docker create <image_id>
docker create 命令为指定的镜像(image)添加一个可读可写层,构建一个新的容器。
这个容器此时并没有启动运行。
docker start <container_id>
docker start 命令为容器文件系统创建一个进程隔离空间。注意:每一个容器只能够有一个进程隔离空间。
docker run <image_id>
看到这个命令的时候我会产生一个疑问:run 和 start 有什么区别?
docker run 其实是两个命令的结合体:docker create 和 docker start。
首先会使用 docker create 利用镜像创建一个容器,然后运行这个容器。这个命令虽然很方面,但是也会隐藏一部分细节。
“
题外话:git pull命令其实就是 git fetch 和 git merge 两个命令的组合。同样的,docker run就是docker create和docker start两个命令的组合。
”
docker ps
这个命令会罗列出所有运行中的容器,隐藏了未运行的容器,如果想看到未运行的,使用 docker ps -a 则可以看到所有的容器。
docker commit <container_id>
docker commit 会将可读可写层转换为一个可读层,这样就把一个容器转换为不可变的镜像。
docker build
docker build 会反复执行多个命令。
build命令根据Dockerfile文件中的FROM指令获取到镜像,然后重复的。1、run(create和start);2、修改;3、commit。在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。
docker 原理中涉及到很多linux相关的知识,这方面的知识还需要后续的学习补充。
总结的不对的地方,还请各位大佬多多指点。
如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我继续坚持下去,非常感谢!
你还可以把我的公众号设为「星标」
,这样当公众号文章更新时,你会在第一时间收到推送消息,避免错过我的文章更新。
Namespace[1]
Layer层[2]
参考资料
命名空间:https://draveness.me/docker。
[2]
层级:https://www.yht7.com/news/140896。
原文始发于微信公众号(白砂):Docker学习之工作原理
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/19969.html