定义及其功能阐述
Dokcerfile 是一个普通的文本文件,文件名一般叫 Dockerfile,其中包含了一系列的指令(Instruction), 每一条指令都会构建一层,就是描述该层是如何创建的。
我们可以像写脚本一样把操作docker的命令写在这个文件里,执行这个文件就相当与执行一系列docker命令来构建镜像
,并且会自己提交到本地从仓库,我们如果想改变镜像,操作这个文件就可以,分享这个文件也就相当于分享镜像,更加方便
指令详解
例子
FROM centos:latest
LABEL maintainer="yangge <yangge@qf.com>" description="Install tree vim*"
RUN rpm -qa | grep tree || yum install -y tree vim*
FORM 定义一个基础镜像
LABEL 定义一些元数据信息,比如作者、版本、关于镜像的描述信息
RUN 执行命令行的命令
构建镜像
docker bulid -t 仓库名/镜像名:tag .
这个 . 表示当前目录,这实际上是在指定上下文的目录是当前目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件
docker build -t centos:1.20 .
Successfully built adc30981bc84 # 表示构建成功
Successfully tagged centos:1.20 # TAG 标签
FROM 指令
主要作用是指定一个镜像作为构建自定义镜像的基础镜像,在这个基础镜像之上进行修改定制。
这个指令是 Dockerfile 中的必备指令,同时也必须是第一条指令。
在 Docker Store 上有很多高质量的官方镜像,可以直接作为我们的基础镜像。
ADD指令
例子
[root@localhost hello_qf]# ls
Dockerfile hello hello.c
[root@localhost hello_qf]# cat Dockerfile
FROM scratch
ADD hello /
CMD ["/hello"]
ADD 是把当前目录下的 hello 文档拷贝到 容器中的根目录下
CMD 执行根目录下的 hello 文件
ENV 指令
用于设置环境变量
格式有两种:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
示例:
推荐的方式,易读
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
下列指令可以支持环境变量: ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD
RUN 指令
RUN 指令是在容器内执行 shell 命令,默认会是用 /bin/sh -c 的方式执行。
执行命令的两种方式
RUN <command>(shell形式,该命令在shell中运行)
RUN ["executable", "param1", "param2"](exec形式)
之前说过,Dockerfile 中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。
注意:Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。所以,在使用 shell 方式,尽量多的使用续行符\
CMD 指令
Dockerfile 中只能有一条CMD指令。如果列出多个,CMD 则只有最后一个CMD会生效。
CMD 主要目的是为运行容器时提供默认值
Docker 不是虚拟机,容器就是进程,CMD 指令就是用于指定默认的容器主进程的启动命令的。在启动(运行)一个容器时可以指定新的命令来替代镜像设置中的这个默认命令。
可以包含可执行文件,当然也可以省略。
CMD 指令的格式和 RUN 相似,也是两种格式:
shell 格式:CMD <命令>
exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
注意:不要混淆RUN 和 CMD。RUN实际上运行一个命令并提交结果; CMD在构建时不执行任何操作,但指定镜像的默认命令。
Docker 不是虚拟机,容器内没有后台服务的概念。
不要期望这样启动一个程序到后台:
CMD systemctl start nginx
这行被 Docker 理解为:
CMD ["sh" "-c" "systemctl start nginx"]
ps: sh的一些选项参数理解
-c string:命令从-c后的字符串读取。
-i:实现脚本交互。
-n:进行shell脚本的语法检查。
-x:实现shell脚本逐条语句的跟踪。
对于容器而言,其启动程序就是容器的应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
就像上面的示例中,主进程是 sh , 那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会使容器退出。
正确的做法是直接执行 nginx 这个可执行文件,并且关闭后台守护的方式,使程序在前台运行。
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT 指令
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器的启动程序及参数。
ENTRYPOINT 在运行时也可以被替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
ENTRYPOINT 的格式和 RUN 指令格式一样,也分为 exec 格式和 shell 格式。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,也就是实际执行时,将变为:
<ENTRYPOINT> "<CMD>"
WORKDIR 指令
用于声明当前的工作目录,以后各层的当前目录就被改为指定的目录。
格式为 WORKDIR <工作目录路径>。
如该目录不存在,WORKDIR 会帮你建立目录。
再次强调!不要以为编写 Dockerfiel 是在写 shell 脚本。
下面是一个错误示例:
RUN cd /app
RUN echo "hello" > world.txt
如果将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /app/world.txt 文件,或者其内容不是 hello。
原因其实很简单,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。这就是对 Dockerfile 构建分层存储的概念不了解所导致的错误。
之前说过每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。
两行 RUN 分别构建了并启动了各自全新的容器。
因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。
FROM alpine
WORKDIR /a/b
RUN touch a_b_f.txt
WORKDIR /a
RUN touch a_f.txt
[root@localhost workdir]# docker run -it alpine:workdir /bin/sh
/a # ls
a_f.txt b
/a # cd b
/a/b # ls
a_b_f.txt
COPY 指令
格式:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。
<目标路径> 可以是容器内的绝对路径,也可以是相对于 WORKDIR 指定的工作目录的相对路径。目标路径不需要事先创建,如果目录不存在会在复制文件前先被创建。
COPY 指令将会从构建的上下文目录中,把源路径的文件或目录复制到新的一层的镜像内的 <目标路径> 位置。比如:
COPY qf.json /usr/src/app/
注意下面是错误的
COPY qf.json /usr/src/app
这样会把 qf.json 拷贝成为 /usr/src/ 目录下的 app 文件
<源路径> 可以是多个,支持通配符,如:
COPY qf* /app/
COPY q?.txt /app/
使用 COPY 指令,源文件的各种元数据都会保留。
比如读、写、执行权限、文件变更时间等。
ADD 指令
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
支持自动解压缩,压缩格式支持: gzip, bzip2 以及 xz
官方推荐使用 COPY 进行文件的复制。
ADD 指定会使构建镜像时的缓存失效,导致构建镜像的速度很慢。
COPY 和 ADD 指令中选择的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
ADD qf.tar.gz /
有疑问加站长微信联系(非本文作者)