什么是dockerfile

Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。

Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。

Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。

可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。

一、FROM和RUN 指令的作用

执行时机:都在docker build时执行

FROM:定制的镜像都是基于 FROM 的镜像 , 为了指定基本映像,第一条指令必须是FROM

RUN:用于执行后面跟着的命令行命令。有以下俩种格式:

shell 格式:

RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。

exec 格式:

RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:

FROM centos
RUN yum install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:
FROM centos
RUN yum install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

二、CMD和 ENTRYPOINT 指令的作用

执行时机:都在docker run时执行

CMD:类似于 RUN 指令,用于运行程序,但二者执行的时间点不同, 如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效

注意,如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。

格式

CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

ENTRYPOINT :类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖。如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效

格式

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。
三、COPY 和 ADD 指令的作用

COPY : 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

ADD : ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY)

COPY 和 ADD 指令不同之处
ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

格式:

COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]
[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

四、设置环境变量ENV和ARG

区别:如果使用ENV定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

五、声明端口EXPOSE

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务

FROM nginx
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html
EXPOSE 3000

构建完成后执行:docker run -p 3000:3000 -d nginx:v0,在浏览器中输入localhost:3000,访问不到服务。

nginx运行的时候对外提供的端口默认是80,即便你在这里声明了3000,也不会改变默认的端口80。这个EXPOSE毛线用都没有,一般是镜像创建者书写的,一旦写错了,就会误导用户,将人带到坑里。因此,在声明EXPOSE的时候,一定要实现查明当前容器默认的服务端口。

既然nginx默认提供的端口是80,那么在启动docker的时候,宿主机和容器的端口映射就存在限制。宿主机这边端口随意,只要这个端口没有被占有就可以。容器这边必须是80端口。比如我的是3000:80 ,在浏览器中输入http://127.0.0.1:3000就可以访问到容器提供的服务。

EXPOSE可以不用但是不能没有,因为 Dockerfile 不一定是一个人维护的,或者说当下一个运维人员接手项目之后能够通过 Dockerfile 里面的参数掌握整体的逻辑,一切还是为了规范。

六、指定工作目录WORKDIR

WORKDIR指令为Dockerfile中的任何 RUN、CMD、ENTRYPOINT、COPY 和 ADD指令设置工作目录。如果WORKDIR不存在,即使在后续的Dockerfile指令中不使用它,也会创建它