一、Dockerfile简介
Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令,Docker程序将这些Dockerfile指令翻译真正的Linux命令;Dockerfile有自己书写格式和支持的命令,Docker程序解决这些命令间的依赖关系,类似于Makefile,Docker程序将读取Dockerfile,根据指令生成定制的image。
Dockerfile的指令是忽略大小写的,建议使用大写,使用#作为注释,每一行只支持一条指令,每条指令可以携带多个参数。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层, 因此每一条指令的内容,就是描述该层应当如何构建。
二、dockerfile指令详解
1. FROM
功能为指定基础镜像,并且必须是第一条指令。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为scratch。这个镜像 是虚拟的概念,并不实际存在,它表示一个空白的镜像。
如果你以scratch为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始构建。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如swarm、coreos/etcd 。对于Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小 巧。使用Go语言开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
2. MAINTAINER
格式为 MAINTAINER ,指定维护者信息。
3. RUN
用来执行命令行命令
- shell格式:RUN <命令> ,就像直接在命令行中输入的命令一样
- exec格式:RUN [“可执行文件”, “参数1”, “参数2”],这更像是函数调用中的格式
使用 && 符连接多个命令; \ 符进行换行。
为防止镜像臃肿,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉(编译构建所需要的软件,所有下载、展开的文件,apt缓存文件等)。
4. CMD
- shell格式:CMD <命令>
- exec 格式:CMD [“可执行文件”, “参数1”, “参数2”…]
- 参数列表格式:CMD [“参数1”, “参数2”…],提供给 ENTRYPOINT 的默认参数
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
Docker不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用upstart/systemd去启动后台服务,容器内没有后台服务的概念。
CMD service nginx start ,如果这样写会出现容器执行后立即退出了,这主要是因为没搞明白前台和后台的概念,没有区分容器和虚拟机的差异,对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,进而退出,其他辅助进程不是他关心的内容。 上述命令可以理解为 CMD[ “sh”, “-c”, “service nginx start”] ,因此主进程是sh ,sh结束,主进程退出,自然容器也会退出。正确的做法是 CMD [ “nginx”, “-g”, “daemon off;” ]
5. ENTRYPOINT
两种格式:
- ENTRYPOINT [“executable”, “param1”, “param2”]
- ENTRYPOINT command param1 param2(shell中执行)
配置容器启动后执行的命令,ENTRYPOINT 在运行时也可以替代,不过比CMD要略显繁琐,需要通过docker run的参数 -entrypoint来指定。
当指定了ENTRYPOINT后,CMD的含义就发生了改变:
如果CMD中不是完整的命令,则将CMD的内容作为参数传给ENTRYPOINT指令,换句话说实际执行时,将变为 ENTRYPOINT CMD。
如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效。
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。
6. EXPOSE
- EXPOSE <端口1> [<端口2>…]
功能为暴漏容器运行时的监听端口给外部,但是EXPOSE并不会使容器访问主机的端口,如果想使得容器与主机的端口有映射关系,必须在容器启动的时候加上 -P参数。
-P:大写P为自动映射,会将EXPOSE暴露出来的端口随机映射到宿主机的端口上,如果没有暴露端口,就不会有映射。
-p:小写p为手动映射,需要自己指定宿主机的端口和容器的端口,形式为:
-p 宿主机端口:容器端口
(1)无论有没有暴露端口、自动映射或者手动映射,宿主机都可以通过容器ip+port(port随容器内部服务监听端口改变而改变)端口访问服务;
(2)要通过宿主机ip+端口的方式访问服务,宿主机的端口必须与容器端口有映射关系;
(3)如果没有暴露端口,-P自动映射不会映射任何端口,-p可以指定宿主机端口和容器端口形成映射。
7. ENV
- ENV <键> <值>
- ENV <键>=<值> …
两者的区别就是第一种是一次设置一个,第二种是一次设置多个
下列指令可以支持环境变量展开:
ADD、COPY 、ENV、EXPOSE 、LABEL 、USER 、WORKDIR 、VOLUME 、STOPSIGNAL、ONBU ILD
使用环境变量方式: $环境变量
8. COPY
将从构建上下文目录中<源路径>的文件或者目录复制到新的一层的镜像内的<目标路径>位置。
<目标路径>可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用WORKDIR指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
此外,还需要注意一点,使用COPY指令,源文件的各种元数据都会保留。比如读、写、执 行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用Git 进行管理的时候。
- COPY:<源路径>… <目标路径>
- COPY: ["<源路径1>",… “<目标路径>”]
COPY src1 \
src2 \
WORKDIR/
执行上述操作会发现将src1 src2 下的文件全部复制到WORKDIR下,并没有复制目录src1 src2,官方对于COPY解释是:
Note: The directory itself is not copied, just its contents.
即:COPY指令拷贝一个文件夹只会拷贝文件夹的内容。
上述指令可以这样写:
COPY src1 \
src2 \
WORKDIR/src
这样将COPY的文件,放在了新创建的src目录下。
9. ADD
ADD指令和COPY的格式和性质基本一致,不过<源文件>可以是一个URL,同时如果源文件是一个压缩文件,ADD会自动执行解压缩。
因此在COPY 和ADD指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY指令,仅在需要自动解压缩的场合使用ADD 。
10. VOLUME
- VOLUME ["<路径1>", “<路径2>”…]
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存 动态数据的应用,其数据库文件应该保存于卷(volume)中。
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
如何使用:
在Dockerfile中定义匿名卷,运行容器时,使用参数 -v 宿主机目录:匿名卷 进行挂载,实现了数据持久化。
11. WORKDIR
- WORKDIR <工作目录路径>
使用WORKDIR指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
12. USER
- USER <用户名>
USER是改变之后层的执行RUN ,CMD以及ENTRYPOINT这类命令的身份。USER只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
13. ONBUILD
- ONBUILD <其它指令>
ONBUILD是一个特殊的指令,它后面跟的是其它指令,比如RUN ,COPY等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
Dockerfile中的其它指令都是为了定制当前镜像而准备的,唯有ONBUILD是为了帮助别人定制自己而准备的。