本节内容很多,我们希望大家能够了解和使用好Dockerfile。
关于dockerfile
虽然我们可以通过docker commit命令来手动创建镜像,但是通过Dockerfile文件,可以帮助我们自动创建镜像,并且能够自定义创建过程。本质上,Dockerfile就是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它简化了从头到尾的构建流程并极大的简化了部署工作。使用dockerfile构建镜像有以下好处:
-
像编程一样构建镜像,支持分层构建以及缓存;
-
可以快速而精确地重新创建镜像以便于维护和升级;
-
便于持续集成;
-
可以在任何地方快速构建镜像
Dockerfile指令
我们需要了解一些基本的Dockerfile 指令,Dockerfile 指令为 Docker 引擎提供了创建容器映像所需的步骤。这些指令按顺序逐一执行。以下是有关一些基本 Dockerfile 指令的详细信息。
1.FROM
FROM 指令用于设置在新映像创建过程期间将使用的容器映像。
格式:FROM
示例:
FROM nginx
FROM microsoft/dotnet:2.1-aspnetcore-runtime
2.RUN
RUN 指令指定将要运行并捕获到新容器映像中的命令。 这些命令包括安装软件、创建文件和目录,以及创建环境配置等。
格式:
RUN ["", "", ""]
RUN
示例:
RUN apt-get update
RUN mkdir -p /usr/src/redis
RUN apt-get update && apt-get install -y libgdiplus
RUN ["apt-get","install","-y","nginx"]
注意:每一个指令都会创建一层,并构成新的镜像。当运行多个指令时,会产生一些非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。因此,在很多情况下,我们可以合并指令并运行,例如:RUN apt-get update && apt-get install -y libgdiplus。在命令过多时,一定要注意格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。使用换行符时,可能会遇到一些问题,具体可以参阅下节的转义字符。
3.COPY
COPY 指令将文件和目录复制到容器的文件系统。文件和目录需位于相对于 Dockerfile 的路径中。
格式:
COPY
如果源或目标包含空格,请将路径括在方括号和双引号中。
COPY ["", ""]
示例:
COPY . .
COPY nginx.conf /etc/nginx/nginx.conf
COPY . /usr/share/nginx/html
COPY hom* /mydir/
4.ADD
ADD 指令与 COPY 指令非常类似,但它包含更多功能。除了将文件从主机复制到容器映像,ADD 指令还可以使用 URL 规范从远程位置复制文件。
格式:
ADD<source> <destination>
示例:
ADD https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe /temp/python-3.5.1.exe
此示例会将 Python for Windows下载到容器映像的 c: emp 目录。
5.WORKDIR
WORKDIR 指令用于为其他 Dockerfile 指令(如 RUN、CMD)设置一个工作目录,并且还设置用于运行容器映像实例的工作目录。
格式:
WORKDIR
示例:
WORKDIR /app
6.CMD
CMD指令用于设置部署容器映像的实例时要运行的默认命令。例如,如果该容器将承载 NGINX Web 服务器,则 CMD 可能包括用于启动Web服务器的指令,如 nginx.exe。 如果 Dockerfile 中指定了多个CMD 指令,只会计算最后一个指令。
格式:
CMD ["<executable", "
CMD
示例:
CMD ["c:\Apache24\bin\httpd.exe", "-w"]
CMD c:\Apache24\bin\httpd.exe -w
7.ENTRYPOINT
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。每个 Dockerfile 中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效。
格式:
ENTRYPOINT ["", ""]
示例:
ENTRYPOINT ["dotnet", "Magicodes.Admin.Web.Host.dll"]
8.ENV
ENV命令用于设置环境变量。这些变量以”key=value”的形式存在,并可以在容器内被脚本或者程序调用。这个机制给在容器中运行应用带来了极大的便利。
格式:
ENV==...
示例:
ENV VERSION=1.0 DEBUG=on
NAME="Magicodes"
9.EXPOSE
EXPOSE用来指定端口,使容器内的应用可以通过端口和外界交互。
格式:
EXPOSE
示例:
EXPOSE 80
说了这么多,我们可以用下图来一言以蔽之:
转义字符
在许多情况下,Dockerfile 指令需要跨多个行;这可通过转义字符完成。 默认 Dockerfile 转义字符是反斜杠 。 由于反斜杠在 Windows 中也是一个文件路径分隔符,这可能导致出现问题。
以下示例显示使用默认转义字符跨多个行的单个 RUN 指令。
FROM microsoft/windowsservercore
RUN powershell.exe -Command
$ErrorActionPreference = 'Stop';
wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:python-3.5.1.exe ;
Start-Process c:python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ;
Remove-Item c:python-3.5.1.exe -Force
要修改转义字符,必须在 Dockerfile 最开始的行上放置一个转义分析程序指令。 如以下示例所示:
# escape=`
FROM microsoft/windowsservercore
RUN powershell.exe -Command `
$ErrorActionPreference = 'Stop'; `
wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:python-3.5.1.exe ; `
Start-Process c:python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ; `
Remove-Item c:python-3.5.1.exe -Force
注意,只有两个值可用作转义字符: 和 ` 。
优化
篇幅有限,我们这里只进行简单讲解,后续结合实际案例再进行细说。但是有几点值得注意的是:
-
不能忽视dockerfile的优化,通常情况下,我们可以忽略那些细小的优化,但是我们需要知道优化的原理,为什么要优化
-
不能为了优化而优化。镜像的构建过程视业务情况情况不同,指令就有多到少的区别,在很多情况下,我们先要以满足业务目标为准,而不是镜像层数。如果需要减少镜像的层数,我们一定要选择合适的基础镜像,或者创建符合我们需要的基础镜像。
下面是一些优化的准则:
-
选择合适的基础镜像
这点相对最为重要。为什么这么说,我们结合现实社会也可以看到,在大部分情况下,一个人一生的成就更多的是看出身。很多情况下,基因和出身决定了你的高度和终点,这点拿到技术层面来说,也是有很大道理的,因此我们需要选择合适的父母——一个合适的镜像。
一个合适的基础镜像是指能满足运行应用所需要的最小的镜像,理论上是能用小的就不要用大的,能用轻量的就不要用重量级的,能用性能好的就不要用性能差的。这里有时候还需要考虑那些能够减少我们构建层数的基础镜像。
-
优化指令顺序
Docker会缓存Dockerfile中尚未更改的所有步骤,但是,如果更改任何指令,将重做其后的所有步骤。也就是指令3有变动,那么4、5、6就会重做。因此,我们需要将最不可能产生更改的指令放在前面,按照这个顺序来编写dockerfile指令。这样,在构建过程中,就可以节省很多时间。比如,我们可以把WORKDIR、ENV等命令放前面,COPY、ADD放后面。
-
合并指令
前面其实我们提到过这点,甚至还特地讲到了转义字符,其实主要是为此服务。前面我们说到了,每一个指令都会创建一层,并构成新的镜像。当运行多个指令时,会产生一些非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。因此,在很多情况下,我们可以合并指令并运行,例如:RUN apt-get update && apt-get install -y libgdiplus。在命令过多时,一定要注意格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。
-
删除多余文件和清理没用的中间结果
这点很易于理解,通常来讲,体积更小,部署更快!因此在构建过程中,我们需要清理那些最终不需要的代码或文件。比如说,临时文件、源代码、缓存等等。
-
使用 .dockerignore
.dockerignore文件用于忽略那些镜像构建时非必须的文件,这些文件可以是开发文档、日志、其他无用的文件。例如:
说了这么多,其实我们更多的还是需要根据命令的实际执行情况来进行调整。