Docker的三大技术底座
命名空间
类似Java当中使用包进行类名的隔离,不同 的包有相同的类名的
1、在Linux系统中,NameSpace是在内核级别以一种抽象的形式来封装系统资源,通过将系统资源放在不同的NameSpace中,来实现资源隔离的目的,不同的NameSpace程序,可以享有一份独立的系统资源。
2、命名空间是Linux为我们提供的用于分离进程树,网络接口,挂载点以及进程间通信等资源的方法。在日常使用Linux或者macOS时,我们并没有运行多个完全分离的服务器的需要,但是如果我们在服务器上启动了多个服务,这些服务其实会相互影响的,每一个服务都能看到其他服务的进程,也可以访问宿主机器上的任意文件,这是很多时候我们都不愿意看到的,我们更希望运行在同一台机器上的不同服务能做到完全隔离,就像运行在多台不同的机器上一样。
3、Linux的命名空间机制提供了以下七种不同的命名空间。
在宿主机上启动两个容器,在这两个容器内都各有一个PID=1的进程,既然Docker不是跑在宿主机上的两个虚拟机,那么它是如何实现在宿主机上运行两个相同的PID进程的呢?
这里就用到了Linux NameSpace,它其实是Linux创建新进程时的一个可选参数,在Linux系统中创建进程的系统调用是clone()方法
代码如下:
int clone(int (*fn) (void *),void *child stack,
int flags, void *arg, . . .
/* pid_ t *ptid, void *newtls, pid_ t *ctid */ ) ;
通过调用这个方法,这个进程会获得一个独立的进程空间,它的pid为1,并且看不到宿主机上的其他进程,这也就是在容器内执行ps命令的结果
不仅仅是PID,当你启动容器之后,Docker会为这个容器创建一系列的其他namespaces。这些namespaces提供了不同层面的隔离。容器的运行受到各个层面对的namesapce的限制。
Docker Engine使用了以下Linux的隔离技术:
①、管理PID的命名空间
②、管理网络命名空间
③、管理进程间通信命名空间
④、管理文件系统挂载点命名空间
⑤、Unix时间系统隔离
通过这些技术,运行时的容器可以看到一个和宿主机上其他容器隔离的环境。
先看下对进程ID进行命名隔离的命名空间:
1、首先:我们在当前的Linux操作系统下运行一个新的Docker容器:用ubuntu的镜像来启动一个 容器
-t:开启一个容器在终端
-i:以交互模式运行
2、查看容器里面的进程
这时有两个 /bin/bash:会话命令解析器 和 ps -ef:进程的打印程序
ps -ef 这个进程执行完就会退出来;然而命令解析进程会常驻内存,后台进程
3、然后再来到宿主机,使用docker top 容器id来查看容器里面的所有的进程运行
信息,这时会查看到宿主机上面的一个进程,这时的processId并不是1
4、然后通过 ps -ef | grep 进程id 会查看到 容器的shim进程会调用runc容器启动进程执行完,就退出来了,但是父进程是常驻内存的 后台进程。
5、容器里面的PID为1的进程和宿主机里面的进程PID为9420对应,容器里面的PID为1的进程对应的父进程是0对应的宿主机的父进程ID是9399
所以在容器里面,进程id是按照0开始,在宿主机里面是按照进程编号来的;但是对应的进程是一样的这个进程进程之间的命名空间的隔离;所以在容器里面使用0或者1是不受外面影响的
6、容器里面的id:9420和外面的容器id:1是怎么关联的?
从容器里面看:还是在宿主机里面看:cd /sys/fs/cgroup/memory/docker/:我们的
物理资源怎么能够对容器进行分配,那么就有个控制分组,因为控制分组在宿主机
里的。这就是容器的id为1对应的宿主机的pid是9420
物理资源包括它的内存,cpu,所有的限制都在上面的目录下:
怎么进行对容器进行分配,涉及到了控制分组:控制分组是在宿主机里面的
命名空间:上面的就是用进程id来进行隔离的
还有网络隔离:同一个网卡在里面和外面看到的不一样的,Ip地址分配也是不一样的。
控制组
①、为什么要需要资源的控制分组:
就是对物理机的磁盘资源,cpu资源 ,网络资源进行控制的
因为我们的物理机的这些资源是共用的,但是同一个物理机上会跑很多的容器,那
么哪个容器要用资源多一点,或者少一点。这个时候我们要进行申请和使用。资源
是怎么限制的是通过Linux底层控制分组的方式来实现的。在k8s的资源文件里面有
限制资源的。
比如内存:这些信息记录在 /sys/fs/cgroup/memory/docker/容器id下:对内存资源
memory进行控制.
属性:usage_in_bytes:用了多少内存。max_usage_in_bytes:最多的时候用了多少
内存
比如对cpu进行控制,在k8s当中250m代表0.25个cpu。1000m 表示1个cpu
在linux是怎么表示的呢 cat cpuacct.usage:代表1s当中有多少个时间片给这个容
器用。
还有设置每个块设置的输入输出控制,例如:磁盘,光盘以及usb等等
联合文件系统UnionFS
能力:做了一个能够将不同文件夹中的层联合到了同一个文件夹中。
什么是联合挂载技术:
联合挂载技术是建立在底层文件系统(ext4fs,xfs等等)之上的一种挂载技术,它可以将原来底层文件系统中的不同目录进行合并,然后向我们呈现出合并之后的一个文件系统。overlayFS则是联合挂载技术的一种实现,除了aufs以外还有overlayFS,VFS,Brtfs,devicemapper等技术。
虽然实现细节不同,但是他们做的事情都是相同的。
Linux内核为Docker提供的overlayFS驱动有2种:overlay2和overlay。overlay2是相对于overlay的一种改进,在inode利用率方面比overlay更有效。
什么是Docker镜像分层机制
Docker Image是有一个层级结构的,最底层的layer为BaseImage(一般为一个操作系统的ISO镜像),然后顺序执行每条指令,生成的layer按照入栈的顺序逐渐累加,最终形成一个image。
直观的角度来说,是这个图所示:
①、有两个容器:一个是Apache的java应用的容器,还一个是不断运转的容器:BusyBox,这两个容器都共用操作系统的内核。操作系统的内核主要做整个系统的内存管理;进程管理等等。
②、容器基于操作系统的命名空间的隔离和资源控制分组的技术进行资源的隔离和限制。不同的容器运行自己的应用程序,那么这个应用程序一定是从文件里面加载来的我们的文件在哪里?而且我们不同的容器还是要对有一些文件进行写入。读文件,写文件;我们启动一个容器是通过启动一个镜像来启动的,在制作镜像的过程当中:也是有基础镜像也是一层层的制作的首先通过底层文件,然后命令文件,上层镜像,所以镜像之间也是分层的。所以每一层镜像就是一个文件,最终镜像的文件合并到一起形成合并的目录,这个是只读的部分。
我们在运行的过程当中还有写入的,在目录的最上层是能写的。下面的层只能读,最终形成联合的文件视图给到应用。
③、有了联合文件系统,我们在做镜像的时候,我们要复用原来文件的话,我们只需要基于基础镜像做叠加,扩展就够了:可以帮助底层的镜像进行复用
④、从宿主机的目录来看:对于容器来说,在镜像的部分有很多的目录,在镜像 ,容器,卷这些 有很多个目录。还有容器层的目录,还有挂载的相应卷的目录;最终形成一个完整的联合文件系统给到应用程序。
动手实操:深入理解Docker容器镜像分层的原理
在容器镜像的每一层对应一个文件夹,层与层之间的关系也对应一个文件夹。
/var/lib/docker/overlay2/:对应的目录是每一层的目录名称,里面的内容是每一层文件的内容,里面的是镜像层,容器层的信息。内容不是以镜像作为整体给分配进来的,而是很多层的,所有的镜像层。
随便进一层看下:
lower目录:可以是多个,是处于最底层的目录,作为只读层
upper目录:只有一个,读写层
work目录:为工作基础目录,挂载后内容会被清空,且在使用过程中,其内容不可
见。
merged目录:为最后联合挂载完成给用户呈现的统一视图,也就是说merged目录里面本身并没有任何实体文件。给我们展示的只是参与联合挂载的目录里面文件而已,真正的文件还是在lower和upper中。所以,在merged目录下编辑文件,或者直接在lower或者upper目录里面的文件都会影响merged里面的视图展示。
镜像层的内容:overlavfs通过三个目录来实现,Lower目录,upper目录,以及work目录,三种目录合并出来的目录称为merged目录
/var/lib/docker/image/orverlay2/layerdb/mounts/{container_id} :每一层和每一层的对应关系存储在这个目录下,这个目录中有多少个容器就有多少个文件。
mount-id:存储的是/var/lib/docker/overlay2下的目录名称
init-id:initID是在mountID后加了一个init。parent:容器所基于的镜像上层的chain_id。