docker镜像结构
docker镜像
从整体的角度来讲,一个完整的Docker镜像可以支撑一个Docker容器的运行,在Docker容器运行过 程中主要提供文件系统。例如一个centos:7的镜像,提供了一个基本的centos:7的发行版,当然此镜像是 不包含操作系统Linux内核的。
从根本上讲,一个容器不过是一个正在运行的进程,并对其应用了一些附加的封装功能,以使其与主 机和其他容器隔离。容器隔离的最重要方面之一是每个容器都与自己的专用文件系统进行交互。该文件系 统由Docker镜像提供。镜像包括运行应用程序所需的一切代码或二进制文件,运行时,依赖项以及所需 的任何其他文件系统对象
docker镜像与docker容器的关系
Docker镜像是Docker容器运行的基础,没有Docker镜像,就不可能有Docker容器,这也是Docker的设计原则之一。
可以理解的是Docker镜像毕竟是镜像,属于静态的内容。而Docker容器就不一样了,容器属于动态的内容。动态的内容,大家很容易 联想到进程,内存,CPU等之类的东西。的确,Docker容器作为动态的内容,都会包含这些
为了便于理解,大家可以把Docker容器,理解为一个或多个运行进程,而这些运行进程将占有相应的内存,相应的CPU计算资 源,相应的虚拟网络设备以及相应的文件系统资源。而Docker容器所占用的文件系统资源,则通过Docker镜像的镜像层文件来提供。
那么作为静态的镜像,如何才有能力转化为一个动态的Docker容器呢?其实,转化的依据是每个镜像的json文件,Docker可以通 过解析。Docker镜像的json的文件,获知应该在这个镜像之上运行什么样的进程,应该为进程配置怎么样的环境变量,此时也就实现了静 态向动态的转变。
是由Docker守护进程来完成这个转化工作的。在上个小节中我们也提到:Docker容器实质上就是一个或者多个进程,而容器的父 进程就是Docker守护进程。这样的,转化工作的执行就不难理解了。Docker守护进程 手握Docker镜像的json文件,为容器配置相应的 环境,并真正运行Docker镜像所指定的进程,完成Docker容器的真正创建。
Docker容器运行起来之后,Docker镜像json文件就失去作用了。此时Docker镜像的绝大部分作用就是为Docker容器提供一个文 件系统,供容器内部的进程访问文件资源。
docker镜像结构
base镜像
1、不依赖其他镜像,从scratch构建。
2、以此为基础镜像,进行扩展。
base镜像通常都是各种Linux发行版的Docker镜像。不同Linux发行版的区别主要就是rootfs。 比如Ubuntu 14.04使用upstart管理服务,apt管理软件包;而 CentOS7使用systemd和yum。这 些都是用户空间上的区别,Linux kernel差别不大。所以Docker可以同时支持多种Linux镜像,模拟 出多种操作系统环境。容器只能使用Docker host的kernel,并且不能修改
分层结构
镜像层
Docker里的镜像绝大部分都是在别的镜像的基础上去进行创建的,也就是使用镜像的分层结 构。实际上,Docker Hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来 的。比如我们现在构建一个新的镜像,具体操作如下:
[root@node1 ~]# vim Dockerfile [root@node1 ~]# cat Dockerfile FROM centos:7 RUN mkdir /cc RUN touch /cc/zz CMD ["/bin/bash"] [root@node1 ~]# docker build -t centos:cz . [+] Building 0.5s (7/7) FINISHED docker:default => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 100B 0.0s => [internal] load metadata for docker.io/library/centos:7 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/3] FROM docker.io/library/centos:7 0.0s => [2/3] RUN mkdir /cc 0.2s => [3/3] RUN touch /cc/zz 0.2s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:735d8a6751d120ce45d73b8d4c913fc82c354f0e425a8ab91af28e8bb8619735 0.0s => => naming to docker.io/library/centos:cz 0.0s [root@node1 ~]#
查看构建过程
[root@node1 ~]# docker history centos:cz IMAGE CREATED CREATED BY SIZE COMMENT 735d8a6751d1 47 seconds ago CMD ["/bin/bash"] 0B buildkit.dockerfile.v0 <missing> 47 seconds ago RUN /bin/sh -c touch /cc/zz # buildkit 0B buildkit.dockerfile.v0 <missing> 47 seconds ago RUN /bin/sh -c mkdir /cc # buildkit 0B buildkit.dockerfile.v0 <missing> 3 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 3 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B <missing> 3 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
容器层
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之 下的都叫“镜像层”。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相 同路径的文件,比如/galaxy,上层的/galaxy会覆盖下层的/galaxy,也就是说用户只能访问到上层中的 文件 /galaxy。在容器层中,用户看到的是一个叠加之后的文件系统。
docker镜像构建介绍
Docker镜像可以通过Docker hub或者阿里云等仓库中获取,这些镜像是由官方或者社区人员提 供的,对于Docker用户来说并不能满足我们的需求,但是从无开始构建镜像成本大。常用的数据 库、中间件、应用软件等都有现成的Docker官方镜像或社区创建的镜像,我们只需要稍作配置就可 以直接使用
使用现成镜像的好处除了省去自己做镜像的工作量外,更重要的是可以利用前人的经验。特别是 使用那些官方镜像,因为Docker的工程师知道如何更好的在容器中运行软件。
当然,某些情况下我们也不得不自己构建镜像,比如找不到现成的镜像,比如自己开发的应用程 序,需要在镜像中加入特定的功能
docker镜像构建三种方法
Docker提供了三种构建镜像的方法:
1、docker commit命令
docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我 们所需要的内容,再使用docker commit将容器打包为镜像即可。
下面展示一个示例:在centos的base镜像中安装vim-common并保存为新镜像。
1、先基于centos7运行容器,容器名为cc,并使用-it生成终端进入容器。 [root@node1 ~]# docker run -itd --name centos-test centos:7 41ade8600a7238a6461a223d89628dd47e8b8c7e8061104be758925ae6085456 [root@node1 ~]# docker exec -it centos-test /bin/bash 2,创建容器目录,文件 [root@41ade8600a72 /]# mkdir /chenchen [root@41ade8600a72 /]# touch /chenchen/file1 退出 [root@41ade8600a72 /]# exit exit 3,打包容器 [root@node1 ~]# docker commit centos-test file-test sha256:d60c2c3057bbb2c57f1950f8c9cb9abe027acddde2aea1c69446d14522d9b1f9 4,查看镜像 [root@node1 ~]# docker images | grep file file-test latest d60c2c3057bb 30 seconds ago 204MB
这样一个新的镜像就构建完成了,centoscy:7镜像是在centos:7镜像基础之上创建的,通过查看镜像 属性,发现centoscy:7要比centos:7镜像大一些。
然而,Docker并不建议用户通过这种方式构建镜像。这是一种手工创建镜像的方式,容易出错,效 率低且可重复性弱。更重要的,使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说 无法对镜像进行审计,存在安全隐患。
2、基于本地模板导入
用户可以直接从一个操作系统模板文件导入一个镜像,主要使用 docker [container] import 命令。 命令 格式为 docker [image] import [OPTIONS] file|URL|-[REPOSITORY[:TAG]] ,要直接导入一个镜 像,可以使用 OpenVZ 提供的模板来创建,或者用其他已导入的镜像模板来创建。OpenVZ 模板的下载 地址为 http://openvz.org/Download/templates/precreated。
下载了 ubuntu:12.04 的模板压缩包,之后使用以下命令导入即可:
[root@node1 ~]# rz -E rz waiting to receive. [root@node1 ~]# cat ubuntu-12.04-x86-minimal.tar.gz | docker import - ubuntu:12.04 sha256:89e6753caa310768bc7f3edb65940013736fdaae792db352a417f6122a79d6a4 [root@node1 ~]# docker images | grep ubuntu ubuntu 12.04 89e6753caa31 23 seconds ago 146MB [root@node1 ~]#
3,Dockerfile构建文文件
dockerfile构建文件
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件 外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构 建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问 题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就Dockerfile。
Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因 此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的 需求时,只需在 Dockerfile上添加或者修改指令,重新生成镜像即可,省去了敲命令的麻烦。
dockerfile文件格式
Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基 于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如ADD指令。每执行一条ADD指令,镜像 添加新的一层,并提交;最后是CMD指令,来指明运行容器时的操作命令。
示例如下,我们构建一个httpd镜像。
[root@docker ~]# ls Dockerfile [root@docker ~]# vim Dockerfile # 1、第一行必须指定,基础镜像信息 FROM centos:7 # 2、维护者信息 MAINTAINER chen@example.com 3、镜像操作指令 RUN yum install -y httpd EXPOSE 80 # 4、容器启动执行指令 CMD ["/bin/bash"]
把构建容器所需要的指令都存放在Dockerfile文件中,这个文件的名字是固定的,不能够更改,再使用docker
build命令构建容器,使用-t定义新的镜像名,如果构建镜像的Dockerfile文件不在当前目录下可以使用-f指定
Dockerfile文件路径,示例如下:[root@docker ~]# docker build -t httpd:cycy /root/ 通过以上镜像的构建过程可以看出,Dockerfile文件内的指令会逐一运行,构建过程如下: 1、下载centos7镜像。 2、添加镜像构建者信息 3、基于centos7镜像启动容器,安装httpd软件,安装完毕后将容器打包为镜像。 4、基于上一步生成的镜像启动容器,将80端口打开,打开后将容器打包为镜像。 5、基于上一步生成的镜像启动容器,添加容器启动后需要执行的指令,再打包为镜像。 也就说一条指令就是一层镜像,还可以通过docker history查看镜像的构建过程,这样我们构建的镜像就呈现出透明化,整个构建 的过程都可以看到。 [root@docker ~]# docker history httpd:cycy IMAGE CREATED CREATED BY SIZE COMMENT b44f420b6834 10 minutes ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B 65c05b81e846 10 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B e7e8c35f9b15 10 minutes ago /bin/sh -c yum -y install httpd 203MB a9c67ae8f88e 13 minutes ago /bin/sh -c #(nop) MAINTAINER chenyu@example… 0B eeb6ee3f44bd 9 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 9 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B <missing> 9 months ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
镜像缓存
使用DockerFile文件构建完镜像以后,Docker会把构建过程中的每一层临时镜像进行缓存。在构建新镜像时,可 以直接使用之前缓存的镜像层,这样能加速镜像的构建。镜像缓存示例如下:
[root@docker ~]# vim Dockerfile # 1、第一行必须指定,基础镜像信息 FROM centos:7 # 2、维护者信息 MAINTAINER chen@example.com # 3、镜像操作指令 RUN mkdir /galaxy 修改之前的Dockerfile文件,然后我们再构建新的镜像,构建过程如下,通过构建过程可以得知,DockerFile文件 里面共三条指令,前两条指令都是用之前构建镜像的缓存,只有第三个指令才重新构建了缓存层。如果希望在构建镜像 时不使用缓存,可以在docker build命令中加上--no-cache参数。 [root@docker ~]# docker build -t cccentos:7 .
dockerfile语法
Dockerfile是由一系列指令和参数构成的脚本,一个Dockerfile里面包含了构建整个镜像的完整指令。Docker通 过docker build执行Dockerfile中的一系列指令自动构建镜像,常用的Dockerfile指令有以下几种。
1、FROM
指令必须为Dockerfile文件开篇的第一个非注释行,用于指定构建镜像所使用的基础镜像,后续的指令 运行都要依靠此基础镜像所提供的的环境。实际使用中,如果没有指定仓库,docker build会先从本机查找是否有此基 础镜像,如果没有会默认去Docker Hub Registry上拉取,再找不到就会报错,格式如下。
FROM <Repository> [:<Tag>]
FROM <Repository>@<Digest>
#Digest:镜像的哈希码,防止镜像被冒名顶替。
2.MAINTAINER
MAINTAINER指令用于让Dockerfile的作者提供个人的信息,Dockerfile并不限MAINTAINER指令的位置, 但是建议放在FROM指令之后,在较新的Docker版本中,已经被LABEL替代,格式如下。
MAINTAINER "cc@example.com
3.LABEL
LABEL指令用于让用户为镜像指定各种元数据(键值对的格式),格式如下。
LABEL <key>=<value> <key>=<value>
4.copy
COPY指令用于复制宿主机上的文件到目标镜像中,格式如下。
COPY <src>..<dest>
COPY ["<src>",.."<dest>"]
<src>:要复制的源文件或者目录,支持通配符
<src>:目标路径,即正创建的镜像的文件系统路径,建议使用绝对路径,否则,COPY指令会以 WORKDIR为其起始路径。如果路径中如果包含空白字符,建议使用第二种格式用引号引起来,否则会被 当成两个文件。
5.WORKDIR
WORKDIR指令用于指定工作目录,可以指多个,每个WORKDIR只影响他下面的指令,直到遇见 下一个WORKDIR为止。WORKDIR也可以调用由ENV指令定义的变量。,格式如下
WORKDIR 相对路径或者绝对路径
6.VOLUME
VOLUME指令用于在镜像中创建一个挂载点目录。Volume有两种类型:绑定挂载卷和docker管 理的卷。在Dockerfile中只支持Docker管理的卷,也就是说只能指定容器内的路径,不能指定宿主机的路 径,格式如下。VOLUME <,mountpoint>
VOLUME ["<mountpoint>"]
8、EXPOSEEXPOSE指令用于指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访 问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配-P的参数才能将待暴露的状态转换 为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟-p :443一个意思。EXPOSE指令可以一 次指定多个端口,例如:EXPOSE 11111/udp 11112/tcp,格式如下。
EXPOSE<port> [<protocol>] [<port>] [/<protocol>]...]
9、ENVENV指令用于为镜像定义所需的环境变量,并可被ENV指令后面的其它指令所调用。调用格式为$variable_name或者 ${variable_name},使用docker run启动容器的时候加上-e的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指 定的此variable_name的值。但是不会影响到Dockerfile中已经引用过此变量的文件名,格式如下。
ENV <key> <value>
ENV <key>=<value> ...
<command> 通常是一个shell命令,系统默认会把后面的命令作为shell的子进程来运行,以"/bin/sh -c"来运行它。 第二种格式的参数是一个JSON格式的数组,其中"executable"为要运行的命令,后面的"paramN"为传递给命令的选项或参数。
11.CMDCMD指令用于用户指定启动容器的默认要运行的程序,也就是PID为1的进程命令,且其运行结束后容器也会终止。如果 不指定,默认是bash。CMD指令指定的默认程序会被docker run命令行指定的参数所覆盖。Dockerfile中可以存在多个CMD 指令,但仅最后一个生效。因为一个Docker容器只能运行一个PID为1的进程。类似于RUN指令,也可以运行任意命令或程 序,但是两者的运行时间点不同。RUN指令运行在docker build的过程中,而CMD指令运行在基于新镜像启动容器时,格式 如下。
CMD command param1 param2
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
前两种语法格式同RUN指令。第一种用法对于CMD指令基本没有意义,因为它运行的程序PID不为1。 第三种则需要结合ENTRYPOINT指令使用,CMD指令后面的命令作为ENTRYPOINT指令的默认参数。如果docker run命令行 结尾有参数指定,那CMD后面的参数不生效。
12.ENTRYPOINTENTRYPOINT指令类似CMD指令的功能,用于为容器指定默认运行程序。Dockerfile中可以存在多个ENTRYPOINT指 令,但仅最后一个生效,与CMD区别在于,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且这 些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序,格式如下。
ENTRYPOINT command param1 param2
ENTRYPOINT ["executable", "param1", "param2"]
不过,docker run的--entrypoint选项的参数可覆盖ENTRYPOINT指定的默认程序。
13.userUSER用于指定docker build过程中任何RUN、CMD等指令的用户名或者UID。默认情况下容器的运行用户为root, 格式如下
USER <user>[:<group>]
USER <UID>[:<GID>[
总结
1、镜像的来源多种多样,可以通过Docker Hub社区获取,或者通过国内的公有镜像仓库中获取。、
2、Docker镜像是Docker容器运行的基础,没有Docker镜像,就不可能有Docker容器,这也是Docker的设计原则之一。
3、Docker可以同时支持多种Linux镜像,模拟出多种操作系统环境。容器只能使用Docker host的kernel,并且不能修改。
4、base镜像有两层含义:不依赖其他镜像,从scratch构建;以此为基础镜像,进行扩展。 5、构建镜像的方式有三种:使用docker commit构建、基于本地模板导入和使用Dockerfile构建构建。
6、docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我们所需要的内容,再 使用docker commit将容器打包为镜像即可。
7、用户可以使用docker import命令直接从一个操作系统模板文件导入一个镜像。
8. 构建镜像的方式有三种:使用docker commit构建、基于本地模板导入和使用Dockerfile构建构建。
9、Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是 描述该层应当如何构建。
10、在Dockerfile语法中,RUN、CMD都是运行命令的指令,RUN是在构建镜像时运行的命令,CMD是容器启动时运行的命 令