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

530 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [Dockerfile](#dockerfile)
- [format](#format)
- [parser directive](#parser-directive)
- [syntax](#syntax)
- [escape](#escape)
- [ENV](#env)
- [.dockerignore file](#dockerignore-file)
- [FROM](#from)
- [ARG和FROM的交互](#arg和from的交互)
- [RUN](#run)
- [RUN --mount](#run---mount)
- [Mount Types](#mount-types)
- [RUN --mount=type=bind](#run---mounttypebind)
- [RUN --network](#run---network)
- [CMD](#cmd)
- [LABEL](#label)
- [EXPOSE](#expose)
- [ENV](#env-1)
- [ADD](#add)
- [ADD --chown](#add---chown)
- [ADD \<git ref\> \<dir\>](#add-git-ref-dir)
- [COPY](#copy)
- [COPY --from](#copy---from)
- [ENTRYPOINT](#entrypoint)
- [ENTRYPOINT和CMD指令的交互](#entrypoint和cmd指令的交互)
- [VOLUME](#volume)
- [VOLUME指令要点](#volume指令要点)
- [USER](#user)
- [WORKDIR](#workdir)
- [ARG](#arg)
- [default value](#default-value)
- [Scope](#scope)
- [ENV和ARG使用](#env和arg使用)
- [预定义的ARG](#预定义的arg)
- [ONBUILD](#onbuild)
# Dockerfile
Docker可以通过读取dockerfile中的指令自动构建image。
## format
如下是dockerfile的格式
```dockerfile
# Comment
INSTRUCTION arguments
```
指令是不区分大小写的但是习惯将其大写以区分INSTRUCTION和arguments。
Dockerfile中的指令将会被按顺序执行dockerfile必须以FROM指令开头。FROM指令制定了构建的Parent image。
## parser directive
parser指令是可选的并影响dockerfile后续的处理方式。parser指令并不会向build过程中添加layer也不会展示为一个build过程。parser指令的格式如下所示
```dockerfile
# directive=value
```
一旦任何comment、空行或builder instruction被处理docker将不再接受parser directive即使后面有格式符合parser instruction的行也会被当作注释处理。故而parser instruction必须位于dockerfile的最顶端。
parser directive是大小写不敏感的但是推荐将其小写。约定也要求在任何parser directive后添加一个空白行。
**如下格式是无效的:**
```dockerfile
# direc \
tive=value
```
**如下格式中parser指令连续出现了两次也是无效的**
```dockerfile
# directive=value1
# directive=value2
FROM ImageName
```
**由于之前已经有一条指令如下parser指令将会被当作注释处理:**
```dockerfile
FROM ImageName
# directive=value
```
**由于之前存在一条注释故而parser指令也会被当作注释处理**
```dockerfile
# About my dockerfile
# directive=value
FROM ImageName
```
**不被识别的parser指令也会被当作注释处理由于上一条parser指令不被识别被当作注释故而第二条能被识别的parser指令也会被当作注释处理**
```dockerfile
# unknowndirective=value
# knowndirective=value
```
**非换行空白符允许在parser指令中出现故而下列格式的parser instruction都是允许的**
```dockerfile
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
```
dockerfile支持如下两条parser directive
- syntax
- escape
### syntax
该特性仅当使用了BuildKit backend时可用当使用classic builder backend时会被忽略。
### escape
```dockerfile
# escape=\ (backslash)
```
or
```dockerfile
# 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否则返回空字符串
可以通过\符号对$进行转义
```dockerfile
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指令结束时的值
```dockerfile
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的文件
```.dockerignore
*.md
!README.md
```
上述代表排除所有.md文件但是不排除README.md
> 在.dockerignore文件中匹配的最后一条pattern将会决定该文件是否包含在context中
```.dockerignore
*.md
!README*.md
README-secret.md
```
上诉代表所有的.md文件都会被移除但是满足README*.md格式的文件将会被保留README-secret.md文件会被移除。
> 如果想要在.dockerignore中指定哪些文件要被包含而不是指定哪些文件被排除可以使用如下形式
```.dockerignore
*
!include-pattern...
```
## FROM
FROM命令格式如下
```dockerfile
FROM [--platform=<platform>] <image> [AS <name>]
```
or
```dockerfile
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
```
or
```dockerfile
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定义的变量例如
```dockerfile
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指令如下所示
```dockerfile
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命令拓展到多行
```dockerfile
RUN /bin/bash -c 'source $HOME/.bashrc && \
echo $HOME'
# 其和如下形式命令等效
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'
```
如果想要使用其他的shell可以使用如下形式
```dockerfile
RUN ["/bin/bash", "-c", "echo hello"]
```
RUN指令的缓存在下次build操作执行时并不会失效在下次build操作时会被重用。RUN指令的缓存可以通过指定--no-cache选项被失效使用示例如下docker build --no-cache
## RUN --mount
RUN --amount允许创建一个文件系统挂载build操作可以访问该文件系统。
syntax
```dockerfile
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`中执行
```dockerfile
FROM ubuntu
CMD echo "This is a test." | wc -
```
如果想不在shell中运行CMD那么必须将command作为JSON ARRAY传递并且指定可执行文件的full path。
```dockerfile
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
```
如果用户在`docker run`命令中指定了参数那么参数将会覆盖CMD命令提供的默认值。
## LABEL
LABEL命令的格式如下所示
```dockerfile
LABEL <key>=<value> <key>=<value> <key>=<value> ...
```
LABEL命令向image中添加元数据一个LABEL是一个key-value对如果想要在LABEL value中包含空格需要加入双引号或是转义符
```dockerfile
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`指定显示的格式:
```shell
# docker image inspect --format='' myimage
```
```json
{
"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
```dockerfile
EXPOSE <port> [<port>/<protocol>...]
```
EXPOSE指令会告知Docker该container在监听指定的网络端口可以指定该端口是在监听TCP或是UDP默认情况下如果没有指定protocol默认值为TCP。
EXPOSE指令实际并不开放端口其作用只是在build image的人和运行container的人之间提供文档该文档会告知哪些端口需要被开放。
> 想要真正的开放端口需要在运行容器时通过docker run命令指定-p选项来开放和映射端口。
默认情况下EXPOSE在未指定协议的情况下使用TCP。可以显式指定开放端口的协议为UDP。
```dockerfile
EXPOSE 80/udp
```
可以通过多行EXPOSE指令同时暴露TCP和UDP端口
```dockerfile
EXPOSE 80/tcp
EXPOSE 80/udp
```
不管EXPOSE命令如何设置都可以在`docker run`时通过-p选项来覆盖设置
```shell
$ docker run -p 80:80/tcp -p 80:80/udp ...
```
## ENV
ENV指令将环境变量key设置为value该值可以为后续的指令提供inline替换。
> 如果双引号没有被转义那么value中的双引号将会被移除。
ENV指令允许一行内设置多条key-value pair。
```dockerfile
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
```dockerfile
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
```
或者可以考虑ARGARG不会持久化到最终的image中
```dockerfile
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
```
## ADD
ADD指令拥有两种形式
```dockerfile
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`可以含有通配符,示例如下:
```dockerfile
ADD hom* /mydir/
```
`?`符号可以匹配任意单个字符,示例如下:
```dockerfile
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指令的使用示例如下
```dockerfile
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命令。
```dockerfile
ADD [--keep-git-dir=<boolean>] <git ref> <dir>
```
`--keep-git-dir`选项代表是否保存git仓库中的.git目录该选项的默认值是`false`.
## COPY
COPY指令可以按如下两种形式编写
```dockerfile
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
```
如果路径中含有空格,使用第二种形式。
COPY命令从`src`中复制文件和目录并且将其添加到容器文件系统中,添加路径为`dest`.
COPY的使用示例如下所示
```dockerfile
COPY hom* /mydir/
```
类似`ADD --chown``COPY`命令也支持`COPY --chown`的用法.
### COPY --from
COPY支持`--from=<name>`选项,可以将`source`设置为先前的build stage而不是build context。**如果找不到具有相同名称的build stage则会采用具有相同名称的image**。
## ENTRYPOINT
`ENTRYPOINT`命令具有两种格式:
- ***exec***格式:
```dockerfile
ENTRYPOINT ["executable", "param1", "param2"]
```
- ***shell***格式:
```dockerfile
ENTRYPOINT command param1 param2
```
ENTRYPOINT允许像一个可执行程序一样配置容器。
例如如下示例使用默认内容启动了一个nginx实例监听80端口
```dockerfile
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将阻止任何`CMD`或`run 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`,示例如下所示:
```dockerfile
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
```dockerfile
VOLUME ["/data"]
```
`VOLUMN`指令创建了一个具有指定名称的挂载点并且将其标记为持有外部挂载volume。VOLUMNE指令的值可以通过json array的形式指定`VOLUME ["/var/log/"]`),也可以通过纯字符串的形式来指定(`VOLUME /var/log`或`VOLUME /var/log /var/db`)。
VOLUMN指令会使用基础image指定目录下的数据来初始化新创建的volume示例如下
```dockerfile
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
```dockerfile
USER <user>[:<group>]
# or
USER <UID>[:<GID>]
```
USER指令用于指定当前stage剩余steps的默认user和group。指定用户用于RUN指令和ENTRYPOINT,CMD命令的执行。
## WORKDIR
```dockerfile
WORKDIR /path/to/workdir
```
WORKDIR用于设置dockerfile中位于该命令之后步骤的工作目录。如果指定的工作目录不存在那么会创建该工作目录。
在dockerfile中WORKDIR指令可以使用多次如果提供了相对路径相对路径会基于当前工作目录解析
```dockerfile
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
```
最终pwd输出的路径是/a/b/c。
WORKDIR指令可以解析环境变量只可以解析显式在dockerfile中设置的环境变量
```dockerfile
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
```
输出值为`/path/$DIRNAME`
> 如果WORKDIR尚未被指定那么WORKDIR的默认值是`/`。如果父镜像中设置了WORKDIR的值那么dockerfile将使用父镜像的WORKDIR
为了避免在非预期的目录中执行操作最好显式在dockerfile中设置WORKDIR.
## ARG
```dockerfile
ARG <name>[=<default value>]
```
ARG指令可以定义一系列变量定义的变量在build时可以通过`docker build --build-arg <var-name>=<var-value>`来设置值。
如果在`--build-arg`中传递了dockfile中不存在的参数会抛出警告表示该变量没有被dockerfile消费。
```dockerfile
FROM busybox
ARG user1
ARG buildno
# ...
```
### default value
可以为ARG指定一个默认值示例如下所示
```dockerfile
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
```shell
docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
```
## ONBUILD
```dockerfile
ONBUILD <INSTRUCTION>
```
ONBUILD指令添加了一个触发器当该镜像作为其他镜像的基础镜像时ONBUILD指定的指令将会被执行。ONBUILD触发器的指令将会在下游build操作的context中被执行就像ONBUILD包含的指令被直接插入到下游FROM指令之后。
使用示例如下:
```dockerfile
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
```