Dockerfile参考

栏目: 编程工具 · 发布时间: 7年前

内容简介:此文档描述了在

Docker 可以从 Dockerfile 中读取指令来自动构建镜像。 Dockerfile 是一个文本文件,它包含了用户可以在命令调用以制作镜像的命令。用户可以使用 docker build 连续执行一些命令行指令来开启一个自动构建。

此文档描述了在 Dockerfile 中可以使用的命令。当你读完这个文档时,请参阅 Dockfile 最佳实践 获取进阶指南。

使用

docker build 命令从 Dockerfile 和上下文构建镜像。构建上下文是特定路径或URL的文件集合。该路径是你本地文件系统的一个目录。URL是一个Git仓库地址。

上下文会被递归处理。所以,路径包含的任意字母路合URL包含的仓库及其子模块也会被处理。一下实例展示了一个使用当前目录作为上下文的 build 命令:

$ docker build .
Sending build context to Docker daemon  6.51 MB
...

构建由 Docker daemon 执行, 而非cli。构建进程的第一件事是(递归的)发送上下文给守护进程(daemon)。在大多数情况下,最好以一个空目录下作为上下文发送给守护进程并且保持 Dockerfile 在该目录下。只为构建 Dockerfile 增加必须的文件。

CMD

CMD 指令有三种用法:

  • CMD ["executable","param1","param2"] ( exec 形式, 这是首选形式)
  • CMD ["param1","param2"] (作为 ENTRYPOINT 默认参数)
  • CMD command param1 param2 (shell 形式)

一个 Dockerfile 里只能有一个 CMD 指令。如果你有多个 CMD 指令,只有 最后一个 生效。

CMD 的主要目的是为运行容器提供默认值。 默认值可以包含一个可执行文件,也忽略可执行文件,在此情况下必须同时指定 ENTRYPOINT 指令。

注: 如果 CMD 用于为 ENTRYPOINT 指令提供默认参数, CMD 和 ENTRYPOINT 都应该使用 json 数组格式。

注: exec 形式传递 json 数组,意味着你必须使用双引号(")而不是单引号(')引用字符

注:与 shell 形式不同, exec 形式不会像,那样调用命令行 shell 。这意味着没有通常的 shell 处理。例如, CMD [ "echo", "$HOME" ] 将不会对 $HOME 做变量替换。如果你想使用 shell 处理可使用 shell 形式或直接执行一个 shell ,例如: ["sh", "-c", "echo $HOME"] 。当使用 exec 形式并且直接执行一个 shell ,在这种情况下 shell 形式,执行环境变量扩展的是 shell ,而不是 docker 。

当使用 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 中指定的默认参数。

注:不要混淆 RUN 和 CMD 。 RUN 实际上运行命令并提交结果; CMD 在构建时什么都不执行,只是指定镜像将要执行的命令。

EXPOSE

EXPOSE <port> [<port>...]

EXPOSE 指令通知 Docker 容器运行时监听指定的网络端口。 EXPOSE 不会使容器端口对宿主机可访问。要那么做,你必须使用 -p 标记来发布一系列端口或者 -P 标记发布所有暴露端口。你可以暴露一个端口号并可以使用另一个端口对外发布。

要在宿主机系统上设置端口重定向, 使用 -P 标记 。 Docker 网络功能支持网络内创建网络而不需要暴露端口,详细信息请 查看功能概述 。

ADD

ADD 有两种形式:

ADD <src>... <dest>
ADD ["<src>",... "<dest>"]

ADD 指令

ENTRYPOINT

ENTRYPOINT 有2中形式:

  • ENTRYPOINT ["executable", "param1", "param2"] ( exec 形式, 首选)
  • ENTRYPOINT command param1 param2 ( shell 形式)

ENTRYPOINT 允许你配置一个将作为可执行程序运行的容器。

例如,以下命令将启动一个 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 或者 run 的命令行参数被使用,但是有个弊端,你的 ENTRYPOINT 将被作为 /bin/sh -c 的一个子命令启动,不能传递信号。这意味着可执行程序不是容器ID为1的进程 - 并且不会接受Unix信号 - 所以你的可执行程序不会接受来自 docker stop <container> 的 SIGTERM 。

只有 Dockerfile 最后一个 ENTRYPOINT 指令会生效。

VOLUME

VOLUME ["/data"]

VOLUME 指令创建一个具有指定名称的挂载点并且将其标记作为从宿主机或者其他容器外部挂载卷。值可以是一个 JSON 数组, VOLUME ["/var/log"] ,或者有多参数的纯字符串,比如: 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 并且拷贝 gretting 文件到新创建的卷。

指定volumes的注意事项

关于 Dockerfile 中的 volumes ,请注意以下事项。

  • 基于 Windows 容器的 Volumes : 当使用基于 Windows 的容器,容器内 volume 的目标位置必须是以下之一:

    C
    
  • 从 Dockerfile 内更改卷: 如果任何构建步骤在 volume 声明之后修改了数据,这些修改将会被丢弃。
  • JSON 格式: 列表将会被作为一个 JSON 数组解析。你必须使用双引号(")而不是单引号(')将单词包起来。
  • 主机目录在容器运行时声明: 主机目录(挂载点)本质上是与主机相关的。这是为了保证镜像的可移植性。因为一个指定的主机目录不能保证在所有的主机上可用。因此,你不能在 Dockerfile 内挂载一个主机目录。 VOLUME 指令不支持指定一个 主机目录 参数。你必须在容器创建或运行时指定挂载点。

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"

如果你使用 docker run -it -p 80:80 --name test apache 运行该镜像,然后你可以使用 docker exec 检查容器进程,或者 docker top ,并且可以通过脚本停止 Apache 。

$ docker exec -it test ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux
$ docker top test
PID                 USER                COMMAND
10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054               root                /usr/sbin/apache2 -k start
10055               33                  /usr/sbin/apache2 -k start
10056               33                  /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real    0m 0.27s
user    0m 0.03s
sys    0m 0.03s
注:你可以使用 --entrypoint 覆盖 ENTRYPOINT 配置,但是这只会将二进制设置为 exec ( sh -c 不会被使用)。

注: exec 形式被解析为 JSON 数组,意味着你必须使用双引号(")包裹单词而不是单引号(')。

注:不像 shell 形式, exec 形式并不会调用 shell 命令。这意味着不会做普通的 shell 处理。例如, ENTRIPOIN ["echo", "$HOME"] 将不能对 $HOME 做变量置换。如果你既想 shell 处理又想使用 shell 形式或直接执行一 shell ,例如: ENTRYPOINT ["sh", "-c", "echo $HOME"] 。当使用 exec 形式和直接执行 shell 时,在 shell 形式这种情况下,是 shell 做的环境变量扩展,而不是 docker 。

Shell形式ENTRYPOINT实例

你可以为 ENTRYPOINT 指定一个纯文本的字符串,它会以 /bin/sh -c 的形式运行。这种形式将使用 shell 处理 shell 代替 shell 环境变量,并且将忽略任何 CMD 或者 docker run 命令的命令行参数。为了确保 docker stop 能够正常发出信号给任何长时间运行的 ENTRYPOINT 可执行文件,您需要记住使用 exec 启动它:

FROM ubuntu
ENTRYPOINT exec top -b

当你启动镜像,你会看到 PID 为1的进程:

$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
Load average: 0.08 0.03 0.05 2/98 6
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     R     3164   0%   0% top -b

它将会在执行 docker stop 时彻底退出:

$ /usr/bin/time docker stop test
test
real    0m 0.20s
user    0m 0.02s
sys    0m 0.04s

如果你忘记了在 ENTRYPOINT 开头增加 exec :

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1

你可以启动它(为了下一步给它指定名称):

$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU:   9% usr   2% sys   0% nic  88% idle   0% io   0% irq   0% sirq
Load average: 0.01 0.02 0.05 2/101 7
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     S     3168   0%   0% /bin/sh -c top -b cmd cmd2
    7     1 root     R     3164   0%   0% top -b

你可以看到 top 的输出, ENTRYPOINT 指定的不是PID 1。

如果你接下来执行 docker stop test ,容器不会被彻底退出 - 超时以后 top 命令会被发送一个 SIGKILL 。

$ docker exec -it test ps aux
PID   USER     COMMAND
    1 root     /bin/sh -c top -b cmd cmd2
    7 root     top -b
    8 root     ps aux
$ /usr/bin/time docker stop test
test
real    0m 10.19s
user    0m 0.04s
sys    0m 0.03s

理解CMD和ENTRYPOINT如何交互

CMD 和 ENTRYPOINT 指令都定义了当启动一个容器时执行什么命令。描述他们如何一起工作的规则很少。

  1. Dockerfile 至少应该指定一个 CMD 或 ENTRYPOINT 命令。
  2. 当容器做一个可执行程序时, ENTRYPOINT 应该被定义。
  3. CMD 应该被用作一种给 ENTRYPOINT 定义默认参数的方式,或在容器中执行 ad-hoc 命令的方式。
  4. 当运行容器时是用了交互参数时, CMD 将被会被覆盖。

下表显示了对不同 ENTRYPOINT / CMD 组合执行的命令:

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

以上所述就是小编给大家介绍的《Dockerfile参考》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

大转换

大转换

尼古拉斯·卡尔 / 闫鲜宁、张付国 / 中信 / 2016-2 / 49

1、我们这个时代最清醒的思考者之一尼古拉斯·卡尔继《浅薄》《玻璃笼子》之后又一重磅力作。 2、在这部跨越历史、经济和技术领域的著作中,作者从廉价的电力运营方式对社会变革的深刻影响延伸到互联网对我们生活的这个世界的重构性影响。 3、《快公司》《金融时报》《华尔街日报》联袂推荐 简介 早在2003年,尼古拉斯·卡尔先生发表在《哈佛商业评论》上的一篇文章——IT Doesn't ......一起来看看 《大转换》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码