1官方文档:Dockerfile reference | Docker Documentation
一、说明
我们将以HelloWorld案例的方法,由浅入深地理解DockerFile指令,并生成自己的镜像。为了避免冗长的官网文章污染您的视线,这里将重要的,常见的指令应用展现出来。这里不累述更深刻的语句用法,因为篇幅实在太大。
二、镜像制作过程
2.1 编写和制作过程
使用dockerfile制作镜像,简单地说就是以下三步:
- 编写dockerfie
- docker build 指令编译
- run新镜像
2.2 docker build语法
docker build就是制作image的编译过程。
语法是:
docker build [OPTIONS] PATH | URL | -
2.3 官网上的[OPTIONS](参数)表
Name, shorthand | Default | Description |
--add-host | Add a custom host-to-IP mapping (host:ip) | |
--build-arg | Set build-time variables | |
--cache-from | Images to consider as cache sources | |
--cgroup-parent | Optional parent cgroup for the container | |
--compress | Compress the build context using gzip | |
--cpu-period | Limit the CPU CFS (Completely Fair Scheduler) period | |
--cpu-quota | Limit the CPU CFS (Completely Fair Scheduler) quota | |
--cpu-shares , -c | CPU shares (relative weight) | |
--cpuset-cpus | CPUs in which to allow execution (0-3, 0,1) | |
--cpuset-mems | MEMs in which to allow execution (0-3, 0,1) | |
--disable-content-trust | true | Skip image verification |
--file , -f | Name of the Dockerfile (Default is PATH/Dockerfile) | |
--force-rm | Always remove intermediate containers | |
--iidfile | Write the image ID to the file | |
--isolation | Container isolation technology | |
--label | Set metadata for an image | |
--memory , -m | Memory limit | |
--memory-swap | Swap limit equal to memory plus swap: -1 to enable unlimited swap | |
--network | Set the networking mode for the RUN instructions during build | |
--no-cache | Do not use cache when building the image | |
--platform | Set platform if server is multi-platform capable | |
--pull | Always attempt to pull a newer version of the image | |
--quiet , -q | Suppress the build output and print image ID on success | |
--rm | true | Remove intermediate containers after a successful build |
--security-opt | Security options | |
--shm-size | Size of /dev/shm | |
--squash | experimental (daemon) | |
--tag , -t | Name and optionally a tag in the name:tag format | |
--target | Set the target build stage to build. | |
--ulimit | Ulimit options | |
--help | Print usage |
注意,此表格首先掌握-t,-f 就基本够了,其它参数可以用时现学。
三、制作Hello-world镜像实例
我们先从制作Hello-world镜像实验开始。该案例虽很简单,但足以表明制作过程的必要步骤,剩下的仅仅是制作更复杂的案例。
3.1 环境要求
- ubuntu或其它linux
- 已经安装docker
- 已经安装了gcc
3.2 制作路径过程
先建立工程目录imgmajer:
mkdir -p ~/imgmaker/src ~/imgmaker/build
cd ~/imgmaker
touch dockerfile
结构如下:
3.3 建立C语言hello-world程序
cd ~/imgmaker/src
vim hello.c
1)hello-world程序
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("hello world\n");
return 0;
}
2)编译
cd ~/imgmaker
gcc ./src/hello.c -o ./build/hello
至此,~/imgmaker/build目录下有了hello进程。
3.4 建立dockerfile文件
cd ~/imgmaker
sudo vim dockerfile
将以下文本粘贴进去。
FROM ubuntu:20.04
MAINTAINER smith smith@qq.com
RUN mkdir /data
COPY ./build/hello /data
ENTRYPOINT ["/data/hello", "smith"]
3.5 编译产生结果镜像
docker build -t hellome:1.0 .
编译后生成镜像hellome:1.0,可以通过docker images看到。
3.6 执行run生成容器
启动新建镜像:
docker run hellome:1.0
至此,基于dockerfile完整地构造出一个新镜像hellome。
四、常见指令简表
保留字表:
保留字 | 意义 | 重要程度 |
---|---|---|
FROM | 定义本镜像的基镜像 | ★★★★★ |
ARG | 对FORM语句的参数部分定义变量 | ★★★ |
MAINTAINER | 维护人员名称 | ★★ |
RUN | 在构造过程的shell指令前加RUN号 | ★★★★★ |
WORKDIR | 指定宿主机工作目录 | ★★★★ |
ADD | 拷贝文件,如果是包文件,解压成目录 | ★★★★★ |
COPY | 拷贝文件 | ★★★★★ |
VOLUME | 数据卷定义 | ★★★ |
CMD | 该镜像通过docker run加载成容器时,运行哪个进程 | ★★★★★ |
ENTERPOINT | 与CMD相同,可以加参数 | ★★★★★ |
EXPOSE | 镜像内守护进程的端口暴露 | ★★★★ |
USER | 指定镜像使用的用户权限 | ★★ |
ENV | 环境宏变量定义 | ★★★ |
五、DockerFile指令详述
5.1 FROM语句
5.1.1 概念
- FROM是第一构造语句,是定义基镜像的。(相当于:pull镜像+run镜像)
- 如果dockerfile只构造一个镜像,FROM语句是唯一的。
- 如果在一个dockerfile构造多个镜像,可以多次出现,每个FROM相当于另起一个dockerfile。
- 如果前边构造出的镜像是后面构造的基镜像。FROM也可以多次出现。
- ARG语句是专门为FROM语句提供参数变量的,是唯一的可以在FROM语句之前出现语句。
- 在FROM语句之后,实际上是容器操作。
2)FROM的语法
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
3)可选项说明
以上两个格式都合法。分别举例如下:
- FROM ubuntu 此处将ubuntu: latest拉取过来做为基
- FROM ubuntu:16.04 此处将ubuntu:16.04 拉取过来做为基
自选项目
- [--platform=<platform>] 这是如果
如果 FROM 引用多平台镜像,可选的 --platform 标志可用于指定图像的平台。例如,linux/amd64、linux/arm64 或 windows/amd64。
- [AS <name>]
是个阶段可选项,可以通过向FROM指令添加AS名称来为新的构建阶段指定名称。该名称可以在随后的FROM和COPY-FROM=<name>指令中使用,以引用在此阶段中构建的映像。
4)补充事项
- ARG是Dockerfile中唯一可以在FROM之前的指令。请参阅了解ARG和FROM如何交互。
- FROM可以在一个Dockerfile中多次出现,以创建多个映像或将一个构建阶段用作另一个构建的依赖项。只需在每个新的FROM指令之前记录提交输出的最后一个镜像ID。
- 每个FROM指令都会清除先前指令创建的任何状态。
5.2 ARG指令
- ARG定义一个参量。
- ARG定义参量为可以用于FROM语句。
- 通过在docker build语句加 --build-arg <varname>=<value>选项将参量带入Dockerfile中。
5.2.1 ARG语法:
ARG <name>[=<default value>]
ARG 指令定义了一个变量,用户可以在构建时使用 --build-arg <varname>=<value> 标志使用 docker build 命令将其传递给构建器。在 Dockerfile 中,构建会输出警告。
[Warning] One or more build-args [foo] were not consumed.
一个 Dockerfile 可能包含一个或多个 ARG 指令。例如,以下是一个有效的 Dockerfile:
FROM busybox
ARG user1
ARG buildno
5.2.2 参考 ARG和FROM 交互
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 在构建阶段之外,因此它不能在 FROM 之后的任何指令中使用。构建阶段:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
5.2.3 参数 ARG和build带入
通过在docker build语句加 --build-arg <varname>=<value>选项将参量带入后,将刷新dockerfile定义的varname的值。
示例:编写两句话的dockerfile有
ARG us=sss
RUN echo $us
采用编译指令:docker build -t myImage:1.0 .
输出sss
采用编译指令:docker build --build-arg=yyy -t myImage:1.0 .
输出yyy
5.3 MAITAINER
维护者标记,现已弃用,目前用LABEL替换。LABEL优点是:新制造的镜像中、他的容器中,都可以看到作者。
- 语法:
MAINTAINER <name>
MAINTAINER 指令设置生成图像的 Author 字段。LABEL 指令是一个更灵活的版本,您应该使用它,因为它可以设置您需要的任何元数据,并且可以轻松查看,例如使用 docker inspect要设置与 MAINTAINER 字段对应的标签,您可以使用:
LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"
这将在 docker inspect 中与其他标签一起可见。
5.4 RUN指令
- RUN是制作镜像过程中shell指令的执行环节。
- 指令将在当前镜像之上的新层中执行任何命令并提交结果。也就是每个RUN语句就加一层。
5.4.1 语法格式:
RUN <command>
(shell form, the command is run in a shell, which by default is/bin/sh -c
on Linux orcmd /S /C
on Windows)RUN ["executable", "param1", "param2"]
(exec form)
5.4.2 注意点:
- exec 形式可以避免 shell 字符串修改,并使用不包含指定 shell 可执行文件的基本映像运行命令。
- 可以使用 SHELL 命令更改 shell 形式的默认 shell。
- 在 shell 形式中,您可以使用(反斜杠)将单个 RUN 指令继续到下一行。例如,考虑这两行:
RUN /bin/bash -c 'source $HOME/.bashrc && \
echo $HOME'
上面可以合并成下列行:
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'
5.4.3 RUN的exec格式
要使用除“/bin/sh”之外的其他 shell,请使用传入所需 shell 的 exec 形式。
RUN ["/bin/bash", "-c", "echo hello"]
5.5 ENV 环境参量宏替换
环境变量(用 ENV 语句声明),可以在某些指令中由 Dockerfile 解释的变量。类似于编译中的宏替换。
使用环境变量时,在用 $variable_name 或 ${variable_name} ,编译时用值替换之。
${variable_name} 语法还支持一些标准的 bash 修饰符,如下所示:
- ${variable:-word} :表示如果variable被赋值,那么结果将是哪个值;如果variable未赋值,结果不是空,而是默认值word。
- ${variable:+word} 表示如果variable被赋值了,则结果为word,否则如未赋值结果为空字符串。
在所有情况下,该词可以是任何字符串,包括其他环境变量。
通过在变量前添加 \ 可以进行转义:例如,\$foo 或 \${foo} 将分别转换为 $foo 和 ${foo} 文字。
4 示例(解析后的表示形式显示在 # 之后):
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO} # WORKDIR /bar
ADD . $FOO # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux
5.6 WORKDIR指令
该指令指出image当前工作目录是哪个(注意不是指宿主机上的路径)。
WORKDIR 指令为 Dockerfile 中跟在它后面的任何 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令设置工作目录。
5.6.1 语法:
WORKDIR /path/to/workdir
5.6.2 WORKDIR指令在一个Dockerfile中可以多次使用
如果提供相对路径,则相对于上一条WORKDIR指令的路径。
WORKDIR /a 绝对路径
WORKDIR b 相对路径
WORKDIR c 相对路径
RUN pwd
此 Dockerfile 中最后一个 pwd 命令的输出将是 /a/b/c。
5.6.3 隐式表示
WORKDIR 指令可以解析先前使用 ENV 设置的环境变量。您只能使用在 Dockerfile 中显式设置的环境变量。
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
这个 Dockerfile 中最后一个 pwd 命令的输出将是 /path/$DIRNAME
如果未指定,则默认工作目录为 /。实际上,如果您不是从头开始构建 Dockerfile (FROM scratch),则 WORKDIR 可能由您使用的基础映像设置。
因此,为避免在未知目录中进行意外操作,最好明确设置您的 WORKDIR。
5.7 USER指令
- 设置镜像操作的人员和组。
- 这和后边的权限管理相关。
5.7.1 语法
USER <user>[:<group>]
USER <UID>[:<GID>]
5.7.2 注意事项
USER 指令设置用户名(或 UID)和可选的用户组(或 GID)以用作当前阶段剩余部分的默认用户和组。指定的用户用于 RUN 指令,并在运行时运行相关的 ENTRYPOINT 和 CMD 命令。
请注意,为用户指定群组时,用户将只有指定的群组成员资格。
警告
当用户没有主要组时,镜像(或下一条指令)将与根组一起运行。在 Windows 上,如果用户不是内置帐户,则必须先创建它。
FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add patrick
# Set it for subsequent commands
USER patrick
5.8 COPY指令
是指文件从宿主机拷贝到制作中容器的过程。
5.8.1 COPY 的两种格式
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
5.8.2 COPY 的解释
- COPY 指令从 <src> 复制新文件或目录,并将它们添加到容器的文件系统路径 <dest> 中。
- 采用两种格式的原因是:有些路径是在win10是有空格的,包含空格的路径需要后一种形式。
- [--chown=<user>:<group>]是对操作追加操作权限。
- --chown 仅在用于构建 Linux 容器的 Dockerfile 上受支持,在 Windows 容器上不起作用。将用户名和组名转换为 ID 限制此功能仅适用于基于 Linux 操作系统的容器。
- 可以指定多个 <src> 资源,但文件和目录的路径将被解释为相对于构建上下文的源。
- 每个 <src> 可能包含通配符。
5.8.3 通配符举例
- 添加以“hom”开头的所有文件:
COPY hom* /mydir/
- 在下面的示例中,? 被替换为任何单个字符,例如“home.txt”。
COPY hom?.txt /mydir/
- <dest> 是绝对路径,或相对于 WORKDIR 的路径,源将被复制到目标容器内。
- 下面的示例使用相对路径,并将“test.txt”添加到 <WORKDIR>/relativeDir/:
COPY test.txt relativeDir/
而此示例使用绝对路径,并将“test.txt”添加到 /absoluteDir/
COPY test.txt /absoluteDir/
当复制包含特殊字符(如[和])的文件或目录时,需要按照Golang规则对那些路径进行转义,防止它们被当作匹配模式。.txt,使用如下;
COPY arr[[]0].txt /mydir/
所有新文件和目录都使用 0 的 UID 和 GID 创建,除非可选的 --chown 标志指定给定的用户名、组名或 UID/GID 组合以请求复制内容的特定所有权。标志允许用户名和组名字符串或直接整数 UID 和 GID 的任意组合。提供不带组名的用户名或不带 GID 的 UID 将使用与 GID 相同的数字 UID。/etc/passwd 和 /etc/group 文件将用于从名称执行转换分别为整数 UID 或 GID。
5.8.4 权限举例
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
如果容器根文件系统不包含 /etc/passwd 或 /etc/group 文件,并且在 --chown 标志中使用了用户名或组名,则构建将在 COPY 操作中失败。
5.9 ADD指令
- ADD基本实现COPY指令。
- ADD不仅拷贝文件,而且将.tar等文件解压成镜像内目录和文件。
- ADD 指令从 <src> 复制新文件、目录或远程文件 URL,并将它们添加到镜像的文件系统路径 <dest> 中。
- 。
5.9.1 ADD有两个格式
ADD [--chown=<user>:<group>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
( 包含空格的路径需要后一种形式)
注意
--chown 仅在用于构建 Linux 容器的 Dockerfile 上受支持,在 Windows 容器上不起作用。将用户名和组名转换为 ID 限制此功能仅适用于基于 Linux 操作系统的容器。
可以指定多个 <src> 资源,但如果它们是文件或目录,它们的路径将被解释为相对于构建上下文的源。
5.9.2 通配符使用
添加以“hom”开头的所有文件:
ADD hom* /mydir/
在下面的示例中,? 被替换为任何单个字符,例如“home.txt”。
ADD hom?.txt /mydir/
5.9.3 关于路径
<dest> 是绝对路径,或相对于 WORKDIR 的路径,源将被复制到目标容器内。下面的示例使用相对路径,并将“test.txt”添加到 <WORKDIR>/relativeDir/:
ADD test.txt relativeDir/
而此示例使用绝对路径,并将“test.txt”添加到 /absoluteDir/
ADD test.txt /absoluteDir/
在添加包含特殊字符(如[和])的文件或目录时,需要按照Golang规则对这些路径进行转义,防止它们被视为匹配模式。例如添加名为arr[0]的文件.txt,使用以下内容;
ADD arr[[]0].txt /mydir/
5.9.4 关于带权限追加文件、目录
所有新文件和目录都使用 0 的 UID 和 GID 创建,除非可选的 --chown 标志指定给定的用户名、组名或 UID/GID 组合以请求添加内容的特定所有权。标志允许用户名和组名字符串或直接整数 UID 和 GID 的任意组合。提供不带组名的用户名或不带 GID 的 UID 将使用与 GID 相同的数字 UID。/etc/passwd 和 /etc/group 文件将用于从名称执行转换分别为整数 UID 或 GID。
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
如果容器根文件系统不包含 /etc/passwd 或 /etc/group 文件,并且在 --chown 标志中使用了用户名或组名,则构建将在 ADD 操作上失败。使用数字 ID 不需要查找和将不依赖于容器根文件系统内容。
5.10 EXPOSE语句
- 是专门对网络访问提供端口暴露。
- EXPOSE指令通知Docker容器在运行时监听指定的网络端口,可以指定端口监听TCP还是UDP,不指定协议默认为TCP。
5.10.1 语法
EXPOSE <port> [<port>/<protocol>...]
5.10.2 端口发布
EXPOSE 指令实际上并没有发布端口。它的作用是构建镜像的人和运行容器的人之间的一种文档,关于哪些端口打算发布。在 docker run 上使用 -pflag 来发布并映射一个或多个端口,或 -P 标志以发布所有暴露的端口并将它们映射到高阶端口。
默认情况下,EXPOSE 假定 TCP。
EXPOSE 80/udp
要在 TCP 和 UDP 上公开,请包括两行:
EXPOSE 80/tcp
EXPOSE 80/udp
在这种情况下,如果您将 -P 与 docker run 一起使用,端口将为 TCP 公开一次,为 UDP 公开一次。请记住,-P 在主机上使用临时高阶主机端口,因此端口不会相同用于 TCP 和 UDP。
无论 EXPOSE 设置如何,您都可以在运行时使用 -p 标志覆盖它们。
$ docker run -p 80:80/tcp -p 80:80/udp ...
要在主机系统上设置端口重定向,请参阅使用 -P 标志。docker network 命令支持为容器之间的通信创建网络,而无需公开或发布特定端口,因为连接到网络的容器可以相互通信通过任何端口。有关详细信息,请参阅此功能的概述。
5.11 CMD指令
5.11.1 CMD
指令的三个格式:
CMD ["executable","param1","param2"]
(exec form, this is the preferred form)CMD ["param1","param2"]
(as default parameters to ENTRYPOINT)CMD command param1 param2
(shell form)
一个Dockerfile中只能有一条CMD指令,如果列出多个CMD则只有最后一条CMD生效。CMD 的主要目的是为正在执行的容器提供默认值。
如果使用 CMD 为 ENTRYPOINT 指令提供默认参数,则 CMD 和 ENTRYPOINT 指令都应使用 JSON 数组格式指定。
Note
exec 格式被解析为 JSON 数组,这意味着您必须在单词周围使用双引号 (“) 而不是单引号 (‘)。
与 shell 形式不同,exec 形式不调用命令 shell。这意味着正常的 shell 处理不会发生。例如,CMD [ "echo", "$HOME" ] 不会对 $HOME 进行变量替换。want shell处理然后使用shell形式或直接执行shell,例如:CMD [“sh”,“-c”,“echo $HOME”]。对于shell形式,是shell在做环境变量扩展,而不是 docker。
5.11.2 CMD
指令举例
当以 shell 或 exec 格式使用时,CMD 指令设置运行图像时要执行的命令。如果您使用 CMD 的 shell 形式,那么 <command> 将在 /bin/sh -c 中执行:
FROM ubuntu
CMD echo "This is a test." | wc -
如果你想在没有 shell 的情况下运行你的 <command> 那么你必须将命令表示为 JSON 数组并给出可执行文件的完整路径。这种数组形式是 CMD 的首选格式。任何附加参数必须单独表示为字符串在数组中:
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
如果您希望您的容器每次都运行相同的可执行文件,那么您应该考虑将 ENTRYPOINT 与 CMD 结合使用。
如果用户为 docker run 指定参数,那么他们将覆盖 CMD 中指定的默认值。
Note
不要将 RUN 与 CMD 混淆。RUN 实际上运行一个命令并提交结果;CMD 在构建时不执行任何操作,而是为图像指定预期的命令。
5.12 VOLUME语句
VOLUME 指令创建一个具有指定名称的挂载点,并将其标记为持有来自本机主机或其他容器的外部挂载卷。
5.12.1 语法格式
VOLUME ["/data"]
多个参数,例如 VOLUME /var/log 或 VOLUME /var/log /var/db。有关更多信息/示例和通过 Docker 客户端安装说明,请参阅通过卷文档共享目录。
docker run 命令使用基础映像中指定位置存在的任何数据初始化新创建的卷。例如,考虑以下 Dockerfile 片段:
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
这个 Dockerfile 生成一个镜像像,使 docker run 在 /myvol 上创建一个新的挂载点,并将greeting文件复制到新创建的卷中。
5.12.2 关于指定卷的注意事项
请记住以下有关 Dockerfile 中的卷的事项。
- 基于 Windows 的容器上的卷:使用基于 Windows 的容器时,容器内卷的目的地必须是以下之一:
- 一个不存在的或空的目录
- C 以外的驱动器:
- 从 Dockerfile 中更改卷:如果任何构建步骤在声明卷后更改卷中的数据,这些更改将被丢弃。
- JSON 格式:列表被解析为 JSON 数组。您必须用双引号 (") 而不是单引号 (') 将单词括起来。
- 主机目录在容器运行时声明:主机目录(挂载点)本质上是依赖于主机的。这是为了保持镜像的可移植性,因为给定的主机目录不能保证在所有平台上都可用因此,您不能从 Dockerfile 中挂载主机目录。VOLUME 指令不支持指定主机目录参数。
PS E:\myproject> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS E:\myproject>
5.13 ENTRYPOINT指令
- ENTRYPOINT和CMD有共同点,那就是都为镜像所派生的容器指定首个执行进程。
- ENTRYPOINT 允许您配置将作为可执行文件运行的容器。
-
只有 Dockerfile 中的最后一条 ENTRYPOINT 指令才会起作用。
5.13.1 ENTRYPOINT的两种格式:
1)exec 格式:
ENTRYPOINT ["executable", "param1", "param2"]
2)shell 格式:
ENTRYPOINT command param1 param2
5.13.2 举例说明
例如,下面以默认内容启动 nginx,监听端口 80:
$ docker run -i -t --rm -p 80:80 nginx
docker run <image> 的命令行参数将附加在 exec 表单 ENTRYPOINT 中的所有元素之后,并将覆盖使用 CMD 指定的所有元素。这允许将参数传递到入口点,即 docker run <image> -d会将 -d 参数传递给入口点。
您可以使用 docker run --entrypoint 标志覆盖 ENTRYPOINT 指令。
shell 形式阻止使用任何 CMD 或运行命令行参数,但缺点是您的 ENTRYPOINT 将作为 /bin/sh -c 的子命令启动,它不传递信号。容器的 PID 1 - 并且不会接收 Unix 信号 - 因此您的可执行文件不会从 docker stop <container> 接收 SIGTERM。
5.13.3 举例说明
Exec形式的 ENTRYPOINT 示例
您可以使用 ENTRYPOINT 的 exec 形式设置相当稳定的默认命令和参数,然后使用任一形式的 CMD 设置更可能更改的其他默认值。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
当您运行容器时,您可以看到 top 是唯一的进程:
$ docker run -it --rm --name test top -H
top - 08:25:00 up 7:27, 0 users, load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
要进一步检查结果,您可以使用 docker exec:
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux
您可以使用 docker stop test 优雅地请求 top 关闭。
以下 Dockerfile 显示使用 ENTRYPOINT 在前台运行 Apache(即作为 PID 1):
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
如果您需要为单个可执行文件编写启动脚本,您可以使用 exec 和 gosu 命令确保最终可执行文件接收 Unix 信号:
#!/usr/bin/env bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
最后,如果您需要在关闭时做一些额外的清理(或与其他容器通信),或者协调多个可执行文件,您可能需要确保 ENTRYPOINT 脚本接收 Unix 信号,传递它们,然后做更多的工作:
#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too
# USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM
# start service in background here
/usr/sbin/apachectl start
echo "[hit enter key to exit] or run 'docker stop <container>'"
read
# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"
5.14 关于.dockerignore 文件
在 docker CLI 将上下文发送到 docker 守护程序之前,它会在上下文的根目录中查找名为 .dockerignore 的文件。如果此文件存在,CLI 会修改上下文以排除与其中的模式匹配的文件和目录。帮助以避免不必要地向守护进程发送大的或敏感的文件和目录,并可能使用 ADD 或 COPY 将它们添加到图像中。
CLI 将 .dockerignore 文件解释为以换行符分隔的模式列表,类似于 Unix shell 的文件 glob。为了匹配的目的,上下文的根被认为是工作目录和根目录。例如,模式 /foo/bar 和 foo/barboth 都排除了 PATH 的 foo 子目录或位于 URL 的 git 存储库的根目录中名为 bar 的文件或目录。两者都不排除任何其他内容。
如果 .dockerignore 文件中的一行在第 1 列中以# 开头,则该行将被视为注释并在被 CLI 解释之前被忽略。
这是一个示例 .dockerignore 文件:
# comment
*/temp*
*/*/temp*
temp?
此文件导致以下构建行为:
Rule | Behavior |
---|---|
# comment | 忽略. |
| 排除根的任何直接子目录中名称以 temp 开头的文件和目录。例如,排除普通文件 /somedir/temporary.txt,目录 /somedir/temp。 |
*/*/temp* | 从根以下两级的任何子目录中排除以 temp 开头的文件和目录。 |
temp? | 排除根目录中名称为 temp 一个字符扩展名的文件和目录。 |
六、后记
本篇对官方文档进行一次瘦身,对于一般的Image生成已经足够。但是对于更“大腕”级别的应用,恐怕依然需要参考官网。本文的另一个特色,是首先给出hello-world的制作过程,后边指令都可以在hello-world基础上实验。