Dockerfile命令及注意事项

Dockerfile 官方文档地址:

https://docs.docker.com 中Command and API references -> Dockerfile references
即:
https://docs.docker.com/engine/reference/builder/

Dockerfile reference

Docker can build images automatically by reading the instructions from a Dockerfile.
A Dockerfile is a text document that contains all the commands a user could call on the
command line to assemble an image. Using docker build users can create an automated
build that executes several command-line instructions in succession.

Docker 可以通过读取 Dockerfile 中的指令来自动构建镜像.
Dockerfile 是一个文本文件, 其中写有用户所有需要的通过命令行执行来构建镜像的命令.
通过 docker build 命令, 用户可以连续自动化的执行这些命令来构建镜像.

This page describes the commands you can use in a Dockerfile. When you are done reading
this page, refer to the Dockerfile
Best Practices
for a tip-oriented guide.

本页描述 Dockerfile 中可用的指令. 当你读完本页后, 可以参考 Dockerfile
最优实践 .

Usage 使用

The docker build command builds an image from a Dockerfile and a context.
The build’s context is the files at a specified location PATH or URL.
The PATH is a directory on your local filesystem. The URL is a the location
of a Git repository.

docker build 命令使用 Dockerfilecontext 来构建镜像. build 命令的上下文是指定的
PATHURL. PATH 本机的文件夹, URL 是 Git Repo 的地址.

A context is processed recursively. So, a PATH includes any subdirectories and
the URL includes the repository and its submodules.

上下文将被递归的处理. 所以, PATH 将会包含子文件夹, URL 将包含 Repo 和子模块.

A simple build command that uses the current directory as context:

最简单的依据当前文件夹为上下文的命令例子:

$ docker build .
Sending build context to Docker daemon  6.51 MB
...

The build is run by the Docker daemon, not by the CLI. The first thing a build process
does is send the entire context (recursively) to the daemon. In most cases, it’s best to
start with an empty directory as context and keep your Dockerfile in that directory.
Add only the files needed for building the Dockerfile.

build 命令将被 Docker 后台服务执行而不是 Docker 客户端. build 处理的第一件事是将整个上下文递归
的发送给后台服务. 大多数情况下, 最好是将 Dockerfile 放在空文件夹下, 然后仅仅将 Dockerfile
需要的文件放到该文件夹下.

Warning: Do not use your root directory, /, as the PATH as it causes the build to
transfer the entire contents of your hard drive to the Docker daemon.

注意: 千万不要使用 root 路径/, 作为 PATH, 因为这将会将整个硬盘内容传递给 Docker 后台服务.

To use a file in the build context, the Dockerfile refers to the file specified in
an instruction, for example, a COPY instruction. To increase the build’s performance,
exclude files and directories by adding a .dockerignore file to the context directory.
For information about how to
create a .dockerignore file
see the documentation on this page.

Traditionally, the Dockerfile is called Dockerfile and located in the root of the
context. You use the -f flag with docker build to point to a Dockerfile anywhere
in your file system.

$ docker build -f /path/to/a/Dockerfile .

You can specify a repository and tag at which to save the new image if the build succeeds:

$ docker build -t shykes/myapp .

The Docker daemon runs the instructions in the Dockerfile one-by-one, committing the
result of each instruction to a new image if necessary, before finally outputting the ID
of your new image. The Docker daemon will automatically clean up the context you sent.

Note that each instruction is run independently, and causes a new image to be created -
so RUN cd /tmp will not have any effect on the next instructions.

Whenever possible, Docker will re-use the intermediate images (cache), to accelerate the
docker build process significantly. This is indicated by the Using cache message in the
console output. (For more information, see the
Build cache section)
in the Dockerfile best practices guide:

$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 0 : FROM alpine:3.2
 ---> 31f630c65071
Step 1 : MAINTAINER SvenDowideit@home.org.au
 ---> Using cache
 ---> 2a1c91448f5f
Step 2 : RUN apk update &&      apk add socat &&        rm -r /var/cache/
 ---> Using cache
 ---> 21ed6e7fbb73
Step 3 : CMD env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \& wait/' | sh
 ---> Using cache
 ---> 7ea8aef582cc
Successfully built 7ea8aef582cc

When you’re done with your build, you’re ready to look into
Pushing a repository to its registry.

Format 命令格式

Here is the format of the Dockerfile:

Dockerfile 命令的格式为

\# Comment
\# 以#开头为注释, 第一个为命令, 大写, 后面跟对应的参数
INSTRUCTION arguments

The instruction is not case-sensitive, however convention is for them to be
UPPERCASE in order to distinguish them from arguments more easily.

命令大小写不敏感, 但为了防止混淆, 尽量使用大写.

Docker runs the instructions in a Dockerfile in order. The first instruction must be
FROM in order to specify the
Base Image
from which you are building.

Docker 依照 Dockerfile 中命令的顺序执行. 第一条命令必须是 FROM 以便于指定镜像基于哪一个镜像构建.

Docker will treat lines that begin with # as a comment. A # marker anywhere else in
the line will be treated as an argument. This allows statements like:

以#开头的是注释, 但非开头的将被认为是参数.

\# Comment
RUN echo 'we are running some # of cool things'

Here is the set of instructions you can use in a Dockerfile for building images.

下面是 Dockerfile 中可以使用的命令的简介.

Environment replacement 环境变量的替换

Environment variables (declared with the ENV statement) can also be used in certain instructions as variables to be interpreted by the Dockerfile. Escapes are also handled for including variable-like syntax into a statement literally.

环境变量(在 ENV声明 )也可以被用作特定指令中的参数. $为环境变量标记, 如需要使用”$”字符, 需要使用$.

Environment variables are notated in the Dockerfile either with $variable_name or ${variable_name}. They are treated equivalently and the brace syntax is typically used to address issues with variable names with no whitespace, like ${foo}_bar.

环境变量在 Dockerfile 中的语法为 $var 或 ${var}, 同 Shell. 在 ${foo}\_bar 这样无间隔的情况外, 两者相等.

The ${variable_name} syntax also supports a few of the standard bash modifiers as specified below:

${var} 语法也支持标准 bash 修饰符:

  • ${variable:-word} indicates that if variable is set then the result will be that value. If variable is not set then word will be the result.
  • ${var:-word} 定义了 ${var} 变量的默认值. ${var} ? ${var} : “word”;
  • ${variable:+word} indicates that if variable is set then word will be the result, otherwise the result is the empty string.
  • ${var:+word} 相当于 ${var} ? “word” : “”;

In all cases, word can be any string, including additional environment variables.

word 可以是任何字符串, 可以包含其他环境变量.

Escaping is possible by adding a \ before the variable: \$foo or \${foo}, for example, will translate to $foo and ${foo} literals respectively.

在$前加\实现转义: \$foo\${foo} 将被视作普通字符串而非变量.

FROM busybox
ENV foo /bar

WORKDIR ${foo}
\# WORKDIR /bar, 这里是变量

ADD . $foo
\# ADD . /bar, 依旧是变量

COPY \$foo /quux
\# COPY $foo /quux, 转以后识别成字符串

Environment variables are supported by the following list of instructions in the Dockerfile:

环境变量支持以下语句:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • LABEL
  • USER
  • WORKDIR
  • VOLUME
  • STOPSIGNAL

as well as:

  • ONBUILD (when combined with one of the supported instructions above)

Note: prior to 1.4, ONBUILD instructions did NOT support environment variable, even when combined with any of the instructions listed above.

Environment variable substitution will use the same value for each variable throughout the entire command. In other words, in this example:

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

will result in def having a value of hello, not bye. However, ghi will have a value of bye because it is not part of the same command that set abc to bye.

总的来说, 环境变量当前行不会生效, 也应尽量避免同一个变量多次赋值.

.dockerignore file

Before the docker CLI sends the context to the docker daemon, it looks for a file named .dockerignore in the root directory of the context. If this file exists, the CLI modifies the context to exclude files and directories that match patterns in it. This helps to avoid unnecessarily sending large or sensitive files and directories to the daemon and potentially adding them to images using ADD or COPY.

The CLI interprets the .dockerignore file as a newline-separated list of patterns similar to the file globs of Unix shells. For the purposes of matching, the root of the context is considered to be both the working and the root directory. For example, the patterns /foo/bar and foo/bar both exclude a file or directory named bar in the foo subdirectory of PATH or in the root of the git repository located at URL. Neither excludes anything else.

Here is an example .dockerignore file:

*/temp*
*/*/temp*
temp?

This file causes the following build behavior:

Rule Behavior
*/temp* Exclude files and directories whose names start with temp in any immediate subdirectory of the root. For example, the plain file /somedir/temporary.txt is excluded, as is the directory /somedir/temp.
*/*/temp* Exclude files and directories starting with temp from any subdirectory that is two levels below the root. For example, /somedir/subdir/temporary.txt is excluded.
temp? Exclude files and directories in the root directory whose names are a one-character extension of temp. For example, /tempa and /tempb are excluded.

Matching is done using Go’s filepath.Match rules. A preprocessing step removes leading and trailing whitespace and eliminates . and .. elements using Go’s filepath.Clean. Lines that are blank after preprocessing are ignored.

Lines starting with ! (exclamation mark) can be used to make exceptions to exclusions. The following is an example .dockerignore file that uses this mechanism:

*.md
!README.md

All markdown files except README.md are excluded from the context.

The placement of ! exception rules influences the behavior: the last line of the .dockerignore that matches a particular file determines whether it is included or excluded. Consider the following example:

*.md
!README*.md
README-secret.md

No markdown files are included in the context except README files other than README-secret.md.

Now consider this example:

*.md
README-secret.md
!README*.md

All of the README files are included. The middle line has no effect because !README*.md matches README-secret.md and comes last.

You can even use the .dockerignore file to exclude the Dockerfile and .dockerignore files. These files are still sent to the daemon because it needs them to do its job. But the ADD and COPY commands do not copy them to the the image.

Finally, you may want to specify which files to include in the context, rather than which to exclude. To achieve this, specify * as the first pattern, followed by one or more ! exception patterns.

Note: For historical reasons, the pattern . is ignored.

Dockerfile 中的命令

FROM 基础镜像
From Image[:tag]

MAINTAINER 作者
MAINTAINER OnO<corn.mars@ono.lol>

RUN 执行Shell命令
RUN /bin/bash -c 'some bash commands ${var}; \
some other commands"

RUN 执行非Shell命令
RUN ["executable", "param1", "param2"...]

Label 注释(version, description, some others...)
LABEL multi.label1="value1" \
  multi.label2="value2" \
  other="value3"

EXPOSE 开放端口, 内部开放而不是向MASTER开放, 需要通过-p a:b 来指定, 指定端口可以不一一对应.
EXPOSE PORT1 PORT2

ENV 设置环境变量, 可以通过命令行docker run --env k2="ov2"替换
ENV k1="v1" k2="v2" ...

CMD 命令只能有一条, 多条以最后一条为准.
CMD ["executable", "param1", "param2"]
CMD ["param1", "param2"]    // Passed Into ENTRYPOINT
CMD command param1 param2

ADD 添加MASTER中的文件(夹) 到Docker中并生成新的Layer. DOCKER_PATH: /开始的为绝对路径, ./开头的是以${WORKDIR}/为相对路径
ADD MASTER_PATH/[FILE|FOLDER] DOCKER_PATH

COPY 和 ADD基本相同, 但是COPY不会解压缩文件
COPY MASTER_PATH/[FILE|FOLDER] DOCKER_PATH

ENTRYPOINT 为每次启动容器执行的脚本
ENTRYPOINT ["executable","param1","param2"]

ENTRYPOINT配置启动不可更改部分, CMD给ENTRYPOINT设置可变参数的默认值
FROM aa
ENTRYPOINT ["xx", "-x", "xxxx"]
CMD ["-y", "yyyy"]
当执行 docker run -d bb, 最终将执行的是xx -x xxxx -y yyyy
当执行 docker run -d bb -y yyyy2, "-y yyyy2" 将替换CMD中的 "-y yyyy", 最终执行的将是 xx -x xxxx -y yyyy2

VOLUME 共享Container内文件夹到主机, Container跟`/`对应主机跟`/var/lib/docker/volume/xxx/`
VOLUME ["/data","/var/log/"]

USER 设置执行RUN,CMD,ENTRYPOINT时的用户/UID
USER daemon

WORKDIR 设置执行RUN,CMD,ENTRYPOINT时的工作目录, 多次设定会
WORKDIR /data

ARG 标记docker run的时候, 必须通过--build-arg aa=bb来进行指定的变量, 可以有默认值, 在执行ARG语句之后变量才生效(在ARG语句之前引用该变量, 将会出现空值)
ARG install_path=/data/redis
RUN make PREFIX=${install_path} install

ONBUILD 用来指定作为父image开放给子image的触发器, 指定的命令在构建镜像时并不执行,而是在它的子镜像中使用 FROM 语句之后立即执行。
ONBUILD ADD . /data/software/redis/
ONBUILD RUN cd /data/software/redis/ && tar zxf redis-*.tar.gz && cd redis-* && make && make PREFIX=${install_path} install

STOPSIGNAL 用来指定Container的退出系统信号
STOPSIGNAL 9 / SIGKILL
CMD, RUN, ENTRYPOINT 的区别:

RUN是在building image时会运行的指令, 在Dockerfile中可以写多条RUN指令, 每运行一次将会创建一个单独的层.

CMDENTRYPOINT则是在运行 container 时会运行的指令, 都只能写一条, 如果写了多条, 则最后一条生效.

CMDENTRYPOINT的区别是:

CMD在运行时会被 command 覆盖, ENTRYPOINT不会被运行时的 command 覆盖, 但是也可以指定. 例如:

docker run -d postgres /bin/bash echo hello

这里的"/bin/bash echo hello"就是 command, 将覆盖 Dockerfile 的CMD, 但是不会覆盖ENTRYPOINT.
如果要覆盖ENTRYPOINT, 那么可以在docker run运行时输入 --entrypoint="xxx".

CMDENTRYPOINT一般用于制作具备后台服务的 image, 例如 apache, database 等. 在使用这种 image 启动 container 时, 自动启动服务.

ADD 与 COPY 的区别

我们可以使用 ADD 和 COPY 拷贝文件(注意是上下文相关目录的文件, 不是本地的任意文件, 除非上下文目录是根)到 container 制作 image.

ADD 多了 2 个功能, 下载 URL 和解压. 其他都一样. 如果你不希望压缩文件拷贝到 container 后会被解压的话, 那么使用 COPY. 否则本地文件 src 为 gzip, bzip2, xz 扩展名情况, 将会自动调用 tar -x. 如果需要自动下载 URL 并拷贝到 container 的话, 请使用 ADD.

其中, 应注意 MASTER_PATH 不能出现 ../ 这样不在当前 context 路径的情况. DOCKER_PATH 要以/结尾, 以免出现拷贝重命名问题.

总之, 想要避免出错, 尽量不要出现复杂的文件 ADD/COPY 情况.

ARG 与 ENV

ARG 是 docker run 时允许传入的参数, ENV 是 docker build 的时候写入 image 的信息.

所以如果同时出现通过 ARG 和 ENV 分别定义的相同变量:

ARG NAME user
ENV NAME daemon

那么将以 ENV 定义的为主, 覆盖 ARG 传入的参数.

ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}

执行 docker run aa –build-arg CONT_IMT_VER=v2.0.1 其中 CONT_IMT_VER 仍为 v1.0.0~

内置 build arg

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy
Donate - Support to make this site better.
捐助 - 支持我让我做得更好.