Docker
能够从Dockerfile
中读取指令来自动构建镜像。Dockerfile
是一个文本文件,它包含了用户能够在命令调用以制做镜像的命令。用户可使用docker build
连续执行一些命令行指令来开启一个自动构建。前端
此文档描述了在Dockerfile
中可使用的命令。当你读完这个文档时,请参阅Dockfile
最佳实践获取进阶指南。nginx
docker build
命令从Dockerfile
和上下文构建镜像。构建上下文是特定路径或URL的文件集合。该路径是你本地文件系统的一个目录。URL是一个Git仓库地址。docker
上下文会被递归处理。因此,路径包含的任意字母路合URL包含的仓库及其子模块也会被处理。一下实例展现了一个使用当前目录做为上下文的build
命令:shell
$ docker build . Sending build context to Docker daemon 6.51 MB ...
构建由Docker daemon
执行, 而非cli。构建进程的第一件事是(递归的)发送上下文给守护进程(daemon)。在大多数状况下,最好以一个空目录下做为上下文发送给守护进程而且保持Dockerfile
在该目录下。只为构建Dockerfile
增长必须的文件。apache
CMD
指令有三种用法:json
CMD ["executable","param1","param2"]
(exec
形式, 这是首选形式)CMD ["param1","param2"]
(做为ENTRYPOINT
默认参数)CMD command param1 param2
(shell 形式)一个Dockerfile
里只能有一个CMD
指令。若是你有多个CMD
指令,只有 最后一个 生效。ubuntu
CMD
的主要目的是为运行容器提供默认值。 默认值能够包含一个可执行文件,也忽略可执行文件,在此状况下必须同时指定ENTRYPOINT
指令。数组
注: 若是CMD
用于为ENTRYPOINT
指令提供默认参数,CMD
和ENTRYPOINT
都应该使用json
数组格式。注:
exec
形式传递json
数组,意味着你必须使用双引号(")而不是单引号(')引用字符bash注: 与
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 <port> [<port>...]
EXPOSE
指令通知Docker
容器运行时监听指定的网络端口。EXPOSE
不会使容器端口对宿主机可访问。要那么作,你必须使用-p
标记来发布一系列端口或者-P
标记发布全部暴露端口。你能够暴露一个端口号并可使用另外一个端口对外发布。
要在宿主机系统上设置端口重定向,使用-P
标记。Docker
网络功能支持网络内建立网络而不须要暴露端口,详细信息请查看功能概述。
ADD
有两种形式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
(路径中包含空格须要这种形式)ADD
指令
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 ["/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
数组解析。你必须使用双引号(")而不是单引号(')将单词包起来。Dockerfile
内挂载一个主机目录。VOLUME
指令不支持指定一个主机目录
参数。你必须在容器建立或运行时指定挂载点。你可使用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
。
你能够为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
指令都定义了当启动一个容器时执行什么命令。描述他们如何一块儿工做的规则不多。
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 |