Dockerfile 指令
CMD
CMD指令用于指定一个容器启动时要运行的命令。这有点儿类似于RUN指令,只是RUN指令是指定镜像被构建时要运行的命令,而CMD是指定容器被启动时要运行的命令。这和使用docker run命令启动容器时指定要运行的命令非常类似
$ sudo docker run -i -t jamtur01/static_web /bin/true
可以认为上述命令和在Dockerfile中使用如下的CMD 指令是等效的。
CMD ["/bin/true"]
选项和命令一起构成数组的形式
CMD ["/bin/bash", "-1"]
需要注意的是,要运行的命令是存放在一个数组结构中的。这将告诉Docker按指定的原 样来运行该命令。当然也可以不使用数组形式指定CMD指令,这时候Docker会在指定的 命令前加上/bin/sh -c这在执行该命令的时候可能会导致意料之外的行为,所以Docker推荐一直使用以数组语法来设置要执行的命令。
最后,还需牢记,使用docker run命令可以覆盖CMD指令。如果我们在Dockerfile 里指定了CMD指令,而同时在docker run命令行中也指定了要运行的命令,命令行中指 定的命令会覆盖Dockerfile中的CMD指令。
CMD [ "/bin/bash"]
基于上述Dockerfile构建的镜像在生成容器启动时会有如下的表现
$ sudo docker run -t -i jamturOl/test
root0e643e6218589:/#
在docker run命令的末尾并未指定要运行什么命令。实际上,Docker使用了 CMD指令中指定的命令。
如果指定:
$ sudo docker run -i -t jamturOl/test /bin/ps
PID TTY TIME CMD
1 ? 00:00:00 ps
$
可以看到,在这里指定了想要运行的命令/bin/ps,该命令会列出所有正在运行的进程。容器并没有启动shell,而是通过命令行参数覆盖了CMD指令中指 定的命令,容器运行后列出了正在运行的进程的列表,之后停止了容器。
在Dockerfile中只能指定一条CMD指令。如果指定了多条CMD指令,也只有最后一条 CMD指令会被使用。如果想在启动容器时运行多个进程或者多条命令,可以考虑使用类似 Supervisor这样的服务管理工具。
ENTRYPOINT
ENTRYPOINT指令与CMD指令非常类似,也很容易和CMD指令弄混。ENTRYPOINT指令提供的命令则不容易在启动容器时被覆盖。 实际上,docker run命令行中指定的任何参数都会被当做参数再次传递给ENTRYPOINT指令中指定的命令。
ENTRYPOINT ["/usr/sbin/nginx"]
类似于CMD指令也可以在该指令中通过数组的方式为命令指定相应的参数
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
重构镜像,并将ENTRYPOINT设置为ENTRYPOINT ["/usr/sbin/nginx"]
$ sudo docker run -t -i image_name -g "daemon off;"
指定了-g "daemon off;”参数,这个参数会传递给用ENTRYPOINT指定的命令,
在这里该命令为 /usr/sbin/nginx -g "daemon off;"。该命令会以前台运行的方式启动Nginx守护进程,此时这个容器就会作为一台Web服务器来运行。
组合使用ENTRYPOINT和CMD指令
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
当启动一个容器时,任何在命令行中指定的参数都会被传递给Nginx守护进 程。比如,我们可以指定-g "daemon off";参数让Nginx守护进程以前台方式运行。如果在启动容器时不指定任何参数,则在CMD指令中指定的-h参数会被传递给Nginx守护进程,即Nginx服务器会以/usr/sbin/nginx-h的方式启动,该命令用来显示Nginx的帮助信息。这使我们可以构建一个镜像,该镜像既可以运行一个默认的命令,同时它也支持通过 docker run命令行为该命令指定可覆盖的选项或者标志。
如果确实需要,你也可以在运行时通过docker run的--entrypoint标志覆盖 ENTRYPOINT 指令。
WORKDIR
WORKDIR指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录, ENTRYPOINT和域CMD指定的程序会在这个目录下执行。可以使用该指令为Dockerfile中后续的一系列指令设置工作目录,也可以为最终的容器设置工作目录。
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT [ "rackup"]
这里将工作目录切换为/opt/webapp/db后运行了 bundle install命令,之后又将工作目录设置/opt/webapp,最后设置了ENTRYPOINT指令来启动rackup命令。可以通过-w标志在运行时覆盖工作目录
$ sudo docker run -ti -w /var/log ubuntu pwd
/var/log
ENV
ENV指令用来在镜像构建过程中设置环境变量
ENV RVM_PATH /home/rvm/
这个新的环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样
RUN gem install unicorn
在其他指令中直接使用这些环境变量。
ENV TARGET_DIR /opt/app
WORKDIR $TARGET_DIR
在这里设定了一个新的环境变量TARGET_DIR,并在WORKDIR中使用了它的值。因此实际上WORKDIR指令的值会被设为/opt/app。
如果需要,可以通过在环境变量前加上一个反斜线来进行转义。
这些环境变量也会被持久保存到从我们的镜像创建的任何容器中。如果我们在使用ENV RVM_PATH /home/rvm/指令构建的容器中运行env命令
[email protected]:~# env
...
RVM_ PATH= /home/rvm/
...
也可以使用docker run命令行的-e标志来传递环境变量。这些变量将只会在运行时有效
$ sudo docker run -ti -e "WEB_PORT=8080" ubuntu env
HOME=/
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=792bl71c5e9f
TERM=xterm
WEB_PORT=8080
USER
USER指令用来指定该镜像会以什么样的用户去运行
USER nginx
基于该镜像启动的容器会以nginx用户的身份来运行。我们可以指定用户名或UID以及组或GID,甚至是两者的组合。
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
也可以在docker run命令中通过-u选项来覆盖该指令指定的值。
如果不指定用户,默认为root
VOLUME
VOLUME指令用来向基于镜像创建的容器添加卷。一个卷是可以存在于一个或者多个容器内的特定的目录,这个目录可以绕过联合文件系统,并提供如下共享数据或者对数据进行持久化的功能。
卷可以在容器间共享和重用。
一个容器可以不是必须和其他容器共享卷。
对卷的修改是立时生效的。
对卷的修改不会对更新镜像产生影响。
卷会一直存在直到没有任何容器再使用它。
卷功能让我们可以将数据(如源代码)、数据库或者其他内容添加到镜像中而不是将这些内容提交到镜像中,并且允许我们在多个容器间共享这些内容。我们可以利用此功能来测试容器和内部的应用程序代码,管理日志,或者处理容器内部的数据库。
VOLUME ["/opt/project"]
这条指令将会为基于此镜像创建的任何容器创建一个名为/opt/project的挂载点。也可以通过指定数组的方式指定多个卷
VOLUME ["/opt/project", "/data"]
至于挂载到了主机的哪个地方,可以通过docker inspect进行查看。
ADD
ADD指令用来将构建环境下的文件和目录复制到镜像中。比如,在安装一个应用程序时。 ADD指令需要源文件位置和目的文件位置两个参数,
ADD software.lic /opt/application/software.lic
这里的ADD指令将会将构建目录下的software.lic文件复制到镜像中的 /opt/application/software.lic。指向源文件的位置参数可以是一个URL或者构建上下文或环境中文件名或者目录。不能对构建目录或者上下文之外的文件进行ADD操作。
在ADD文件时,Docker通过目的地址参数末尾的字符来判断文件源是目录还是文件。 如果目标地址以/结尾,那么Docker就认为源位置指向的是一个目录。如果目的地址不是以/结尾,那么Docker就 认为源位置指向的是文件。文件源也可以使用URL的格式
ADD http://wordpress.org/latest.zip /root/wordpress.zip
最后值得一提的是,ADD在处理本地归档文件(tar archive)时还有一些小魔法。如果 将一个归档文件(合法的归档文件包括gzip、bzip2、xz)指定为源文件,Docker会自动将 .归档文件解开(unpack)
ADD latest.tar.gz /var/www/wordpress/
这条命令会将归档文件latest.tar.gz解开到/var/www/wordpress/目录下。 Docker解开归档文件的行为和使用带-x选项的tar命令一样:该指令执行后的输出是原目的目录己经存在的内容加上归档文件中的内容。如果目的位置的目录下己经存在了和归档文件同名的文件或者目录,那么目的位置中的文件或者目录不会被覆盖。
最后,如果目的位置不存在的话,Docker将会为我们创建这个全路径,包括路径中的 任何目录。新创建的文件和目录的模式为0755,并且UID和GID都是0。
ADD指令会使得构建缓存变得无效,这一点也非常重要。如果通过ADD指令向镜像添加一 个文件或者目录,那么这将使Dockerfile中的后续指令都不能继续使用之前的构建缓存
COPY
COPY指令非常类似于ADD,它们根本的不同是COPY只关心在构建上下文中复制本地文件,而不会去做文件提取(extraction)和解压(decompression)的工作。
COPY conf.d/ /etc/apache2/
这条指令将会把本地conf.d目录中的文件复制到/etc/apache2/目录中。
文件源路径必须是一个与当前构建环境相对的文件或者目录,本地文件都放到和 Dockerfile同一个目录下。不能复制该目录之外的任何文件,因为构建环境将会上传到 Docker守护进程,而复制是在Docker守护进程中进行的。任何位于构建环境之外的东西都 是不可用的。COPY指令的目的位置则必须是容器内部的一个绝对路径。
任何由该指令创建的文件或者目录的UID和GID都会设置为0。
如果源路径是一个目录,那么这个目录将整个被复制到容器中,包括文件系统元数据; 如果源文件为任何类型的文件,则该文件会随同元数据一起被复制。在这个例子里,源路径 以/结尾,所以Docker会认为它是目录,并将它复制到目的目录中。
如果目的位置不存在,Docker将会自动创建所有需要的目录结构,就像mkdir -p命令那样。
ONBUILD
ONBUILD指令能为镜像添加触发器(trigger)。当一个镜像被用做其他镜像的基础镜像时(比如你的镜像需要从某未准备好的位置添加源代码,或者你需要执行特定于构建镜像的 环境的构建脚本),该镜像中的触发器将会被执行。触发器会在构建过程中插入新指令,我们可以认为这些指令是紧跟在FROM之后指定的。
ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make
上面的代码将会在创建的镜像中加入ONBUILD触发器,ONBUILD指令可以在镜像上运 行docker inspect命令来查看
$ sudo docker inspect 508efa4e4bf8
"OnBuild":[
"ADD . /app/src",
"RUN cd /app/src/ && make"
为Apache2镜像构建一个全新的Dockerfile,该镜像名为jamturOl/apache2
FROM ubuntu:14.04
MAINTAINER James Turnbull "[email protected]"
RUN apt-get update
RUN apt-get install -y apache2
ENV APACHE—RUN一USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ONBUILD ADD . /var/www/
EXPOSE 80
ENTRYPOINT ["/usr/sbin/apache2"]
CMD ["-D", "FOREGROUND"]
构建该镜像
$ sudo docker build -t="jamturOl/apache2".
Step 7 : ONBUILD ADD . /var/www/
> Running in 0ell7f6ea4ba
—> a79983575b86 Successfully built a79983575b86
在新构建的镜像中包含一条ONBUILD指令,该指令会使用ADD指令将构建环境所在的目录下的内容全部添加到镜像中的/var/www/目录下。我们可以轻而易举地将这个Dockerfile作为一个通用的Web应用程序的模板,可以基于这个模板来构建Web应用程序。这种机制使我每次都会将本地源代码添加到镜像,也支持为不同的应用程序进行一些特定的配置或者设置构建信息。这时可以将jamtur01/apache2当做一个镜像模板。
ONBUILD触发器会按照在父镜像中指定的顺序执行,并且只能被继承一次(也就是说只能在子镜像中执行,而不会在孙子镜像中执行)。如果我们再基于jamturOl/webapp构建一个镜像,则新镜像是jamtur01/apache2的孙子镜像,因此在该镜像的构建过程中ONBUILD触发器是不会被执行的。
这里有好几条指令是不能用在ONBUILD指令中的,包括FROM、MAINTAINER和ONBUILD 本身。之所以这么规定是为了防止在Dockerfile构建过程中产生递归调用的问题。