020-docker镜像UnionFS、Docker镜像加载原理、分层的镜像与容器、结合docker命令理解镜像
一、概述
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,
它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
1.1、UnionFS(联合文件系统)
UnionFS (联合文件系统) : Union文件系统(UnionFS) 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一’ 次提交来一层层的叠加,同时可以将不同月录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
1.2、Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
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等等。
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。
针对DockerFile,每一次RUN 都会基于基础镜像,作出一个新的images
1.3、分层的镜像与容器
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
Docker镜像都是只读的,当容器启动时,一个新的可写层镜像加载到镜像顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层。
1.3.1、镜像
镜像(Image)就是一堆只读层(read-only layer)的统一视角
从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机(译者注:运行Docker的机器)的文件系统上访问到。统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式。
在主机文件系统上找到有关这些层的文件。需要注意的是,在一个运行中的容器内部,这些层是不可见的。存在于/var/lib/docker/aufs目录下。/var/lib/docker/
1.3.2、容器
容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
容器 = 镜像 + 读写层。并且容器的定义并没有提及是否要运行容器。
1.3.3、运行容器
一个运行态容器(running container)被定义为一个可读写的统一文件系统加上隔离的进程空间和包含其中的进程。下面这张图片展示了一个运行中的容器。
正是文件系统隔离技术使得Docker成为了一个前途无量的技术。一个容器中的进程可能会对文件进行修改、删除、创建,这些改变都将作用于可读写层(read-write layer)。下面这张图展示了这个行为。
我们可以通过运行以下命令来验证我们上面所说的:
docker run ubuntu touch happiness.txt
即便是这个ubuntu容器不再运行,我们依旧能够在主机的文件系统上找到这个新文件。
find / -name happiness.txt
/var/lib/docker/aufs/diff/860a7b...889/happiness.txt
1.3.4、镜像层
一个层组成【id、元数据、指向父的指针】
1、元数据(metadata)
就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层和读写层都包含元数据。
镜像层(image layer)的元数据被保存在名为”json”的文件中,比如说:/var/lib/docker/graph/e809f156dc985.../json。
一个容器的元数据被分成了很多文件,但或多或少能够在/var/lib/docker/containers/<id>目录下找到,<id>就是一个可读层的id。这个目录下的文件大多是运行时的数据,比如说网络,日志等等。
2、父指针
每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。
1.3.5、为什么需要镜像分层
共享资源,有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
二、结合docker命令理解镜像
3.1、创建:docker create <image-id>
docker create 命令为指定的镜像(image)添加了一个可读写层,构成了一个新的容器。注意,这个容器并没有运行。
3.2.、docker start <container-id>
Docker start命令为容器文件系统创建了一个进程隔离空间。注意,每一个容器只能够有一个进程隔离空间。
3.3、docker run <image-id>
docker run就是docker create和docker start两个命令的组合。docker run 命令先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常的方便,并且隐藏了两个命令的细节
docker run命令类似于git pull命令。git pull命令就是git fetch 和 git merge两个命令的组合,同样的,docker run就是docker create和docker start两个命令的组合。
3.4、docker ps【docker ps -a】
docker ps 命令会列出所有运行中的容器。
docker ps –a命令会列出所有的容器,不管是运行的,还是停止的。
3.5、docker images【docker images -a】
docker images 所有顶层镜像
docker images -a 列出了所有的镜像,也可以说是列出了所有的可读层。如果你想要查看某一个image-id下的所有层,可以使用docker history来查看。
docker history ubuntu:14.04 可递归地输出指定镜像的历史镜像。
3.6、docker stop <container-id>
docker stop命令会向运行中的容器发送一个SIGTERM的信号,然后停止所有的进程。
3.7、docker kill <container-id>
docker kill 命令向所有运行在容器中的进程发送了一个不友好的SIGKILL信号。
3.8、docker pause <container-id>
docker stop和docker kill命令会发送UNIX的信号给运行中的进程,docker pause命令则不一样,它利用了cgroups的特性将运行中的进程空间暂停。具体的内部原理你可以在这里找到:https://www.kernel.org/doc/Doc ... m.txt,但是这种方式的不足之处在于发送一个SIGTSTP信号对于进程来说不够简单易懂,以至于不能够让所有进程暂停。
3.9、docker rm <container-id>
docker rm命令会移除构成容器的可读写层。注意,这个命令只能对非运行态容器执行。
3.10、docker rmi <image-id>
docker rmi 命令会移除构成镜像的一个只读层。你只能够使用docker rmi来移除最顶层(top level layer)(也可以说是镜像),你也可以使用-f参数来强制删除中间的只读层。
3.11、docker commit <container-id>
docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。
3.12、docker build
它会反复的执行多个命令
从上图可以看到,build命令根据Dockerfile文件中的FROM指令获取到镜像,然后重复地1)run(create和start)、2)修改、3)commit。在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。
3.13、docker exec <running-container-id>
docker exec 命令会在运行中的容器执行一个新进程。
3.14、docker inspect <container-id> or <image-id>
docker inspect命令会提取出容器或者镜像最顶层的元数据。
3.15、docker save <image-id>
docker save命令会创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用。和export命令不同,这个命令为每一个层都保存了它们的元数据。这个命令只能对镜像生效。
3.16、docker export <container-id>
docker export命令创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容(译者注:expoxt后的容器再import到Docker中,通过docker images –tree命令只能看到一个镜像;而save后的镜像则不同,它能够看到这个镜像的历史镜像)。
参看地址:http://dockone.io/article/783