内容简介:此文档描述了在
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
文件到新创建的卷。
关于 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
指令都定义了当启动一个容器时执行什么命令。描述他们如何一起工作的规则很少。
-
Dockerfile
至少应该指定一个CMD
或ENTRYPOINT
命令。 -
当容器做一个可执行程序时,
ENTRYPOINT
应该被定义。 -
CMD
应该被用作一种给ENTRYPOINT
定义默认参数的方式,或在容器中执行ad-hoc
命令的方式。 -
当运行容器时是用了交互参数时,
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参考》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [Vue 2.x Todo 教程练习参考答案] 标为完成练习参考答案
- [Vue 2.x Todo 教程练习参考答案] 添加todo练习参考答案
- MyOIDC v1.1.0 发布,基于 OIDC 协议的参考实现,根据各类库提供实现参考
- [Vue 2.x Todo 教程练习参考答案] 入门仪式_Hello_Vue练习参考答案
- 微服务参考架构实现
- 领域驱动设计参考
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。