前言

在有了Docker相关的基础知识后,就可以开始指定出行计划了(Dockerfile),计划里将记录我们的出发点(FROM),需要购买的物品(COPY/ADD),在预定的景点拍照(RUN)。当然本身Dockerfile其实也可以在被发明的那个时候称为Recipe的,只是这个单词在计算机领域被用到太广泛了,没有Dockerfile来得特立独行。下面就来简单说说Dockerfile.

Dockerfile简介

Dockerfile是用来构建镜像的文本文件,其中记录了如何构建镜像的命令。按照最新的文档,目前可以在Dockerfile中使用FROM, RUN, CMD, LABEL, MAINTAINER, EXPOSE, ENV, ADD, COPY, ENTRYPOINT, VOLUME, USER, WORKDIR, ARG, ONBUILD, STOPSIGNAL, HEALTHCHECK, SHELL共18个指令。其中MAINTAINER已经不推荐使用,可以使用LABEL替代。

下面就是一个IIS镜像的例子。这里需要注意的是ENTRYPOINT指令中的使用的ServiceMonitor.exe, 这是专为ENTRYPOINT设计的应用,目前已在Github上开源

# escape=`
FROM microsoft/windowsservercore:1709

RUN powershell -Command `
    Add-WindowsFeature Web-Server; `
    Invoke-WebRequest -UseBasicParsing -Uri "https://dotnetbinaries.blob.core.windows.net/servicemonitor/2.0.1.3/ServiceMonitor.exe" -OutFile "C:\ServiceMonitor.exe"

EXPOSE 80

ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]

其中FROM指令用于指定在构建新镜像时将使用的基础镜像, 通用用途的Windows容器一般可以选择则 microsoft/windowsservercore 或者 microsoft/nanoserver。如果镜像是用来运行ASP.NET或者ASP.NET Core应用,那么可以使用 microsoft/aspnet 或者 microsoft/aspnetcore 镜像。

接下来的RUN指令则是执行PowerShell的Invoke-WebRequest命令下载ServiceMonitor.exe应用。EXPOSE则是用来通知Docker当容器运行时监听的端口。

命令格式

在重点介绍ENTRYPOINT指令前,先来谈谈Dockerfile中的命令格式,直白的表达就是什么时候用中括号([])。在Docker的官方文档说明中,使用中括号的被称为exec格式,反之则称为shell格式。两者的区别可以用以下两个Dockerfile来对比区分。一句话的解释就是使用exec格式的话,将按照exec中指定的命令运行。使用shell格式化的话,将使用默认命令行执行。

exec form

FROM microsoft/windowservercore:1709

RUN ["powershell", "New-Item", "c:/test"]

shell form

FROM microsoft/windowservercore:1709

RUN powershell New-Item c:/test

clipboard.png

ENTRYPOINT 指令

ENTRYPOINT指令的作用是将容器配置成可执行程序,或者说设置默认执行的程序,而CMD指令只指定了程序默认执行时使用的参数。在Docker的官方文档中有一张非常好的表格罗列各种情况。现摘录如下。

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

这张表格里混合了好几种情况,可以按照纵向列的方式来阅读。当没有指定ENTRYPOINT时,以CMD中指定的命令和参数为准(第一列)。当ENTRYPOINT和CMD同时存在,且ENTRYPOINT以shell格式存在时,以ENTRYPOINT中指定的命令为准(第二列)。当ENTRYPOINT和CMD同时存在,且都以exec格式存在,则合并ENTRYPOINT和CMD中指定的命令和参数。

PowerShell及转义符号

PowerShell 首次出现在2006年,它的出现统一了Windows下命令行的设计方式,让管理员和开发者可以使用动词-名词的方式来使用开发命令行工具。PowerShell本身对标Linux的Python,作为胶水语言来简化操作系统的管理。当然Python目前比PowerShell还是领先一些,Web应用开发的能力也是PowerShell所不具备的,这和PowerShell做为管理员工具存在的初始定位有关。

那么在Dockerfile中如何使用PowerShell呢?这里以一个Dockerfile为例进行说明。首先,通过escape注释语句设置本Dockerfile中的转义符号为倒角符(`)。接下来通过SHELL指令设置容器内默认命令行为PowerShell。然后通过RUN指令运行PowerShell命令。这里需要注意的是,示例中运行的已经不是单行命令,而是脚本块。因此命令之间是通过分号(;)进行分隔的。而为了避免一行命令过长,则使用倒角符截断命令参数。

PowerShell如果要展开来讨论,做为开发语言也有很多要讨论的地方,在Dockerfile中一般只是用来执行现成的命令。同时需要注意的在Windows Server, version 1709中PowerShell已从nanoserver镜像中移除。

#escape=`

FROM microsoft/windowsservercore

SHELL [ "powershell", "-command" ]

RUN $ErrorActionPreference = 'Stop'; `
    $ProgressPreference = 'SilentlyContinue'; `
    $null = New-Item -Path c:\apps -Type Directory

RUN Invoke-WebRequest -Uri https://download.sysinternals.com/files/SysinternalsSuite.zip `
                      -UseBasicParsing -OutFile c:\apps\SysinternalsSuite.zip `
                      -Proxy http://192.168.0.124:1080; `
    Expand-Archive -Path C:\apps\SysinternalsSuite.zip -DestinationPath C:\apps\sysinternals\ -Force; `
    Remove-Item -Path c:\apps\SysinternalsSuite.zip

ENTRYPOINT [ "powershell" ]

总结

文章标题中使用了出行计划来形容Dockerfile之于容器制作的作用。技术上来说Dockerfile的存在使得开发人员可以在代码库中管理最终交付物的生成过程,比如如果应用要发布到Windows Server, version 1803中,只需更改FROM中使用的基础镜像即可。


追寻云的痕迹
301 声望6 粉丝