Dockerfile 官网介绍
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
,用户可以创建一个自动执行一系列命令行指令的自动构建。
Dockerfile 指令
构建流程
查看镜像的 Dockerfile
docker hub 镜像的 Dockerfile links 单元,可以找到各个 TAG 的镜像的 Dockerfile 连接,如下图所示
也可以在镜像中通过 Image Layers 查看
下图红框就是 redis 这个镜像在构建时执行的 Dockerfile 指令,每个指令执行后,都会形成新的 Image Layer
BuildKit
BuildKit 是 docker 从 18.09 版本开始支持的新的构筑端,由 moby/buildkit 项目 提供技术支持。
BuildKit 构筑端 相对于原有实现由如下优点:
使用 BuildKit
执行 docker build
前,在客户端设置下面环境变量
DOCKER_BUILDKIT=1
Docker Buildx 总是允许 BuildKit
外部 Dockerfile 前端
BuildKit 支持从容器镜像动态的加载前端。
使用外部 Dockerfile 前端时,在 Dockerfile 第一行声明下面内容以指定希望使用的镜像
# syntax=docker/dockerfile:1
BuildKit 附带了Dockerfile frontend builtin,但它建议使用一个外部镜像。这可以确保
流程
docker build -t 镜像库/镜像名:TAG .
构建镜像示例
FROM centos
MAINTAINER [email protected]
ENV USER_PATH /usr/local
WORKDIR $USER_PATH
# vim
RUN yum -y install vim
# ifconfig
RUN yum -y install net-tools
# jdk 8
# 安装 jdk 需要先安装 glibc
RUN yum -y install glibc.i686
RUN mkdir ${USER_PATH}/java
# Add 指令的 参数使用相对路径
ADD jdk-8u171-linux-x64.tar.gz ${USER_PATH}/java
# 配置环境变量
ENV JAVA_HOME ${USER_PATH}/java/jdk1.8.0_171
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH ${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar:${JRE_HOME}/lib:$CLASSPATH
ENV PATH ${JAVA_HOME}/bin:$PATH
EXPOST 80
# docker build 执行过程中,每个指令都会输出
# 所以下面两个 CMD 不是用来启动容器时执行的
# 而是在 docker build 时起到类似 日志 的作用
CMD echo $USER_PATH
CMD echo "centos started..."
CMD /bin/bash
Docker 按顺序运行 Dockerfile 中的指令。
Dokcerfile 指令的基础格式如下
# Comment
INSTRUCTION arguments
指令 (INSTRUCTION) 在语法上大小写不明。
但是,习惯通常 全大写,以更好的和参数区分。
Dockerfile 必须用一个 FROM 指令开头,用于指定你构建的父镜像。
FROM 指令也可能在 解析器指令(parser directives) 、注释 或 全局 ARG(全局变量定义) 之后。
FROM 指令可能在一个或多个 ARG 指令之前,这些 ARG 指令定义用于 Dockerfile FROM 行的参数
没明白,为啥是之前?
原文
FROM may only be preceded by one or more ARG instructions, which declare arguments that are used in FROM lines in the Dockerfile.
Docker 将以 # 开头的行视为注释
有效的 解析指令(parser directive) 也以 # 开始,但不会被当做注释
只要不是行首,行内任何其他位置出现 # ,一律视为参数
符合语法的注释如下
# Comment
RUN echo 'we are running some # of cool things'
注释行会在 Dockerfile 指令执行前被移除,这意味着下面片段中的注释不会被 echo 指令执行(被 echo 指令输出)
RUN echo hello \
# comment
world
上面的片段等价于
RUN echo hello \
# comment
world
注释不支持续行符(即 \)
注释(#) 和 指令 前的空格会被忽略,但是指令参数中的空格是保留的
语法允许 注释 和 指令 前存在空格,但并不鼓励这个行为。并且从向后兼容性考虑,这些空格会被忽略。
因此,下面的两个片段是等效的
# 片段1
# this is a comment-line
RUN echo hello
RUN echo world
#片段2
# this is a comment-line
RUN echo hello
RUN echo world
同时,下面的示例中的前导空格实在指令参数中的,因此打印结果为 “ hello world”
注意 hello 前有空格
RUN echo "\
hello\
world"
# directive=value
语法上,解析器指令大小写不敏感解析器指令不支持续行符,失效
# direc \
tive=value
解析器指令只使用一次,出现两次,失效
# directive=value1
# directive=value2
FROM ImageName
必须声明在 Dockerfile 的最顶端
出现在 builder 指令后,视为注释,失效
FROM ImageName
# directive=value
一个不可识别的解析器指令(unknowndirective),被视为注释,失效
同时,下面那个可识别的解析器指令,因为在出现在了一个注释(unknowndirective 被视为注释了)之后,所以也被视为一个注释,失效
# unknowndirective=value
# knowndirective=value
FROM ImageName
格式
# syntax=[remote image reference]
示例
# syntax=docker/dockerfile:1
# syntax=docker.io/docker/dockerfile:1
# syntax=example.com/user/repo:tag@sha256:abcdef...
syntax 指令解析器指令只在使用 BuildKit 时生效,使用原始构筑后端时会被忽略。
syntax 指令解析器定义 用于构建 Dockerfile 的本地语法。它允许无缝的使用作为Docker映像分发、并在容器沙盒环境中执行的外部实现
通常 Dockerfile 实现提供以下功能
官方发布
Docker 官网会在 Docker hub 上的 docker/dockerfile 仓库发布官方版本镜像。用户可以使用它们构建 Dockerfile。
新镜像通过下面两个渠道发行
stable
stable 自带版本控制,例如
如果指定版本,如 1.2 or 1.2.1,Dockerfile 需要手动升级以获取 bug 修复和新特性。
Dockerfile Builder 的新老版本保持兼容
labs
lab 提供还未在 stable 版中可用的 Dockerfile 特性的抢先体验。
lab 和 stable 一起发布,在相同版本后的基础上添加 -labs 后缀,如
渠道选择
声明
转义解析器指令只有下面 二选一
# escape=\
或
# escape=`
说明
声明
通过 ENV 语句,如
ENV FOO=/bar
环境变量可以在某些指令中作为变量使用
为了逐字逐句地将类似变量的语法包括在陈述中,转义字符也生效(??没看懂)
原文
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
引用
Dockerfile 中,环境变量可以通过下面方式引用
$variable_name
${variable_name}
两种引用方式的区别
${variable_name}
是两侧无空格场景(如路径问题)的经典用法 ${foo}_bar
${variable_name}
支持下面的标准bash修饰符${variable:-word}
变量有值时使用原值,否则使用 word${variable:+word}
变量有值时使用 word,否则使用空字符串转义
$variable_name
与 ${variable_name}
也可以被转义字符 \ 转义
转以后作为一个文本,而不是指令存在
```shell
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO} # WORKDIR /bar
ADD . $FOO # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux
```
指令支持
下列 Dockerfile 指令支持环境变量
值的替换
环境变量的替换将为整个指令(这里的整个指令是指一行指令而不是整个 Dockerfile 中的所有指令)里的每个变量使用同样的值
下面的案例中,def = hello ,ghi = bye,因为 ghi 不是给 abc 赋值为 bye 的指令的一部分
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
流程
.dockerignore
文件/foo/bar
和 foo/bar
同时存在时,意义
用于避免向守护进程发送没必要的过大或敏感的文件和目录
pattern 语法
pattern | 作用 |
---|---|
# comment |
注释,忽略 |
! |
例外项,原先应该排除的不排除了 |
*/ |
根目录下任意 1 级子目录,每级子目录的名字无所谓 |
**/ |
根目录下的任意多级子目录,可以是 0 级、1 级、多级,每级子目录的名字无所谓 |
temp* |
目录或文件以 temp 开头,后面可以接任意内容或不接 |
temp? |
目录或文件以 temp 开头,后面接并且只接一个字符 |
示例
*/aaa*
匹配:/随便/aaa随便
*/*/aaa*
匹配:/随便/随便/aaa随便
**/aaa?
匹配:/随便几级目录/aaa任意一个字符
匹配规则
*.md
!README*.md
README-secret.md
.md
文件一般都排除,但 .md
文件中的 README 文件不排除.md
文件排除*.md
README-secret.md
!README*.md
.md
文件一般都排除,但 .md
文件中的 README 文件不排除.dockerignore
文件*
! 保留文件 1
! 保留文件 2
! ...
! 保留文件 n
FROM 通常在 Dockerfile 的首行,
FROM 基础镜像:TAG
维护者名字
已过期
MAINTAINER name
指令说明
分层运行指令与生成提交符合 Docker 的核心理念:
(镜像) 提交到 Docker 中是很廉价 (的操作)
我们可以从镜像历史的任意一个节点创建容器
这类似与 源码中心
指令格式
RUN 指令有两种格式
RUN
RUN /bin/bash -c '指令'
RUN ["executable", "param1", "param2"]
可以避免 shell 字符串改写,保证 RUN 的指令在一个不包含其他特殊 shell 的基本 shell 中执行
可以通过 RUN ["/bin/bash", "-c", "指令"]
的形式变更此格式下的 shell
== 这种格式基于 JSON 格式==
必须使用双引号 "
需要转义反斜杠 \ 为 \\
指令不会运行于 shell 环境,因此类似 RUN [ "echo", "$HOME" ]
中的 $HOME
会无法识别
指定对外暴露的的端口
EXPOSE port
指定容器创建后默认的工作目录
即,使用 docker exec
进入容器后的那个目录
WORKDIR path
指定镜像用什么样的用户执行指令
默认 root
USER root
设置环境变量
ENV 变量标识符 变量值
声明一个挂载点作为容器的卷
VOLUME /myvol
它没有马上与宿主机的某个目录进行绑定
当使用这个镜像运行容器时,才会将它挂载到 Docker 安装目录的指定目录下,不同版本的 Docker 会有差别,比如
Docker 安装路径/volumes/{容器ID}
复制主机行文件或目录至容器内文件系统
COPY
主机上文件或路径,甚至远程文件 RULs 复制到镜像的文件系统中
ADD
使用相对路径
可以自动处理 URL
可以自动解压
从 URL 上 ADD 的压缩包不能自动解压
指定容器启动 (docker run
) 后默认需要执行的指令
指令格式
指令的格式同 RUN
CMD
CMD ["executable", "param1", "param2"]
说明
docker run
后的命令替代CMD ["catalina.sh", "run"]
docker run -it -p 8080:8080 /bin/bash
启动CMD ["/bin/bash"]
并使上面的 CMD 失效与 RUN 的区别
docker build
阶段执行docker run
后执行指定容器启动 (docker run
) 后默认需要执行的指令
ENTRYPOINT ["executable", "param1", "param2"]
说明 / 与 CMD 区别
docker run
后的命令覆盖docker run
后的命令会作为参数传递给 ENTRYPOINT 指定的程序示例
以 nginx 为例
FROM nginx
......
ENTRYPOINT ["nginx","-c"]
CMD ["/etc/nginx/nginx.conf"]
nginx 容器启动后,固定运行 nginx -c conf_file
指令(用指定的配置文件启动)
CMD 指令将 conf_file 的默认值 /etc/nginx/nginx.conf
传递给上面的命令
若 docker run
后面携带了命令,会作为新的 CMD 给上面的命令传参,覆盖默认值
默认 | 传参 | |
---|---|---|
ENTRYPOINT 指令 | ENTRYPOINT [“nginx”,“-c”] | ENTRYPOINT [“nginx”,“-c”] |
docker run |
docker run nginx:test |
docker run nginx:test -c /etc/nginx/my.conf |
容器运行后的实际指令 | nginx -c /etc/nginx/nginx.conf |
nginx -c /etc/nginx/my.conf |