简介
dockerfile是专门用来构建镜像的文件,使用docker build
命令来进行镜像的构建。
通常为docker build -t ${imageName}:${tagName} .
,指使用当前目录下的Dockerfile文件来进行镜像构建。如果需要指定具体某个Dockerfile文件,可以使用-f
参数,例如docker build -t ${imageName}:${tagName} -f /path/to/dockerfile .
来进行构建。
Dockerfile关键字/语法
一个常见的Dockerfile一般为这个:
FROM ubuntu:16.04
COPY ./a.txt /tmp
下面一一来说明下常用的一些指令。
FROM
FROM指的是这个镜像要从哪个镜像进行构建,语法为:
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
如果image在本地不存在,会从远程仓库中先pull在执行
RUN
RUN会执行相应的命令,同时会在当前docker的镜像中写入一个新的layer层并提交。,基本语法有:
RUN <command>
RUN ["executable","param1","param2"]
推荐使用第二种,同时注意:
- 要使用双引号而不是单引号,如果在第一种写法中有换行需求,使用反斜杠
\
来执行 - 如果我们要使用反斜杠来代表反斜杠时,注意要写两个
\\
来代表 - Unlike the shell form, the exec form does not invoke a command shell. This means that normal shell processing does not happen。For example, RUN [ "echo", "$HOME" ] will not do variable substitution on $HOME。
- RUN执行会再下次执行build时被缓存,如果不想要缓存而是真正从头构建,那么构建时需要添加
--no-cache
标识
CMD
官网对CMD的主要用途的说明是:用来在执行一个容器时提供默认值。因此,每个Dockerfile文件中只能有一个CMD指令。
CMD有3种写法
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
注意第一种是最佳实践的写法,同时基本上CMD和ENTRYPOINT配合使用的。
注意:
- 如果你需要你的command不是在shell下执行,那么你必须使用第一种写法,同时给你的二进制文件指定成绝对路径
LABEL
LABEL用来给镜像添加一些元数据,一个LABEL执行是一个key-value键值对。基本语法如下:
LABEL <key>=<value> <key>=<value> <key>=<value>...
MAINTAINER
MAINTAINER指令用来在生成的镜像中显示作者是谁。基本语法如下:
MAINTAINER <name>
不过现在看官方文档说这个指令即将被废弃了,推荐用LABEL标签来替代这个功能,例如:LABEL maintainer="SvenDowideit@home.org.au"
EXPOSE
EXPOSE指令指定了一个容器在运行时指定要监听的指定类型的网络端口。可以指定TCP和UDP类型,默认是TCP。语法如下:
EXPORT <port> [<port>/<protocol>...]
不过需要注意的是,EXPOSE并不会真正的暴露这些端口出来,他只是一种在镜像的构建者和使用者之间的文档说明功能,告诉哪些端口会暴露。当运行一个容器时,在执行docker run
命令时,需要使用-p
或者-P
标签来暴露端口
ENV
ENV指令用来设置环境变量,具体的语法为:
ENV <key> <value>
ENV <key>=<value> ...
第一种为设置一个环境变量,第二种为在一行内设置多个环境变量,这些环境变量会在后续的Dockerfile中被用到,不过需要注意的是,将环境变量持久化可能会造成一些不期望的副作用,如果要在某个命令中使用某个环境变量,可以使用RUN <key>=<value> <command>
ADD
ADD指令用来将宿主机上的文件,目录或者远程地址的文件复制到镜像中,有两种语法:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
注意:
- src支持通配符,例如
ADD home* /mydir/
会将home开头的所有文件复制到镜像的/mydir/文件下 - dest必须为绝对地址,如果设置为相对地址时,那么是相对于
WORKDIR
的 --chown
只能是Linux容器使用,windows下无法使用- src的路径如果是本地,那么必须是在当前执行
docker build
的上下文环境也就是同一个目录下的,不能执行../../
这种路径 - 如果src是一个目录,那么目录下的所有文件都会被复制,包括文件的元数据,不过注意,目录本身不会被复制
- 如果scr是一个压缩文件,那么ADD操作会默认直接进行解压成对应的目录,但是如果是一个远程地址不会执行解压操作
- ADD指令在一个Dockerfile找那个支持执行多次
COPY
COPY指令用来复制文件或者目录到相应的路径,基本的语法是:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (this form is required for paths containing whitespace)
COPY指令和ADD差不多,只是不同的是如果src是一个压缩文件,并不会和ADD一样执行解压操作
ENTRYPOINT
ENTRYPOINT指令用来当容器启动的时候执行什么命令,基本的语法为:
ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)
其实官网文档说的蛮清楚,懒得翻译了,直接贴过来吧。
Command line arguments to docker run
有一点值得注意的是,虽然CMD和RUN指令也能和ENTRYPOINT一样做同样的事情,但是启动的时候会作为/bin/sh -c
的子命令,进程的PID不是1,会导致执行docker stop时收不到unix的信号量例如SIGTERM
。
注意,如果你写了多个ENTRYPOINT,只有最后一个会生效。
最后,基本都是entrypoint配合CMD来一起使用的,这样方便容器的使用者通过传参进来后覆写CMD指令,所以一般都是CMD用来做程序的参数,ENTRYPOINT执行相应的程序,例如:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
关于具体如何使用ENTRYPOINT和CMD来配合,官方文档说的蛮清楚,详见https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact
VOLUME
VOLUME指令创建一个指定名称以及从宿主机或者其他容器进行挂载的挂载点。基本的语法如下:
VOLUME ["/data"]
VOLUME /var/log /var/db
关于Docker中的volume,可以看下官方文档对volume的一些说明:
USER
USER指令用来在运行一个镜像或者是在接下来的Dockerfile中执行任何RUN
,CMD
,ENTRYPOINT
时设置具体的用户名或者UID,或者同时再指定用户组或者GID
注意,如果user没有一个主要的组将会在root用户组下运行
WORKDIR
WORKDIR指令是设置在执行接下来的RUN
,CMD
,ENTRYPONT
,COPY
,ADD
指令时的工作目录。如果在Dockerfile中没有设置WORKDIR,那么WORKDIR将会自动创建.
有这些需要注意的:
WORKDIR指令可以在Dockerfile中多次使用,只是如果第一次是绝对路径而后面有相对路径,那么这个相对路径是基于上面的绝对路径的,例如:
WORKDIR /a WORKDIR b WORKDIR c RUN pwd
这里,pwd的执行路径是
/a/b/c
WORKDIR可以使用Dockerfile中设置的环境变量,例如:
ENV DIRPATH /path WORKDIR $DIRPATH/$DIRNAME RUN pwd
pwd的执行路径为
/path/$DIRNAME
ARG
ARG指令是定义一个变量,同时这个变量的时在执行docker build
时通过--build-arg <varname>=<value>
标识传递进来。如果定义了一个变量但是用build的时候没有传递,将会收到如下警告:
[Warning] One or more build-args [foo] were not consumed.
基本的语法如下:
ARG <name>[=<default value>]
Dockerfile可以包括多个ARG指令。如果设置了默认值,用户在build时没有传递将会用默认值代替。同时需要注意的是ARG生效的位置是从被定义的位置开始,而不是从被使用的那行开始。看下面这个例子:
FROM busybox
USER ${user:-some_user}
ARG user
USER $user
用户通过下列命令来进行构建
docker build --build-arg user=what_user .
第二行中USER因为默认值的原因值为some_user,然而在第三行user变量才被定义。第四行中,定义的user变量被通过构建时传进来的what_user赋值。在被ARG定义之前,任何使用这个变量的值都为空字符串。
需要注意的是,ENV和ARG都可以做差不多类似的事情,只是ENV会在镜像中被持久化,而ARG只在构建过程中能够有效
ONBUILD
ONBUILD指令比较有趣,用来当构建出来的镜像被别人用做base镜像也就是FROM xxx时才会被触发。基本语法为:
ONBUILD [INSTRUCTION]
例如:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
不过需要注意的是,ONBUILD的指令不能再次包含ONBUILD指令来递归操作。同时,ONBUILD只能可能不会触发FROM和MAINTAINER指令。
基本的指令和基本用法就这些了,其他的可以看官网文档