本文共 3300 字,大约阅读时间需要 11 分钟。
很多时候,当大家谈论起Docker,经常会提到Docker作为容器解决方案,在虚拟化资源方面存在不小优势。轻量级虚拟化技术的优点暂且不谈,从软件生命周期来看,Docker在打包软件、分发软件方面的能力同样出众。而后者很大程度上依赖于Docker的镜像技术。
Docker镜像技术提供了一套标准,创造性地使用Dockerfile来规范化Docker化应用的制作流程,结果产生的Docker镜像便于传输与管理,最终通过Docker镜像运行Docker容器,完成容器化应用的交付。
经过本系列对于Docker镜像的镜像,大家应该已经清楚Docker镜像的存储、以及Docker镜像的内容。Docker镜像的内容,应该说包含两部分,除了镜像层中的文件之外,还包括一层镜像的json文件。镜像层文件的理解较为简单,但是Docker镜像的json文件理解起来就会稍显复杂。本文就带大家深入理解Docker镜像的json文件。
我们一直提到“通过Docker镜像运行Docker容器”,如果仔细思考这句话,可能依然会存在一些疑惑。不难的理解是:Docker镜像层中的文件全部属于静态的磁盘文件,而Docker容器属于一个动态的产物,可以认为是一个或者多个运行中的进程。那么,静态的Docker镜像转换为动态的Docker容器背后肯定会有一些不为人知的秘密。我们不妨带着以下几个问题来思考Docker镜像的json文件:
1.如何判定一个Docker镜像应该运行哪个进程,这部分信息存在哪?
2.有了以上信息,将Docker镜像运行成Docker容器的行为是谁在主导?
这一次,我们依旧从动态
和静态
这两个词来看Docker镜像的前世今生。首先,Docker镜像的镜像层文件属于静态文件,当容器运行起来之后这部分内容将作为Docker容器的文件系统内容,提供Docker容器的文件系统视角。我们带着这样的观点,再来看Dockerfile的概念。
在Dockerfile的原语中,大家肯定对ENV、VOLUME、EXPOSE以及CMD等命令非常熟悉。
ENV MYPATH=/root
:ENV命令在构建Docker镜像时,为镜像添加一个环境变量,以便该环境变量在启动Docker容器时作用于容器内的进程;这部分信息不应该以静态文件的形式被打入Docker的镜像层文件。
VOLUME /data
:VOLUME命令在构建Docker镜像时,为镜像添加一个数据卷标识,以便通过该镜像运行容器时为容器挂载一个数据卷;由于构建时真实的数据卷还不存在,所以这部分信息不应该以静态文件的形式被打入Docker的镜像层文件。
EXPOSE 80
:EXPOSE命令在构建Docker镜像时,记录容器内部实际监听的端口,以便在通过bridge模式配置Docker容器网络时,将该端口与宿主机进行一次DNAT转换;这部分信息属于Docker容器运行时所需要的信息,也不应该以静态文件的形式被打入Docker的镜像层文件。
CMD ["./run.sh"]
:CMD命令在构建Docker镜像时,记录启动Docker容器的执行命令入口,一般用以指定用户的应用程序;这部分配置信息更不应该以静态文件的形式被打入Docker的镜像层文件。
Dockerfile中以上举例的4类命令,通过分析,我们得出初步的结论:Dockerfile的部分命令各自包含一类动态信息,这类信息不属于Docker镜像层中的文件内容。
高移植性保障了Docker镜像的一次构建,多处运行
,那么使得Docker容器可以顺利运行,Docker自然不会抛弃构建Docker镜像时的动态内容。因此,动态内容的存储就显得尤为重要。
此时就是Docker镜像json文件
登场的时机。构建Docker镜像时,所有动态的信息都会会记录进相应Docker镜像的json文件中。
需要注意的是,虽然镜像的动态信息会被存储于Docker镜像的json文件中,但是并不代表json文件中仅存储动态信息,Dockerfile构建过程中,机会所有的操作都会会记录在json文件中
.
有了Docker镜像json文件来描述Docker容器的动态信息,那么json文件作为Docker镜像的一部分,在Docker体系中,由哪一模块来完成json文件中动态信息的解析与执行呢?
如果大家清楚“每一个Docker容器都是Docker Daemon的子进程”的话,肯定会联想到Docker Daemon。答案正是Docker Daemon。站在启动容器的角度上,Docker Daemon的作用就是以下两点:
1.将Docker镜像的镜像层文件作为Docker容器的rootfs。
2.提取Docker镜像json文件中的动态文件,确定启动进程,并为之配置动态运行环境。
Docker Daemon、Docker镜像以及Docker容器三者的简单示意图如下:
通过上图,我们可以使用ubuntu:14.04镜像运行Docker容器时,前者的镜像层layer内容将作为容器的rootfs;而前者的json文件,会由Docker Daemon解析,并提取出其中的容器执行入口CMD信息,以及容器进程的环境变量ENV信息,最终初始化容器进程。当然,容器进程的执行入口来源于镜像提供的rootfs。假如此时ubuntu 14.04镜像的json文件中又含有VOLUME信息,那么Docker Daemon将会为Docker容器在宿主机上创建一个文件目录,并挂载到容器内部,实现镜像中VOLUME动态信息的运用,其他动态信息的运用也大同小异。
全文分析至此,还是更多的从理论的角度阐述Docker镜像的json文件,那么现实情况中,此类json文件到底存的内容是什么呢?我们依然以ubuntu:14.04为例,揭开Docker镜像json文件的这面目。
一个含有标签的Docker镜像一般会有一个或者多个层级镜像组合而成,而每个镜像层都会含有一个json文件。上图中,我们展现了ubuntu:14.04镜像中4个镜像层的具体情况,特别分析了镜像8251da35e7a7
和e5855facec0b
。通过查看这两个镜像的json文件,我们可以发现,两个json文件中的config属性中,除了理所应当不同的镜像ID之外,只有Cmd属性不同。由于镜像e5855facec0b
是镜像8251da35e7a7
的父镜像,同时构建子镜像的时候使用的Dockerfile命令为CMD ["/bin/bash"]
,因此子镜像在父镜像json文件的基础上,更新config属性中的Cmd属性,完成自身json文件的生成。Docker镜像中父子镜像的json文件有很大的相似性,子镜像仅在父镜像json文件的基础上,修改运行自身对应的Dockerfile命令后造成的差异。
镜像构建完毕,Docker镜像的镜像层layer文件以及json文件均准备完毕,那么当Docker Daemon通过此ubuntu:14.04镜像运行Docker容器时,首先提取最上层镜像的json文件,找到config属性中的Cmd命令,并在镜像层文件构成的容器rootfs中找到相应的执行程序,最终执行,完成容器的启动。(实际情况要更复杂,会涉及Entrypoint以及Cmd两部分的内容,本系列后续会深入深入两者的作用与区别)
当然,除了Cmd之外,json文件的config 属性中同时还存在User、ExposedPorts、Entrypoint、NetworkDisabled等一系列启动容器时所需的动态信息。
Docker镜像的json文件扮演极其重要的角色,提供了静态镜像向动态容器的转化依据,同时清晰地记录了父子镜像之间的差异,基于此差异,后续Docker构建的cache机制才得以实现。
欢迎关注[Docker源码分析]微信公众号,更多精彩即将呈现。