document: https://docs.docker.com/engine/reference/builder/
1. 通过Dockerfile构建镜像:
docker build . # . 指定当前路径为构建时的上下文环境(build context), 会在该路径下寻找Dockerfile文件,构建镜像
# 可以通过 -t 参数指定镜像名字和tag
docker build -t sky/app .
# 可以通过-f参数指定Dockerfile的路径, 而不是从build context下找
docker build -f /path/to/a/Dockerfile .
# 可以同时创建多个镜像, 只需要用多个-t参数
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
2. Dockerfile的内容:
0. ARG
:
ARG
是唯一一个可以在FROM之前出现的指令, 用于定义变量, 该变量可以给后续所有构建阶段的FROM指令使用:
在FROM之前定义的ARG是不属于任何构建阶段的, 所以不能被构建阶段内除FROM之外的指令访问.
该指令定义一个变量或者提供变量的默认值,在构建时可以通过
--build-arg <varname>=<value>
来指定变量的值
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
如果要在构建阶段内部(非FROM指令)使用ARG定义的变量的值, 需要在构建阶段内部定义一个ARG指令, 这样就可以使用其默认值了
ARG VERSION=latest # 为变量提供默认值
FROM busybox:${VERSION}
ARG VERSION
RUN echo ${VERSION} > image_version
ENV
定义的环境变量可以覆盖ARG
定义的变量
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER
1. FROM
:
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
- 每个
FROM
指令都会初始化一个新的构建阶段(build stage). 并且为该阶段后续指令设置一个基础镜像. - 一个镜像中可以存在多个构建阶段, 构建阶段中的部分指令可以引用前面阶段的内容. 引用方式就是设置一个别名:
AS <name>
2. RUN
:
RUN <COMMAND>
: 会被当做字符串传递给``/bin/sh -c或
cmd /S /C`RUN ["executable", "param1", "param2"]
:
RUN
指令会在镜像的顶层开辟一个新的层(layer), 并在其中执行指令, 执行结果会被提交到新创建的层中, 因此结果可被后续步骤使用.
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME' # 通过 \ 来换行
由于一个RUN
指令就会创建一个layer, 为了让镜像尽可能小, 应该尽可能减少RUN
指令的个数
3. CMD
:
CMD
指令的唯一目的, 是为刚启动的容器提供一个默认的entrypoint(即启动容器时执行的命令).如果启动容器时指定了entrypoint, 则
CMD
的指令会被覆盖
FROM ubuntu
CMD echo "This is a test." | wc -
4. LABEL
:
LABEL
指令的作用是为镜像提供更多描述信息…这里只给例子:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
LABEL multi.label1="value1" multi.label2="value2" other="value3"
5. EXPOSE
:
EXPOSE
指令主要目的是暴露端口可以指定暴露的监听端口是TCP还是UDP的, 在没指定的情况下默认是TCP
EXPOSE 80/udp
EXPOSE 80/tcp
EXPOSE 80/udp
但事实上, 在运行时, docker向外暴露的服务端口未必要是EXPOSE指定的, 因为docker run有-p
参数, 可以指定端口映射, 因此可以在运行时动态改变:
docker run -p 80:80/tcp -p 80:80/udp ...
6. ENV
:
ENV
指令为接下来的指令设置环境变量.注意, 该环境变量在容器运行时还会持续保留
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
如果一个环境变量只需要在RUN
指令执行阶段使用, 可以考虑这样写:
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
或者可以考虑使用ARG
, 使用ARG设置的环境变量不会在容器运行时继续保持:
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
7. ADD
:
ADD [--chown=<user>:<group>] <src> <src2> ... <dest>
ADD [--chown=<user>:<group>] ["<src>", ..., "<dest>"]
# 注意--chown 只支持linux容器构建过程中
ADD指令用于从复制文件、目录、远程文件URL, 并且将其添加到镜像的文件系统的目标路径:
<dest>
上
# 路径支持简单正则匹配
ADD hom* /mydir/
ADD hom?.txt /mydir/ # ? 匹配单个字符
ADD arr[[0]].txt /mydir/ # 匹配arr[0].txt, 要对[ ]进行转义!
# --chown 可以用UID和GID 也可以用用户名和组名
# 如果只提供UID/用户名, 组名的GID将于用户名对应的UID一致
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/ # GID和UID一致
ADD --chown=1 files* /somedir/ # 1:1
ADD --chown=10:11 files* /somedir/
当没有指定build context时, 只能是文件的URL路径:
docker build - < somefile # 从标准输入中读取文件内容进行构建
**使用ADD
**时必须遵循以下规则:
-
路径必须在build context内部.
ADD ../something /some
是不允许的 -
如果是一个URL且尾部没有反斜线, 则从URL下载的文件直接复制到
-
如果是一个URL且尾部有个反斜线, 则从URL下载的文件会复制到
<dest>/
下 -
如果是一个文件夹, 那么整个文件夹的内容都会复制(包括文件系统的元信息)
-
如果是一个本地的压缩包(
tar
,gzip
,bzip2
,xz
), 那么将自动解压
为一个目录, 从URL下载的压缩包不会自动解压. -
如果有多个源路径或者使用了通配符, 则必须是一个路径且尾部带有反斜线
-
如果尾部没有反斜线会被认为是一个文件, 中的文件内容将对进行重写
-
如果不存在, 将会递归的创建目录
8. COPY
:
COPY
的通常用法:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
似乎与ADD没啥区别, 但是COPY指令能够接受一个参数: --from=<name>
. 可以从先前的build stage中复制文件(FROM ... AS <NAME>
)
当指定
--from=<name>
时, 就变成了先前 build stage 环境中的文件路径了!
有时可能需要在前面的build stage中编译一个东西, 要到随后若干个build stage之后才能用到, --from 参数就起到了这个作用
9. ENTRYPOINT
exec
form:
ENTRYPOINT ["executable", "param1", "param2"]
在exec
形式下, docker run 最后的额外参数将作为命令行参数附加到exectuable
的参数列表后面, CMD
指令将无效. 想重写该ENTRYPOINT需要通过docker run --entrypoint
进行
shell
form:
ENTRYPOINT command padram1 param2
shell
形式的entrypoint将直接覆盖CMD
以及docker run
的额外参数, 但是以此方式执行的指令无法接受到任何SIGNAL(即无法被KILL), 因此并不十分推荐
10. VOLUME
VOLUME
指令用于创建一个挂载点, 注意只是挂载点, 构建镜像过程中并不会挂载任何主机上的路径挂载到挂载点的主机目录是容器运行时声明的: 通过
-v host_dir:container_dir
进行映射
VOLUME ["/data", "/data1"]
VOLUME /myvol
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
11. USER
USER <user>[:<group>]
USER <UID>[:<GID>]
USER
指令用来设置执行 RUN
`CMD\
ENTRYPOINT` 指令的用户和所属组
12. WORKDIR
WORKDIR /path/to/workdir
这个应该不必多说…
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
The output of the final pwd
command in this Dockerfile
would be /a/b/c
.