Files
rikako-note/docker/docker file.md
2023-02-13 17:05:42 +08:00

24 KiB
Raw Blame History

Dockerfile

Docker可以通过读取dockerfile中的指令自动构建image。

format

如下是dockerfile的格式

# Comment
INSTRUCTION arguments

指令是不区分大小写的但是习惯将其大写以区分INSTRUCTION和arguments。
Dockerfile中的指令将会被按顺序执行dockerfile必须以FROM指令开头。FROM指令制定了构建的Parent image。

parser directive

parser指令是可选的并影响dockerfile后续的处理方式。parser指令并不会向build过程中添加layer也不会展示为一个build过程。parser指令的格式如下所示

# directive=value

一旦任何comment、空行或builder instruction被处理docker将不再接受parser directive即使后面有格式符合parser instruction的行也会被当作注释处理。故而parser instruction必须位于dockerfile的最顶端。
parser directive是大小写不敏感的但是推荐将其小写。约定也要求在任何parser directive后添加一个空白行。
如下格式是无效的:

# direc \
tive=value

如下格式中parser指令连续出现了两次也是无效的

# directive=value1
# directive=value2

FROM ImageName

由于之前已经有一条指令如下parser指令将会被当作注释处理:

FROM ImageName
# directive=value

由于之前存在一条注释故而parser指令也会被当作注释处理

# About my dockerfile
# directive=value
FROM ImageName

不被识别的parser指令也会被当作注释处理由于上一条parser指令不被识别被当作注释故而第二条能被识别的parser指令也会被当作注释处理

# unknowndirective=value
# knowndirective=value

非换行空白符允许在parser指令中出现故而下列格式的parser instruction都是允许的

#directive=value
# directive =value
#	directive= value
# directive = value
#	  dIrEcTiVe=value

dockerfile支持如下两条parser directive

  • syntax
  • escape

syntax

该特性仅当使用了BuildKit backend时可用当使用classic builder backend时会被忽略。

escape

# escape=\ (backslash)

or

# escape=` (backtick)

escape指令用于设置dockerfile中的转义符。如果没有显式设置的情况下默认情况下转义符为\
转义符在dockerfile中既用于对行内的字符进行转义也用于对换行符进行转义\后跟一个换行符可以将一行指令拆分为多行进行编写。

ENV

环境变量dockerfile中以ENV开始的变量可以在其他指令中作为变量被使用由dockerfile进行解析。
环境变量在dockerfile中通过$variable_name或${variable_name}的形式进行使用。
${variable_name}支持一些标准的bash使用

  • ${variable:-word}如果variable被设置那么返回值为variable设置的值如果variable没有被设置那么返回值为word
  • ${variable:+word}如果variable被设置那么返回word否则返回空字符串 可以通过\符号对$进行转义
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO}   # WORKDIR /bar
ADD . $FOO       # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux

在dockerfile中如下命令都支持使用环境变量

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD (when combined with one of the supported instructions above) 在ENV中使用环境变量时环境变量的值使用的是上一条ENV指令结束时的值
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

其中def的值为hello而ghi的值为bye。

.dockerignore file

当docker cli将context发送给docker daemon之前其会先在context根目录查找文件名为.dockerignore的文件。如果该.dockerignore文件存在docker cli将会删除context中符合pattern的文件。者可以避免务必要的发送大文件或敏感文件。
docker cli将.dockerignore解释为用换行符分隔的一系列模式context的根目录既被当作工作目录也被当作根目录

在.dockerignore中#开头的行将被看作注释。

.dockerignore的语法如下

rule behave
# comment 注释,忽略
*/temp* 排除了根目录的直接子目录中任何以temp开头的文件和目录例如/somedir/temporary.txt或/somedir/temp
*/*/temp* 排除深度为2的子目录中任何以temp开头的文件或者目录例如/somedir/subdir/temporary.txt is excluded
temp? 排除根目录的子目录中名称为temp+任意字符的文件和目录
**/*.go **用于匹配任意层包含0的路径此pattern会排除任何以.go结尾的文件

当pattern以!开头时代表不排除满足pattern的文件

*.md
!README.md

上述代表排除所有.md文件但是不排除README.md

在.dockerignore文件中匹配的最后一条pattern将会决定该文件是否包含在context中

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

上诉代表所有的.md文件都会被移除但是满足README*.md格式的文件将会被保留README-secret.md文件会被移除。

如果想要在.dockerignore中指定哪些文件要被包含而不是指定哪些文件被排除可以使用如下形式

*
!include-pattern...

FROM

FROM命令格式如下

FROM [--platform=<platform>] <image> [AS <name>]

or

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

or

FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM命令设置了一个初始build状态并且为其他命令设置了一个初始的镜像。一个有效的dockerfile文件必须以FROM命令开始。

  • 在同一个dockerfile中FROM命令可以出现多次用于创建多个镜像或将其中一个build stage作为另一个的依赖。只需要在每条新的FROM语句之前记录上一个由commit输出的image id每条新FROM语句都会清除之前语句创建的所有状态。
  • 可以为一个新的build stage指定一个名称通过在FROM语句之后添加AS NAME该名称可以在来连续的FROM语句或COPY --from=<name>中被使用。
  • tag或digest值也是可选的如果忽略那么会使用带lattest标签的值。
    如果FROM引用了一个多平台的镜像那么--platform可以被使用例如linux/amd64, linux/arm64, or windows/amd64.默认情况下会使用build请求目标平台的值--platform=$BUILDPLATFORM

ARG和FROM的交互

FROM指令支持使用ARG定义的变量例如

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

位于FROM指令之前的的ARG指令是不被包含在build stage中的故而无法被FROM之后的指令使用。如果想要使用在FROM之前声明的ARG变量的值可以在build stage之内使用一个没有值的ARG指令如下所示

ARG VERSION=latest
FROM busybox:$VERSION
# 再次声明VERSION,以此使用FROM之前的VERSION值
ARG VERSION
RUN echo $VERSION > image_version

RUN

RUN命令具有两种格式

  • RUN <command>shell格式该命令默认是跑在shell中的默认情况下是/bin/sh -c
  • RUN ["executable", "param1", "param2"] RUN指令将会在新的layer中执行任何command并且将结果提交提交结果镜像将会作为dockerfile中下一步操作的镜像。
    在shell格式中可以使用转义符来将RUN命令拓展到多行
RUN /bin/bash -c 'source $HOME/.bashrc && \
echo $HOME'
# 其和如下形式命令等效
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'

如果想要使用其他的shell可以使用如下形式

RUN ["/bin/bash", "-c", "echo hello"]

RUN指令的缓存在下次build操作执行时并不会失效在下次build操作时会被重用。RUN指令的缓存可以通过指定--no-cache选项被失效使用示例如下docker build --no-cache

RUN --mount

RUN --amount允许创建一个文件系统挂载build操作可以访问该文件系统。
syntax

RUN --mount=[type=<TYPE>][,option=<value>[,option=<value>]...]

Mount Types

挂载类型如下:

  • binddefault绑定装载上下文目录只读
  • cache挂载一个临时目录到缓存目录用于编译和包管理
  • secret允许build container访问secure文件例如私钥而无需将私钥拷贝到image中
  • ssh允许build container通过ssh agent访问ssh key

RUN --mount=type=bind

该mount type允许将文件或者目录绑定到build container中默认情况下一个bind-mount是只读的。

OPTION DESCRIPTION
target 挂载到的目录
source from中的source path
from source根路径的build stage或image name默认情况下为build context
rw,readwrite 允许在mount后的文件系统执行写入操作写入信息将会被丢弃

RUN --mount=type=bind允许将context或镜像中的目录绑定到build container中并且只有在该条RUN指令运行时才可以访问挂载的目录。

RUN --network

控制该命令运行在哪种网络环境下,支持如下网络环境:
syntax:RUN --network=type

  • default运行在默认网络环境下
  • none运行在无网络访问的环境下
  • host运行在宿主机的网络环境下

CMD

CMD命令具有三种形式

  • CMD ["executable","param1","param2"] (exec form)
  • CMD ["param1","param2"] (作为ENTRYPOINT的默认参数)
  • CMD command param1 param2 (shell form)

在dockerfile中只有一条CMD指令能够生效如果dockerfile中存在多条CMD指令那么只有最后一条CMD指令能够生效。
CMD指令的主要作用是为执行中的容器提供默认值。该默认值可以包含可执行文件也可以省略可执行文件将CMD指令的参数作为ENTRYPOINT指令的默认参数。

如果CMD指令用于向ENTRYPOINT指令提供默认参数那么ENTRYPOINT指令和CMD指令都要按照JSON数组的格式进行声明

当使用exec form或shell form时CMD指令制定了镜像运行时默认执行的command。
如果使用CMD的shell form那么command将会在/bin/sh -c中执行

FROM ubuntu
CMD echo "This is a test." | wc -

如果想不在shell中运行CMD那么必须将command作为JSON ARRAY传递并且指定可执行文件的full path。

FROM ubuntu
CMD ["/usr/bin/wc","--help"]

如果用户在docker run命令中指定了参数那么参数将会覆盖CMD命令提供的默认值。

LABEL

LABEL命令的格式如下所示

LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL命令向image中添加元数据一个LABEL是一个key-value对如果想要在LABEL value中包含空格需要加入双引号或是转义符

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命令中指定多个key-value pair。
**在父镜像中定义的LABEL能被继承如果父镜像和子镜像中都定义了相同的LABEL那么最近的LABEL定义将会覆盖父镜像的同名LABEL。
如果要查看一个镜像的LABEL可以通过docker image inspect命令来进行查看,可以通过--format指定显示的格式:

# docker image inspect --format='' myimage
{
  "com.example.vendor": "ACME Incorporated",
  "com.example.label-with-value": "foo",
  "version": "1.0",
  "description": "This text illustrates that label-values can span multiple lines.",
  "multi.label1": "value1",
  "multi.label2": "value2",
  "other": "value3"
}

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

EXPOSE指令会告知Docker该container在监听指定的网络端口可以指定该端口是在监听TCP或是UDP默认情况下如果没有指定protocol默认值为TCP。
EXPOSE指令实际并不开放端口其作用只是在build image的人和运行container的人之间提供文档该文档会告知哪些端口需要被开放。

想要真正的开放端口需要在运行容器时通过docker run命令指定-p选项来开放和映射端口。
默认情况下EXPOSE在未指定协议的情况下使用TCP。可以显式指定开放端口的协议为UDP。

EXPOSE 80/udp

可以通过多行EXPOSE指令同时暴露TCP和UDP端口

EXPOSE 80/tcp
EXPOSE 80/udp

不管EXPOSE命令如何设置都可以在docker run时通过-p选项来覆盖设置

$ docker run -p 80:80/tcp -p 80:80/udp ...

ENV

ENV指令将环境变量key设置为value该值可以为后续的指令提供inline替换。

如果双引号没有被转义那么value中的双引号将会被移除。

ENV指令允许一行内设置多条key-value pair。

ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy

通过ENV命令设置的环境变量将会被保留当container通过resulting image产生时。可以通过docker inspect来查看值并且通过docker run --env key=value的形式来更改。

ENV命令设置的key-value对会保留在最终的镜像中并且在镜像产生的容器中可见。

如果环境变量只是在build的过程中需要并且最终image中不希望存在该环境变量可以考虑只为单行命令设置value

RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...

或者可以考虑ARGARG不会持久化到最终的image中

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...

ADD

ADD指令拥有两种形式

ADD [--chown=<user>:<group>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

如果路径中包含空格,需要使用第二种形式。
ADD指令会从src拷贝新文件、目录或remote file URL并将其添加到镜像的文件系统中path为dest
可以指定复数个src资源,但如果src为文件或者目录,那么src的path将会被解释为相对于build context的路径。
每个src可以含有通配符,示例如下:

ADD hom* /mydir/

?符号可以匹配任意单个字符,示例如下:

ADD hom?.txt /mydir/

dest是一个绝对路径,或是相对于WORKDIR的相对路径,src将会被复制到目标容器中。
如果想要ADD包含特殊字符的文件或目录例如[]),需要对这些路径进行转义。

ADD --chown

所有新文件和目录创建时UID和GID都是0如果想要为新建文件或目录指定其他的值可以使用--chown选项来指定username:groupname或`uid:gid.
在仅仅指定了username或UID而没有指定groupname或GID的情况下将会把GID设置为和UID相同的值。
ADD指令的使用示例如下

ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
  • 如果src是一个本地archive并且以一种可识别的格式压缩那么其会被解压为一个目录从remote获取的资源不会被解压。
  • 如果制定了多个src,则dest必须是目录,并且dest必须以/结尾。
  • 如果dest没有指定/作为结尾,那么dest被视作一个常规文件。
  • 如果dest不存在,那么其会自动创建路径中所有的缺失目录。

ADD <git ref> <dir>

该形式允许添加一个git仓库到镜像中而不需要镜像中存在git命令。

ADD [--keep-git-dir=<boolean>] <git ref> <dir>

--keep-git-dir选项代表是否保存git仓库中的.git目录该选项的默认值是false.

COPY

COPY指令可以按如下两种形式编写

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

如果路径中含有空格,使用第二种形式。
COPY命令从src中复制文件和目录并且将其添加到容器文件系统中,添加路径为dest.
COPY的使用示例如下所示

COPY hom* /mydir/

类似ADD --chownCOPY命令也支持COPY --chown的用法.

COPY --from

COPY支持--from=<name>选项,可以将source设置为先前的build stage而不是build context。如果找不到具有相同名称的build stage则会采用具有相同名称的image

ENTRYPOINT

ENTRYPOINT命令具有两种格式:

  • exec格式:
    ENTRYPOINT ["executable", "param1", "param2"]
    
  • shell格式:
    ENTRYPOINT command param1 param2
    

ENTRYPOINT允许像一个可执行程序一样配置容器。
例如如下示例使用默认内容启动了一个nginx实例监听80端口

docker run -i -t --rm -p 80:80 nginx

docker run <image>命令之后的命令行参数将添加到exec形式ENTRYPOINT命令的所有元素之后**并且命令行参数会覆盖CMD指令中指定的元素。**其允许参数被传递给entry point。
例如,docker run <image> -d将会把-d传递给entry point。

可以通过docker run --entrypoint命令来覆盖ENTRYPOINT指令。

shell格式的ENTRYPOINT将阻止任何CMDrun command line参数被使用,但是shell形式的ENTRYPOINT会作为/bin/sh -c的一个子命令启动。

如果ENTRYPOINT作为/bin/sh -c的一个子命令启动,意味着目标可执行文件并不是容器的PID 1也不会接收Unix信号-可以执行文件不会从docker stop命令中接收到SIGTERM信号。

只有dockerfile中的最后一个ENTRYPOINT命令会起作用。
如果想要在使用ENTRYPOINT shell form时保证PID 1进程是目标可执行文件可以在ENTRYPOINT指令前加上exec,示例如下所示:

FROM ubuntu
ENTRYPOINT exec top -b

如果shell form不带exec,该命令将作为/bin/sh -c的子命令执行,PID 1进程为sh而不是top,调用docker stop时容器SIGTERM信号将会被发送给sh,然后top进程会在超时之后接收到一个SIGKILL信号并无法干净的退出。

ENTRYPOINT和CMD指令的交互

ENTRYPOINT指令和CMD指令都用于指定容器启动时的命令,它们遵循如下规则:

  • dockerfile至少应该指定一条ENTRYPOINT或CMD指令
  • 当想要将容器像可执行文件一样运行(可以在docker run命令后添加传递给ENTRYPOINT指令的参数时),应该使用ENTRYPOINT指令
  • 当为docker run命令指定了额外参数时,CMD指令中为ENTRYPOINT指令指定的默认参数将会被命令行参数覆盖

VOLUME

VOLUME ["/data"]

VOLUMN指令创建了一个具有指定名称的挂载点并且将其标记为持有外部挂载volume。VOLUMNE指令的值可以通过json array的形式指定VOLUME ["/var/log/"]),也可以通过纯字符串的形式来指定(VOLUME /var/logVOLUME /var/log /var/db)。
VOLUMN指令会使用基础image指定目录下的数据来初始化新创建的volume示例如下

FROM ubuntu
RUN mkdir /myvol
RUN 
Learn more about the "RUN " Dockerfile command.
echo "hello world" > /myvol/greeting
VOLUME /myvol

在使用docker run指令执行上述dockerfile产生镜像时会在/myvol创建一个挂载点并且将greeting文件复制到新创建的volume中。

VOLUME指令要点

  • 如果在VOLUME指令创建volume之后任何build step修改了volume路径下的数据那些修改都会被丢弃

USER

USER <user>[:<group>]
# or
USER <UID>[:<GID>]

USER指令用于指定当前stage剩余steps的默认user和group。指定用户用于RUN指令和ENTRYPOINT,CMD命令的执行。

WORKDIR

WORKDIR /path/to/workdir

WORKDIR用于设置dockerfile中位于该命令之后步骤的工作目录。如果指定的工作目录不存在那么会创建该工作目录。
在dockerfile中WORKDIR指令可以使用多次如果提供了相对路径相对路径会基于当前工作目录解析

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

最终pwd输出的路径是/a/b/c。
WORKDIR指令可以解析环境变量只可以解析显式在dockerfile中设置的环境变量

ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

输出值为/path/$DIRNAME

如果WORKDIR尚未被指定那么WORKDIR的默认值是/。如果父镜像中设置了WORKDIR的值那么dockerfile将使用父镜像的WORKDIR

为了避免在非预期的目录中执行操作最好显式在dockerfile中设置WORKDIR.

ARG

ARG <name>[=<default value>]

ARG指令可以定义一系列变量定义的变量在build时可以通过docker build --build-arg <var-name>=<var-value>来设置值。
如果在--build-arg中传递了dockfile中不存在的参数会抛出警告表示该变量没有被dockerfile消费。

FROM busybox
ARG user1
ARG buildno
# ...

default value

可以为ARG指定一个默认值示例如下所示

FROM busybox
ARG user1=someuser
ARG buildno=1
# ...

如果在build时没有传递值给ARG变量那么会使用默认值。

Scope

一个ARG指令的作用域范围从定义该ARG行开始算起一直到当前build stage结束。

ENV和ARG使用

当ENV和ARG指令同时被定义时ENV定义的变量总是会覆盖ARG指令定义的变量。

预定义的ARG

dockerfile含有一组预定义的ARG变量可以直接使用这些预定义的ARG变量而无需在dockerfile中添加ARG指令

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy
  • ALL_PROXY
  • all_proxy
 docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .

ONBUILD

ONBUILD <INSTRUCTION>

ONBUILD指令添加了一个触发器当该镜像作为其他镜像的基础镜像时ONBUILD指定的指令将会被执行。ONBUILD触发器的指令将会在下游build操作的context中被执行就像ONBUILD包含的指令被直接插入到下游FROM指令之后。
使用示例如下:

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src