Docker可以从Dockerfile中读取指令自动构建镜像。Dockerfile是一个文本文件,其中包含用户可以在命令行调用以构建映像的命令。用户可以使用dockerbuild连续执行一些命令行命令来启动自动构建。本文档描述了可在Dockerfile中使用的命令。阅读完本文档后,请参阅Dockfile最佳实践以获得高级指导。使用dockerbuild命令从Dockerfile和上下文构建图像。构建上下文是位于特定路径或URL的文件集合。该路径是本地文件系统上的一个目录。URL是一个Git仓库地址。上下文是递归处理的。因此,路径中包含的任何字母路径URL都包含存储库,并且还将处理其子模块。以下示例显示了使用当前目录作为上下文的构建命令:$dockerbuild.SendingbuildcontexttoDockerdaemon6.51MB...构建由Docker守护进程执行,而不是cli。构建过程做的第一件事是(递归地)将上下文发送到守护进程。在大多数情况下,最好发送带有空目录作为上下文的守护进程,并将Dockerfile保存在该目录中。仅添加构建Dockerfile所需的文件。CMDCMD指令有3种用法:CMD["executable","param1","param2"](exec形式,这是首选形式)CMD["param1","param2"](作为ENTRYPOINT的默认参数)CMDcommandparam1param2(shell格式)一个Dockerfile中只能有一个CMD命令。如果您有多个CMD命令,则只有最后一个命令生效。CMD的主要目的是为运行的容器提供默认值。默认可以包含可执行文件,或忽略可执行文件,在这种情况下还必须指定ENTRYPOINT指令。注意:如果使用CMD为ENTRYPOINT命令提供默认参数,那么CMD和ENTRYPOINT都应该使用json数组格式。注意:exec形式传递的是json数组,意味着必须使用双引号(")而不是单引号(')来引用字符注意:与shell形式不同,exec形式不会像.这样调用命令行shell。这意味着有通常的外壳处理。例如,CMD["echo","$HOME"]不会对$HOME进行变量替换。如果要使用shell处理,可以使用shell形式或者直接执行一个shell,例如:["sh","-c","echo$HOME"]。当使用exec形式,直接执行一个shell时,这里是shell形式,执行环境变量扩展的是shell,不是docker。当使用shell或exec格式时,CMD命令设置图像运行时要执行的命令。如果您使用CMD的shell形式,将作为/bin/sh-c运行:FROMubuntuCMDecho"Thisisatest."|wc-如果您想在不使用shell的情况下运行表示为使用可执行文件的完整路径的JSON数组。数组形式是CMD的首选格式。任何单独的参数都必须表示为数组的单独字符串。FROMubuntuCMD["/usr/bin/wc","--help"]如果您的系统容器每次都运行相同的可执行文件,您应该考虑将ENTRYPOINT与CMD结合使用。如果用户为dockerrun指定参数,那么它们将覆盖CMD中指定的默认参数。注意:不要混淆RUN和CMD。RUN实际运行命令并提交结果;CMD在构建时不执行任何操作,只是指定图像将执行的命令。EXPOSEEXPOSE[...]EXPOSE命令告诉Docker容器在运行时监听指定的网络端口。EXPOSE不会使容器端口可供主机访问。为此,您必须使用-p标志发布一系列端口或使用-P标志发布所有暴露的端口。可以暴露一个端口号,用另一个端口对外发布。要在主机系统上设置端口重定向,请使用-P标志。Docker网络功能支持在不暴露端口的情况下在网络内创建网络。详情请参考功能概览。ADDADD有两种形式:ADD...ADD["",...""](如果路径包含空格则需要这种形式)ADD指令ENTRYPOINTENTRYPOINT有两种形式:ENTRYPOINT["executable","param1","param2"](exec形式,首选)ENTRYPOINT命令param1param2(shell形式)ENTRYPOINT允许您配置将作为可执行程序运行的容器。例如下面的命令会默认启动一个nginx监控80端口:dockerrun-i-t--rm-p80:80nginxdockerrun的命令行参数会附加到exec中ENTRYPOINT的所有元素上形式,并覆盖所有用CMD指定的元素。这允许将参数传递给条目,例如,dockerrun-d会将-d参数传递给条目。您可以使用dockerrun--entrypoint标志覆盖ENTRYPOINT执行。shell形式阻止使用任何CMD或运行命令行参数,但有一个缺点,您的ENTRYPOINT将作为/bin/sh-c的子命令启动,并且无法传递信号。这意味着可执行文件不是容器ID为1的进程-并且不会接收Unix信号-因此您的可执行文件不会从dockerstop接收SIGTERM。只有Dockerfile的最后一个ENTRYPOINT指令才会生效。VOLUMEVOLUME["/data"]VOLUME指令创建一个具有指定名称的挂载点,并将其标记为从主机或其他容器外部挂载的卷。该值可以是JSON数组、VOLUME["/var/log"]或带有多个参数的纯字符串,例如:VOLUME/var/log或VOLUME/var/log/var/db。有关Docker客户端安装指令的更多信息/示例,请移至文档以通过卷共享目录。dockerrun命令使用基础映像中指定位置中存在的任意数据初始化新创建的卷。例如,考虑以下Dockerfile片段:FROMubuntuRUNmkdir/myvolRUNecho"helloworld">/myvol/greetingVOLUME/myvol这个Dockerfile的结果是dockerrun将创建一个新的挂载点/myvol并将gretting文件复制到新创建的卷。关于指定卷的注意事项请注意Dockerfile中有关卷的以下内容。基于Windows容器的卷:使用基于Windows的容器时,容器内卷的目标位置必须是以下之一:C以外的驱动器上的不存在或空目录:从Dockerfile中更改卷:如果任何构建步骤在volume声明后修改了数据,这些修改将被丢弃。JSON格式:列表将被解析为JSON数组。必须在单词周围使用双引号(")而不是单引号(')。宿主目录在容器运行时声明:宿主目录(挂载点)本质上是宿主相对的。这是为了确保镜像的可靠性。可移植性,因为指定的宿主机目录不能保证在所有宿主机上都可用,所以不能在Dockerfile中挂载宿主机目录,VOLUME指令不支持指定宿主机目录参数,必须在容器创建或运行时指定Mountpoint.Exec形式的ENTRYPOINT实例您可以使用ENTRYPOINT的exec形式来设置相当稳定的默认命令和参数,然后使用任一形式的CMD来设置更可能被修改的其他默认值。FROMubuntuENTRYPOINT["top","-b"]CMD["-c"]但是运行容器时,只能看到top进程:$dockerrun-it--rm--nametesttop-Htop-08:25:00到7:27,0个用户,平均负载:0.00、0.01、0.05线程:总共1个,1个正在运行,0个正在睡眠,0个已停止,0个僵尸%Cpu(s):0.1us,0.1sy,0.0ni,99.7id,0.0wa,0.0hi,0.0si,0.0stKiBMem:总计2056668,已使用1616832,免费439836,99352buffersKiB交换:总计1441840,已使用0,免费1441840。1324440cachedMemPIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND1root2001974423362080R0.00.10:00.04top要进一步检查结果,可以使用dockerexec:$dockerexec-ittestpsauxUSERPID%CPU%MEMVSZRSSTTYSTATSTARTTIMECOMMANDroot12.60.1197522352?Ss+08:240:00top-b-Hroot70.00.1155722164?R+08:250:00psaux你可以使用dockerstoptest让top正常退出。以下Dockerfile演示了使用ENTRYPOINT在前面运行Apache(例如,使用PID1)。FROMdebian:stableRUNapt-getupdate&&apt-getinstall-y--force-yesapache2EXPOSE80443VOLUME["/var/www","/var/log/apache2","/etc/apache2"]ENTRYPOINT["/usr/sbin/apache2ctl","-D","FOREGROUND"]如果需要为单个可执行程序编写启动脚本,可以使用exec和gosu命令来保证最终的可执行程序能够接收到Unix信号。#!/usr/bin/envbashset-eif["$1"='postgres'];然后chown-Rpostgres"$PGDATA"if[-z"$(ls-A"$PGDATA")"];然后gosupostgresinitdbfiexecgosupostgres"$@"fiexec"$@"最后,如果你需要在退出时做一些额外的清理(或与其他容器通信),或者合作执行多个可执行文件,你可能需要确保ENTRYPOINT该脚本接受Unix信号,传递它们并做更多工作:#!/bin/sh#注意:我使用sh编写了这个,因此它也可以在busybox容器中工作#如果您还需要进行手动清理,请使用陷阱服务停止后,#或者需要在一个容器中启动多个服务trap"echoTRAPedsignal"HUPINTQUITTERM#startserviceinbackgroundhere/usr/sbin/apachectlstartecho"[hitenterkeytoexit]orrun'dockerstop'"read#stopserviceandcleanuphereecho"stoppingapache"/usr/sbin/apachectlstopecho"exited$0"如果你用dockerrun-it-p80:80--nametestapache运行镜像,然后你可以用dockerexec,或者dockertop检查容器进程,你可以通过脚本停止Apache。$dockerexec-ittestpsauxUSERPID%CPU%MEMVSZRSSTTYSTATSTARTTIMECOMMANDroot10.10.04448692?Ss+00:420:00/bin/sh/run.sh123cmdcmd2root190.00.2713044440?Ss00:420:00/usr/sbin/apache2-kstartwww-data200.20.23604686004?sl00:420:00/usr/sbin/apache2-kstartwww-data210.20.23604686000?sl00:420:00/usr/sbin/apache2-kstartroot810.00.1155722140?R+00:440:00psaux$dockertoptestPIDUSERCOMMAND10035root{run.sh}/bin/sh/run.sh123cmdcmd210054root/usr/sbin/apache2-kstart1005533/usr/sbin/apache2-kstart1005633/usr/sbin/apache2-kstart$/usr/bin/timedockerstoptesttestreal0m0.27suser0m0.03ssys0m0.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指定一个纯文本字符串,这将be/bin/sh-以c的形式运行,这种形式将使用shell来处理shell而不是shell环境变量,并且会忽略CMD或dockerrun命令的任何命令行参数。为确保dockerstop正确地向任何长时间运行的ENTRYPOINT可执行文件发出信号,您需要记住使用exec启动它:FROMubuntuENTRYPOINTexectop-b当您启动映像时,您将看到PID为1的进程:$dockerrun-it--rm--nametesttopMem:1704520Kused,352148Kfree,0Kshrd,0Kbuff,140368121167873KcachedCPU:5%usr0%sys0%nic94%idle0%io0%irq0%sirqLoadaverage:0.080.030.052/986PIDPPIDUSERSTATVSZ%VSZ%CPUCOMMAND10rootR31640%0%top-b执行dockerstop时会完全退出:$/usr/bin/timedockerstoptesttestreal0m0.20suser0m0.02ssys0m0.04s如果您忘记在ENTRYPOINT的开头添加exec:FROMubuntuENTRYPOINTtop-bCMD--ignored-param1您可以启动它(下一步为它命名):$dockerrun-it--nametesttop--ignored-param2Mem:1704184Kused,352484Kfree,0Kshrd,0Kbuff,140621524238337KcachedCPU:9%usr2%sys0%nic88%idle0%io0%irq0%sirqLoad21average:0.0.0.052/1017PIDPPID用户统计VSZ%VSZ%CPU命令10rootS31680%0%/bin/sh-ctop-bcmdcmd271rootR31640%0%top-b可以看到top的输出,ENTRYPOINT没有指定为PID1如果接下来执行dockerstoptest,容器不会完全退出-超时后top命令将发送一个SIGKILL。$dockerexec-ittestpsauxPIDUSERCOMMAND1root/bin/sh-ctop-bcmdcmd27roottop-b8rootpsaux$/usr/bin/timedockerstoptesttestreal0m10.19suser0m0.04ssys0m0.03了解CMD和ENTRYPOINT如何交互CMD和ENTRYPOINT指令都定义了启动容器时要执行的命令。很少有规则描述它们如何协同工作。Dockerfile至少应该指定一个CMD或ENTRYPOINT命令。ENTRYPOINT应该在容器生成可执行程序时定义。CMD应该用作定义ENTRYPOINT的默认参数或在容器内执行临时命令的方法。当使用交互式参数运行容器时,CMD将被覆盖。下表显示了对不同ENTRYPOINT/CMD组合执行的命令:NoENTRYPOINTENTRYPOINTexec_entryp1_entryPOINT[“exec_entry”,“p1_entry”]NoCMDerror,notallowed/bin/sh-cexec_entryp1_entryexec_entryp1_entryCMD[”]p1_entryCMD[”]pc1_cmdexec_cmdp1_cmd/bin/sh-cexec_entryp1_entryexec_entryp1_entryexec_cmdp1_cmdCMD[“p1_cmd”,“p2_cmd”]p1_cmdp2_cmd/bin/sh-cexec_entryp1_entryexec_entryp1_entryp1_cmdp2_cmdCMDexec_cmdp1_cmd/shbin/shbin-_cmd/shbin/shcexec_entryp1_entryexec_entryp1_entry/bin/sh-cexec_cmdp1_cmd