深入浅出Docker原理及实战(三)——制作Dockerfile


声明:这是我在大学毕业后进入第一家互联网公司学习的内容


深入浅出Docker原理及实战系列第三篇,我主要分享如何制作一个Dockerfile,以及基本命令格式。

Dockerfile简介

Docker可以通过阅读Dockerfile的指令来自动构建镜像。 Dockerfile是一个文本文件,其中包含用户可以在命令行上调用组装镜像的所有命令。使用docker build命令可以创建自动构建流程,该构建连续执行多个命令行指令。

制作Dockfile

Docker镜像由只读层组成,每个只读层代表一条Dockerfile指令。这些层是堆叠的,每个层都是上一层的变化的增量。下面是一个Dockerfile的内容:

FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

每条指令创建一层:

  • FROM:基于ubuntu:18.04的镜像创建一个只读层。
  • COPY:从Docker客户端的当前目录添加文件。
  • RUN: 使用make构建应用程序
  • CMD: 指定在容器中运行什么命令。

运行镜像并生成容器时,可以在基础层之上添加一个新的可写层(“容器层”)。对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入可写容器层。

BuildKit

从版本18.09开始,Docker支持由moby / buildkit 项目提供的用于执行构建的新后端。与旧的实现相比,BuildKit后端提供了许多好处。例如,BuildKit可以:

  • 检测并跳过执行未使用的构建阶段
  • 并行构建独立构建阶段
  • 两次构建之间仅增量传输构建上下文中的已更改文件
  • 在构建上下文中检测并跳过未使用的文件的传输
  • 使用具有许多新功能的外部Dockerfile实现
  • 避免与其他API产生副作用(中间图像和容器)
  • 优先考虑构建缓存以进行自动修剪
  • 要使用BuildKit后端,您需要DOCKER_BUILDKIT=1在CLI上设置环境变量 ,然后再调用docker build。

要了解基于BuildKit的构建可用的实验性Dockerfile语法,请参阅BuildKit存储库中的文档

注意!目前我还没有太了解这个功能,而且项目中dockerfile都没有引入,所以对于该功能不做深入了解。

Dockfile格式

文件的指令不区分大小写。但是,约定是大写,以便更轻松地将它们与参数区分开。

Docker按顺序在Dockerfile中运行指令,Dockerfile必须以“FROM”指令开头。这可能在解析器指令,注释和全局范围的ARG之后。

FROM指令指定要从上一层镜像中构建。

FROM只能前面有一个或多个ARG指令,这些指令声明Dockerfile中FROM行中使用的参数

Docker会将以#开头的行视为注释,除非该行是有效的解析器指令。

注释中不支持换行符。

Dockerfile指令

在Dockerfile中,每一条语句都是一个指令。

Dockerfile有以下指令列表:

部分指令
基础镜像信息FROM
镜像操作指令RUN、COPY、ADD、EXPOSE、WORKDIR、VOLUME等
容器启动时执行指令CMD、ENTRYPOINT

解析器指令

解析器指令是可选的,并且会影响Dockerfile处理后续行的方式。解析器指令不会在构建中添加图层,也不会显示为构建步骤。解析器指令以形式写为特殊类型的注释# directive=value。单个指令只能使用一次。

支持以下解析器指令:

  • syntax
  • escape

syntax仅当使用BuildKit后端时才启用此功能,我还没有用BuildKit,所以到在此不讲。

escape写法

# escape=\

该escape指令设置用来逃避的字符的字符 Dockerfile。如果未指定,则默认转义字符为\。

转义字符用于转义一行中的字符和转义换行符。这允许一条Dockerfile指令跨越多行。请注意,无论escape是否包含在Dockerfile,都不会在RUN命令中执行转义,除非在行末。

将转义符设置为`对Windows下尤其有用 ,其中目录路径分隔符为\。

一个简单的演示

如果不加escape指令

FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\

PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>

--------------------------------------------
加了escape指令后

# escape=`

FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\

PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
 ---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
 ---> Running in a2c157f842f5
 Volume in drive C has no label.
 Volume Serial Number is 7E6D-E0F7

 Directory of c:\

10/05/2016  05:04 PM             1,894 License.txt
10/05/2016  02:22 PM    <DIR>          Program Files
10/05/2016  02:14 PM    <DIR>          Program Files (x86)
10/28/2016  11:18 AM                62 testfile.txt
10/28/2016  11:20 AM    <DIR>          Users
10/28/2016  11:20 AM    <DIR>          Windows
           2 File(s)          1,956 bytes
           4 Dir(s)  21,259,096,064 bytes free
 ---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS C:\John>

Environment replacement

环境变量(与ENV的声明),也可以在特定指令作为变量用来被解释 Dockerfile。转义也可以通过将类变量的语法包括在语句中而得到处理。

Dockerfile用 v a r i a b l e n a m e 或 variable_name或 variablename{variable_name}表示环境变量。它们被等效地对待,并且大括号语法通常用于解决变量名没有空格的问题,例如${foo}_bar

该${variable_name}语法还支持一些标准bash 修饰符,如下所示:

  • ${variable:-word}表示如果variable设置,则结果将是该值。如果variable未设置,则为word结果。
  • ${variable:+word}指示如果variable设置了if,则将为word结果,否则结果为空字符串。

在所有情况下,word都可以是任何字符串,包括其他环境变量。

可通过在变量前添加\来进行转义:例如,$foo或${foo}将分别转换为 f o o 和 foo和 foo{foo}文字。

示例(解析的表示形式显示在之后#):

FROM busybox
ENV foo /bar
WORKDIR ${foo}   # WORKDIR /bar
ADD . $foo       # ADD . /bar
COPY \$foo /quux # COPY $foo /quux

在一条指令中,环境变量替换将对每个变量使用相同的值。

例如

ENV abc=hello
ENV abc=bye def=$abc hjk=$abc  #def=hello  hjk=hello
ENV ghi=$abc                   #ghi=bye

例子中def值为hello,而不是bye。但是, ghi=bye,因为它不是设置abc=bye这一指令的一部分。

FROM

格式:

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM指令初始化一个新的构建阶段,并为后续指令设置基本镜像。
因此,有效的Dockerfile必须以FROM指令开头。FROM的镜像可以是任何本地可以访问到仓库里的镜像-从公共存储库(docker hub)中拉出镜像特别容易启动。

其中注意以下5点:

  • ARG是先于FROM之前。
  • 一个Dockerfile可以出现多次FROM以创建多个镜像,也可以将一个构建阶段作为对另一个构建阶段的依赖。只需在每个新FROM指令之前记录一次提交输出的最后一个镜像ID。每个FROM指令清除由先前指令创建的任何状态。
  • 通过将AS 添加到FROM指令中,可以选择为新的构建阶段指定名称。该名称可以在后续版本FROM和 COPY --from=<name|index>说明中使用,以引用此阶段中构建的镜像。
  • 该tag或digest值是可选的。如果两个都不选择,那么缺省情况下构建器将采用latest标签。如果构建器找不到该tag值,则返回错误。
  • –platform在FROM引用多平台镜像的情况下,可选标志可用于指定镜像的平台。例如,linux/amd64, linux/arm64,或windows/amd64。默认情况下,使用构建请求的目标平台(即本机)。

了解ARG和FROM之间的交互方式

FROM支持由出现在第一个FROM之前的任何ARG指令声明的变量。

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}   #base:latest
CMD  /code/run-app

FROM extras:${CODE_VERSION} #base:latest
CMD  /code/run-extras

在FROM之前声明的ARG在构建阶段之外,因此不能在FROM之后的任何指令中使用ARG的变量,如果要使用在第一个FROM之前声明的ARG的值,请使用ARG指令再次声明变量即可。

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

RUN

RUN有2种形式:

RUN <command> (shell形式,命令在shell中运行,默认情况下在Linux是/bin/sh -c)
RUN ["executable", "param1", "param2"] (执行列表)

该RUN指令将在当前镜像上方的新层中执行任何命令,并提交结果。生成的提交镜像将用于下一步的Dockerfile。

分层RUN指令和生成提交符合Docker的核心概念,在这些概念上,提交很方便,并且可以从映像历史记录的任何位置创建容器,就像源代码控制一样。

exec形式可以避免破坏shell字符串,并使用不包含指定shell可执行文件的基本映像运行RUN命令(例如bash)。

在shell形式中,可以使用\将一条RUN指令继续到下一行。
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'


它们合在一起相当于以下一行:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

要使用'/ bin / sh'以外的其他shell,请使用exec形式传入所需的shell。例如:

RUN ["/bin/bash", "-c", "echo hello"]

注意

exec表单被解析为JSON数组,这意味着必须在单词周围使用双引号(")而非单引号(’)

RUN在一次构建期间,指令缓存不会自动失效。比如 RUN yum install -y vim 将在下一个构建中重用。RUN指令的缓存可以通过使用–no-cache 标志来使无效,例如docker build --no-cache。还可以由ADD和COPY指令来使无效。

CMD

CMD有三种形式:

CMD ["executable","param1","param2"](exec形式,这是首选形式)
CMD ["param1","param2"](作为ENTRYPOINT的默认参数)
CMD command (shell形式)

CMD主要目的是为执行中的容器提供默认值。这些默认值可以包含一个可执行文件,也可以忽略该可执行文件,在这种情况下,您还必须指定一条ENTRYPOINT 指令。

注意!!!一个Dockerfile只能一个CMD指令。如果出现多个CMD 则只有最后一个CMD才会生效。

与shell形式不同,exec形式不会调用命令shell。这意味着不会进行常规的shell处理。例如, CMD [ “echo”, " H O M E " ] 将 不 会 对 进 行 变 量 替 换 HOME" ]将不会对进行变量替换 HOME"]HOME。如果要进行shell处理,则可以使用shell形式或直接执行shell,例如:CMD [ “sh”, “-c”, “echo $HOME” ]

如果CMD使用的shell形式,则将在中执行 /bin/sh -c

FROM ubuntu
CMD echo "This is a test." | wc -

如果要在没有shell的情况下运行 ,则必须将命令表示为JSON数组,并提供可执行文件的完整路径。 此数组形式是的首选格式CMD。任何其他参数必须在数组中分别表示为字符串:

FROM ubuntu
CMD ["/usr/bin/wc","--help"]

如果希望容器每次都运行相同的可执行文件,则应考虑ENTRYPOINT与CMD结合使用

如果docker run指定了参数,则它们将覆盖中指定的CMD默认参数。

最后重申一点CMD和RUN的区别:RUN实际上运行命令并提交结果;CMD在构建时不执行任何操作,而是在镜像启动后执行命令。

LABEL

格式:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

该LABEL指令将元数据添加到镜像。一个LABEL是键值对。如果要在LABEL值中包含空格,请像在命令行分析中一样使用引号和反斜杠。

例子:

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

一个镜像可以有多个标签。您可以在一行上指定多个标签

LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

基础镜像(FROM中的镜像)中包含的标签会被后面的镜像继承。如果标签已经存在但具有不同的值,则最近应用的值将覆盖任何先前设置的值。

要查看镜像的标签,请使用docker image inspect命令。可以使用该–format选项仅显示标签。

docker image inspect --format='' myimage
{
  "com.example.vendor": "ACME Incorporated",
  "com.example.label-with-value": "foo",
  "version": "1.0",
  "description": "This text illustrates that label-values can span multiple lines.",
  "multi.label1": "value1",
  "multi.label2": "value2",
  "other": "value3"
}

EXPOSE

格式:

EXPOSE <port> [<port>/<protocol>...]

EXPOSE指令通知Docker容器在运行时监听指定的网络端口。您可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。

EXPOSE指令实际上并未发布端口。它充当构建映像的人员和运行容器的人员之间的一种文档类型,有关打算发布哪些端口的信息。要在运行容器时实际发布端口,请使用-p标记在docker run 发布和映射一个或多个端口,或者使用-P标记发布所有公开的端口并将它们映射到高阶端口。

默认情况下,EXPOSE指定的是TCP
EXPOSE 80

指定UDP
EXPOSE 80/udp

同时在TCP和UDP上公开
EXPOSE 80/tcp
EXPOSE 80/udp

在这种情况下,docker run -p,则该端口仅对TCP公开一次,对于UDP公开一次。
-p的端口在主机上使用临时的高阶主机端口,因此该端口对于TCP和UDP将是不同的。

无论EXPOSE设置如何,都可以在运行时使用该-p标志覆盖它们。例如
docker run -p 80:80/tcp -p 80:80/udp

ENV

格式:

ENV <key> <value>
ENV <key>=<value> 

ENV指令将环境变量设置为。此值将在构建阶段中所有后续指令的环境中使用,并且支持Environment replacement

ENV指令有两种形式。

第一种形式ENV 会将一个变量设置为一个值。第一个空格之后的整个字符串将被视为-包括空格字符。该值将为其他环境变量解释,因此如果不对引号字符进行转义,则将其删除。

第二种形式ENV = 允许一次设置多个变量。请注意,第二种形式在语法中使用等号(=),而第一种形式则不使用等号(=)。像命令行解析一样,引号和反斜杠可用于在值中包含空格。

例子:

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy
和
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

将在最终镜像中产生相同的效果

在运行容器时,使用设置的环境变量将保留。可以使用docker inspect查看ENV的值,并使用更改它们docker run --env =

注意!!!环境变量的持久性可能导致意外的副作用,如果要为单个命令设置值,只需要RUN = 即可让command里使用key

ADD

格式:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

ADD从目录或远程文件URL 中复制新文件, ,并将它们添加到镜像文件系统中的路径。

可以指定多个资源,但是如果它们是文件或目录,则将其路径解释为相对于构建上下文源的路径。每个都可能包含通配符,并且匹配将使用Go的 filepath.Match规则完成

除非选择–chown标志指定给定的用户名,组名或UID / GID组合以请求对所添加内容的特定所有权,否则所有新文件和目录的UID和GID均为0。–chown标志的格式允许用户名和组名字符串或直接整数UID和GID任意组合。提供不带组名的用户名或不带GID的UID将使用与GID相同的数字UID。如果提供了用户名或组名,则将使用容器的根文件系统 /etc/passwd和/etc/group文件将名称分别转换为整数UID或GID。

例子:

# 要添加所有以“hom”开头的文件:
ADD hom?.txt /mydir/

# 使用相对路径,并将“test.txt”添加到<WORKDIR>/relativeDir/
ADD test.txt relativeDir/

ADD --chown=55:mygroup files* /somedir/

ADD --chown=bin files* /somedir/

注意!!!如果的内容已更改,则遇到的第一个ADD指令将使Dockerfile中所有后续指令的缓存无效。这包括使高速缓存中的RUN指令无效。

COPY

格式:

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

COPY从复制新文件或目录,并将它们添加到容器的文件系统中,路径为。

可以指定多个资源,但是文件和目录的路径将被解释为相对于构建上下文的来源。

ENTRYPOINT

格式:

#EXEC的形式,这是优选的形式:
ENTRYPOINT ["executable", "param1", "param2"]
#Shell的形式
ENTRYPOINT command param1 param2

ENTRYPOINT可以作为可执行文件运行的容器的配置,一般配合CMD使用。

指定 ENTRYPOINT 为 exec 形式时,命令行上指定的参数会作为参数添加到 ENTRYPOINT 的参数列表中。

可以使用exec形式的ENTRYPOINT来设置相当稳定的默认命令和参数,然后使用两种形式的CMD来设置更可能被更改的其他默认值。

例如:

  • EXEC形式
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

docker build -t test:1.0 .

docker run -it --rm --name test test:1.0 

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 -c 
    
结果执行的是top -b 

docker run -it --rm --name test  test:1.0  -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 -b -H


结果是执行top -b -H
  • Shell形式

shell形式可以为ENTRYPOINT指定一个纯字符串,它将在中执行/bin/sh -c。这种形式将使用shell处理来替代shell环境变量,并且将忽略任何CMD或docker run命令行参数。为了确保能够正确docker stop发出任何长期运行的ENTRYPOINT可执行文件信号,需要以下exec启动它

FROM ubuntu
ENTRYPOINT exec top -b

docker build -t test:1.0 .

docker run -it --rm --name test test:1.0

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


如果忘记添加exec到开头的ENTRYPOINT中

FROM ubuntu
ENTRYPOINT top -b
CMD -H

docker build -t test:1.0 .

docker run -it --name test test:1.0 -c 

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
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.4  0.0   2612   604 pts/0    Ss+  09:16   0:00 /bin/sh -c top
root          6  0.0  0.0   5956  3324 pts/0    S+   09:16   0:00 top -b
    
从输出中可以看到top,指定ENTRYPOINT的不是PID 1,而且CMD中的参数-H无效,docker run带的-c参数也无效

VOLUME

格式:

VOLUME ["/data"]

VOLUME指令创建具有指定名称的挂载点,并将其标记为保存本机或其他容器的外部安装的存储卷。

例子:

docker run命令使用基础镜像内指定位置上存在的任何数据初始化新创建的卷

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

Dockerfile生成一个镜像,docker run会创建一个新的挂载点/myvol并将该greeting文件复制 到新创建的卷中。

如果删除容器里的greeting,然后重启容器,该文件仍然被删除

但是如果删除容器里的greeting并且再删除容器,再创建一个新的容器,该文件则存在,因为实际上又执行了dockerfile里的RUN echo “hello world” > /myvol/greeting 命令

注意点

  • 主机目录是在容器运行时声明的:主机目录(挂载点)本质上是依赖于主机的。这是为了保留镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此无法从Dockerfile内挂载主机目录。
  • 从Dockerfile中更改卷:如果在声明了卷之后有任何构建步骤更改了卷中的数据,则这些更改的指令将无效。

WORKDIR

格式:

WORKDIR /path/to/workdir

WORKDIR会设置工作目录并成为后续Dockerfile里的RUN, CMD, ENTRYPOINT, COPY 和 ADD 的当前路径,如果WORKDIR不存在,即使以后的Dockerfile指令中未使用它也将被创建。

例子:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

最后pwd的路径为 /a/b/c

WORKDIR能解析在此之前ENV设置的环境变量,而且只能使用Dockerfile中显示的环境变量

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

最后pwd的路径为 /path/$DIRNAME

ARG

格式:

ARG <name>[=<default value>]

该ARG指令定义了一个变量,用户可以在构建时docker build使用带有–build-arg = 标志的命令将其传递给构建器。如果用户指定了未在Dockerfile中定义的构建参数,则构建会输出警告。

例子:

FROM busybox
ARG user1=someuser
ARG buildno=1

如果ARG具有默认值,并且在构建时未传递任何值,那么构建器将使用默认值。即user1=someuser、buildno=1

一个ARG变量定义只能在Dockerfile构建的一个阶段内的那一行到结束范围内生效,简而言之就是ARG声明的那一刻到下一个FROM中间有效

FROM busybox
RUN echo SETTINGS
ARG SETTINGS
RUN ./run/setup $SETTINGS

FROM busybox
RUN echo SETTINGS
ARG SETTINGS
RUN ./run/other $SETTINGS

docker build --build-arg SETTINGS=hello .

执行结果为:
第一阶段的FROM过程
RUN echo SETTINGS
ARG hello
RUN ./run/setup hello

第二阶段的FROM过程
RUN echo SETTINGS
ARG hello
RUN ./run/other hello

ARG在它被定义的构建阶段范围进行。要在多个阶段使用ARG,每个阶段都必须包含ARG指令。

ARG或ENV会指定RUN指令可用的变量。使用ENV指令定义的环境变量 始终会覆盖ARG同名指令。

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER

docker build --build-arg CONT_IMG_VER=v2.0.1 .

在这种情况下,该RUN指令将使用v1.0.0而不是用户传递的ARG:v2.0.1。此行为类似于Shell脚本,其中局部作用域的变量从其定义的角度覆盖作为参数传递或从环境继承的变量。

使用上面的示例,但使用不同的ENV规范,可以在ARG和ENV指令之间创建更有用的交互:

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER  > /haha

docker build --build-arg CONT_IMG_VER=v2.0.1 -t test:1.5 .
docker run --name test --rm -itd test:1.5


docker exec -it test cat /haha
v2.0.1

与ARG指令不同,ENV值始终保留在生成的镜像中。使用此Dockerfile示例,CONT_IMG_VER它仍然保留在映像中,但其值将是指令第3行中的默认设置ENV,而ENV是ARG在docker bulid构建时传的参数v2.0.1 。

在此示例中,变量扩展技术可以从命令行传递参数,并利用ENV指令将其保留在最终映像中 。

相似指令对比

RUN and CMD

RUN是针对镜像,在镜像构建的时候运行命令并提交结果,一个Dockerfile可以有很多个RUN命令

CMD是针对容器,在镜像构建的时候不执行任何操作,而是在镜像启动后执行命令,一个Dockerfile即使有很多个CMD命令,也只生效最后一个(原因在下面解释)

COPY and ADD

COPY能够将构建命令所在的主机本地的文件或目录,复制到镜像文件系统。

ADD指令不仅能够将构建命令所在的主机本地的文件或目录,而且能够将远程URL所对应的文件或目录,作为资源复制到镜像文件系统。

满足同等功能的情况下,推荐使用COPY指令。ADD指令更擅长读取本地tar文件并解压缩。而且由于因为路径、资源更改而导致缓存失效的情况出现,我们更应该尽量少用ADD,而改用使用RUN wget或RUN curl替代,并且把这一步骤尽可能地放在Dockerfile的后面位置。

ENTRYPOINT and CMD组合执行的命令

  • Dockerfile应至少指定CMD或ENTRYPOINT命令之一。
  • 使用容器作为可执行文件时应定义ENTRYPOINT
  • CMD应该用作ENTRYPOINT在容器中定义命令或执行临时命令的默认参数的方式。
  • 当使用其他参数运行容器时,CMD 将被覆盖。

ENTRYPOINT应该被当做docker的可执行程序,CMD应该被当做ENTRYPOINT的默认参数。

组合列表:以df命令为例

CMD/ENTRYPOINTNo ENTRYPOINTENTRYPOINT du -HENTRYPOINT [“du”, “-H”]
No CMD不允许/bin/sh -c du -Hdu -H
CMD [“df”, “-h”]df -h/bin/sh -c du -Hdu -H df -h
CMD [“-h”, “-T”]-h -T/bin/sh -c du -Hdu -H -h -T
CMD df -h/bin/sh -c df -h/bin/sh -c du -Hdu -H /bin/sh -c df -h
  • 如果 ENTRYPOINT 使用了 shell 模式,CMD 指令会被忽略。
  • 如果 ENTRYPOINT 使用了 exec 模式,CMD 指定的内容被追加为 ENTRYPOINT 指定命令的参数。

结论:一起使用ENTRYPOINT和CMD时,务必同时使用两个指令的exec形式,这一点很重要。尝试使用shell形式,或者混合匹配shell和exec形式几乎不会给您想要的结果。

总结

Dockerfile基本命令和写法格式都讲完了,下期主要讲Dockerfile的构建底层实现原理。

参考文献

Dockerfile官方文档参考


版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值