Dockerfile指令

Dockerfile指令

Dockerfile是用于构建docker镜像的配置文件

Dockerfile基本语法和格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Comment
INSTRUCTION arguments

#号开头的行表示注释,指令建议大写,参数可以有多个,参数可以使用\折行
#号和RUN指令前面的空格会被忽略,第一个指令必须是FROM

引用的镜像版本
docker/dockerfile:1 使用主版本为1的最新版本镜像,即保持主版本号1不变,获取最新的次版本或补丁版本镜像
docker/dockerfile:1.2 使用次版本为1.2的最新补丁版本镜像,即主版本和次版本号1.2不变,只获取最新的补丁版本,不会使用1.3以上版本
docker/dockerfile:1.2.1 固定版本镜像,不使用最新版本镜像

通过escape指令指定转义符号,如下指定反斜杠\为转义符,如未指定,默认转义符也是\
# escape=\

变量可以是任何字符,也可以引用之前的变量,格式和bash类似
$variable_name or ${variable_name}
${variable:-word}: 如果变量未定义,则返回word,否则返回定义的变量值
${variable:+word}: 如果变量未定义,则返回空,否则返回word
\$user: 使用\转义$符号,即显示为$user字符串
变量支持以下指令调用: ADD,COPY,ENV,EXPOSE,FROM,LABEL,STOPSIGNAL,USER,VOLUME,WORKDIR,ONBUILD(与前面所有指令配合使用)

.dockerignore文件说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 构建时忽略.dockerignore中指定的文件,另外注意语句的顺序可能会影响匹配结果,建议先精确匹配,再泛匹配
$ more .dockerignore
# 表示注释
# comment
# 排除根目录下名称为temp加一个字符的文件或目录,如/temp1, /temp2.txt
temp?
# 排除一级子目录下的任何以temp开头的文件或目录,如/somedir/temp1.txt, /somedir/temp2
*/temp*
# 排除二级子目录下的任何以temp开头的文件或目录,如/dir1/dir2/temp3.txt
*/*/temp*
# 排除根目录及所有子目录下的.go文件
**/*.go
# !表示包括,如下两行内容表示排除根目录下除了README.md文件之外的所有*.md文件
*.md
!README.md

Dockerfile指令

FROM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 用法
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
# 说明
第一个指令必须是`FROM`,即引用一个基础镜像,但是`ARG`指令可用于定义变量,如指定基础镜像信息,并位于FROM指令之前.
在一个Dockerfile文件中可以多次指定FROM指令,用于构建多个镜像或用于多阶段构建镜像
# 示例
# 使用ARG指定基础镜像版本,可用于多平台自动构建镜像
ARG VERSION=latest
FROM busybox:$VERSION
CMD echo test > /tmp/file

# 指定多个FROM用于多阶段构建镜像
FROM busybox:latest AS builder
CMD echo test > /tmp/file

FROM alpine:3.13
COPY --from=builder /tmp/file /opt

RUN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 用法
# shell格式,默认使用/bin/sh -c执行command命令
RUN command
# exec格式,使用JSON数组格式,参数必须使用双引号(")括起来,而不能使用单引号(')
RUN ["executable", "param1", "param2"]
# 说明
一个RUN指令表示执行一次提交,即生成一层数据,所以建议尽可能合并多条RUN指令,减少镜像层级
# 示例
# shell格式
RUN echo hello
RUN /bin/bash -c 'echo hello'
# exec格式,注意必须指定shell
RUN ["/bin/bash", "-c", "echo hello"]

# 执行多条命令,按需选择以下两个执行方式
# && 只有上一条命令正确执行时才会执行下一条命令
RUN /bin/bash -c 'source $HOME/.bashrc && \
echo $HOME'
# ; 顺序执行所有命令
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

CMD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 用法
# exec格式
CMD ["executable","param1","param2"]
# 作为ENTRYPOINT的默认参数
CMD ["param1","param2"]
# shell格式
CMD command param1 param2
# 说明
一个Dockerfile文件中只能有一个CMD指令,如果有多个则最后一个CMD指令生效
注意RUN和CMD的差别,RUN执行命令并返回结果,CMD在构建时不执行任何操作,仅作为镜像的默认命令,在容器启动时执行
建议指定命令的绝对路径
# 示例
# shell格式
CMD top
# exec格式
CMD ["nginx", "-g", "daemon off;"]

LABEL

1
2
3
4
5
6
7
8
9
10
11
# 用法
LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 说明
一个镜像可以有多个标签,可以合并多个标签为一行,
# 示例
# 指定多个标签
LABEL env="prod" version="1.2.0"
LABEL env="prod" \
version="1.2.0"
# 查看镜像标签
docker image inspect --format='' myimage

MAINTAINER-该指令已废弃

用于指定镜像作者信息,可用LABEL指令代替

EXPOSE

1
2
3
4
5
6
7
8
9
10
11
# 用法
EXPOSE <port> [<port>/<protocol>...]
# 说明
EXPOSE指令用于定义容器运行时监听的端口,默认监听TCP,如需监听UDP需要指定
# 示例
EXPOSE 53/udp
# 同时监听TCP和UDP
EXPOSE 53/tcp
EXPOSE 53/udp
# 运行容器时也需要指定TCP和UDP端口
docker run -p 80:80/tcp -p 80:80/udp ...

ENV

1
2
3
4
5
6
# 用法
ENV <key>=<value> ...
# 说明
支持一个ENV指令设置多个环境变量
# 示例
ENV env="dev" version="1.2.0"

ADD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 用法
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
# 说明
ADD指令支持添加文件,目录或下载URL文件到镜像中,可以指定多个源,支持通配符
如果源文件是一个压缩包(格式为gzip, bzip2 or xz),则会自动解压后再复制到目标
如果是从URL下载的压缩包,则不会自动解压
如果目标是相对路径的目录,则自动添加WORKDIR目录前缀
默认文件属性为root,即uid和gid为0,可以通过--chown选项修改
# 示例
ADD file* /data/
ADD nginx*.log /data/
# 复制*.sh 到 WORKDIR/data/下
ADD *.sh data/
# 更文件属性,如果指定用户名则容器中需要存在该用户,否则ADD命令将执行失败,建议指定uid
ADD --chown=app:app file* /data
ADD --chown=1000:1000 file* /data
# 指定URL
ADD https://www.baidu.com/index.html /data

COPY

1
2
3
4
5
6
7
8
9
10
# 用法
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
# 说明
ADD类似,但是不会解压文件
复制目录时,会递归复制目录下的文件,但目录本身不会复制
# 示例
COPY file* /data/
COPY nginx*.log /data/
COPY *.sh data/

ENTRYPOINT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 用法
# exec格式
ENTRYPOINT ["executable", "param1", "param2"]
# shell格式
ENTRYPOINT command param1 param2
# 说明
docker run 的命令行参数将附加在使用exec格式指定的ENTRYPOINT所有参数之后,并将覆盖使用CMD指定的所有参数
可以使用docker run --ENTRYPOINT 覆盖ENTRYPOINT指令
Dockerfile 应该至少指定一个CMD或ENTRYPOINT命令
CMD用于指定ENTRYPOINT默认的参数
# 示例
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

VOLUME

1
2
3
4
5
6
7
# 用法
VOLUME ["/data"]
# 说明
指定挂载目录,支持挂载其他容器下的目录
# 示例
VOLUME ["/var/log/"]
VOLUME /var/log

USER

1
2
3
4
5
6
7
8
# 用法
USER <user>[:<group>]
USER <UID>[:<GID>]
# 说明
指定RUN, CMD and ENTRYPOINT指令运行的用户
如果没有指定组则默认为root组
# 示例
USER nginx:nginx

WORKDIR

1
2
3
4
5
6
7
8
# 用法
WORKDIR /path/to/workdir
# 说明
指定工作目录
# 示例
ENV DIR=/data
WORKDIR $DIR
RUN pwd

ARG

1
2
3
4
5
6
# 用法
ARG <name>[=<default value>]
# 说明
使用ARG指令传递构建时变量
# 示例
ARG user=usera

ONBUILD

STOPSIGNAL

HEALTHCHECK

SHELL

建议

1.每个Dockerfile文件为单独目录,即每个镜像为单独目录
2.构建镜像时指定镜像前缀和tag版本号,建议同时指定当前版本号和最新版本号latest
3.使用默认的Dockerfile文件名,使用git管理Dockerfile文件,支持直接调用git-repo-url构建镜像
批量复制文件时,使用.dockerignore文件排除非必要的文件

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 建议创建单独目录
$ cd && mkdir demo && cd demo
$ vim Dockerfile
FROM busybox
RUN echo "hello world" > /tmp/test
# 不推荐此命令,不指定-t选项则默认为<none>即dangling镜像
$ docker build .
# 指定版本号:v1.0
$ docker build -t test/demo:v1.0 .
# 不指定版本号则默认为:latest最新版本号
$ docker build -t test/demo .
# 建议同时指定当前版本号和最新版本号,其中:latest版本标识可以省略,注意最后的点.不能少,代表当前目录
$ docker build -t test/demo:v1.0 -t test/demo .
$ docker run test/demo cat /tmp/test
hello world
$ vim Dockerfile
FROM busybox
RUN echo "hello world" > /tmp/test && echo "hello docker" >> /tmp/test
$ mv Dockerfile Dockerfile-demo-v1.1
# 默认读取目录下的Dockerfile文件,如不存在则报错
$ docker build -t test/demo:v1.1 -t test/demo:latest .
unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /root/demo/Dockerfile: no such file or directory
# 指定自定义的Dockerfile文件
$ docker build -f Dockerfile-demo-v1.1 -t test/demo:v1.1 -t test/demo:latest .
$ docker run test/demo cat /tmp/test
hello world
hello docker
$ docker images test/*
REPOSITORY TAG IMAGE ID CREATED SIZE
test/demo latest 843a5388ecdd 32 seconds ago 1.24MB
test/demo v1.1 843a5388ecdd 32 seconds ago 1.24MB
test/demo v1.0 94129859891d About a minute ago 1.24MB
# 还原默认的Dockerfile文件名
$ cd && cp demo/Dockerfile-demo-v1.1 demo/Dockerfile
vim demo/Dockerfile
FROM busybox
RUN echo "hello world, hello docker" > /tmp/test
# 指定Dockerfile文件所在的目录
$ docker build -t test/demo:v1.2 -t test/demo:latest demo/
$ docker images test/*
REPOSITORY TAG IMAGE ID CREATED SIZE
test/demo latest a558982d3671 6 seconds ago 1.24MB
test/demo v1.2 a558982d3671 6 seconds ago 1.24MB
test/demo v1.1 843a5388ecdd 24 minutes ago 1.24MB
test/demo v1.0 94129859891d 25 minutes ago 1.24MB
$ docker run test/demo cat /tmp/test
hello world, hello docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 安装软件之前先更新源,安装之后清理缓存
RUN apt-get update && apt-get install -y \
package-bar \
package-baz \
package-foo \
&& rm -rf /var/lib/apt/lists/*

# 添加远程URL并解压
ADD https://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things

# nginx镜像配置
FROM nginx:1.19-alpine
ARG TZ='Asia/Shanghai'
ENV TZ ${TZ}
RUN apk upgrade --update \
&& apk add bash tzdata curl wget ca-certificates \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& rm -rf /var/cache/apk/*
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

# alpine镜像配置
FROM alpine:3.13
ARG TZ="Asia/Shanghai"
ENV TZ ${TZ}
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk upgrade \
&& apk add bash tzdata bind-tools busybox-extras ca-certificates libc6-compat wget curl \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& rm -rf /var/cache/apk/*
CMD ["/bin/bash"]

# debian镜像语言配置
RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG en_US.utf8

# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone


# Dockerfile 最佳实践配置
# We suggest using the major.minor tag, not major.minor.patch.
FROM alpine:3.13

# Non-root user for security purposes.
#
# UIDs below 10,000 are a security risk, as a container breakout could result
# in the container being ran as a more privileged user on the host kernel with
# the same UID.
#
# Static GID/UID is also useful for chown'ing files outside the container where
# such a user does not exist.
RUN addgroup --gid 10001 --system nonroot \
&& adduser --uid 10000 --system --ingroup nonroot --home /home/nonroot nonroot

# Install packages here with `apk add --no-cache`, copy your binary
# into /sbin/, etc.

# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini.
# NOTE: See https://github.com/hexops/dockerfile#is-tini-still-required-in-2020-i-thought-docker-added-it-natively
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--", "myapp"]
# Replace "myapp" above with your binary

# bind-tools is needed for DNS resolution to work in *some* Docker networks, but not all.
# This applies to nslookup, Go binaries, etc. If you want your Docker image to work even
# in more obscure Docker environments, use this.
RUN apk add --no-cache bind-tools

# Use the non-root user to run our application
USER nonroot

# Default arguments for your app (remove if you have none):
CMD ["--foo", "1", "--bar=2"]

参考
https://docs.docker.com/engine/reference/builder/
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
https://docs.docker.com/develop/develop-images/multistage-build/
https://docs.docker.com/develop/dev-best-practices/
https://github.com/hexops/dockerfile
https://github.com/vimagick/dockerfiles
https://www.cnblogs.com/backups/p/docker_2dockerfile.html