Docker学习——Dockerfile中的构建命令

Dockerfile的作用

相当于docker构建镜像的说明书,docker会根据Dockerfile中的构建命令一步一步的构建镜像,每一步构建指令都会产生一个镜像,在这个镜像的基础上在执行下一步构建指令,本博客不会涉及docker构建镜像的细节,只会介绍Dockerfile中的命令,运行dokcer build命令可以构建镜像,此时会将工作目录下的所有文件发送给docker守护进程(不管构建过程是不是需要)

Parser directives

Parser directives会影响docker后续处理行的方式,它不会在构建中填加额外的层,关于构建过程中层的概念,请查看:docker 的image是什么

使用格式:#directive=value

注意事项:以下使用方式会导致Parser directives无效

换行

# direc \tive=value

重复定义

# directive=value1# directive=value2FROM ImageName

出现在构建命令之后

FROM ImageName# directive=value

出现在非parser directive之后

# About my dockerfile# directive=valueFROM ImageName

不是合法的parser directive,目前docker支持的Parser directives只有escape,所以我在这里废话总结

# unknowndirective=value# knowndirective=value

escape

定义转义符号,目前支持两种形式

# escape=\ (backslash)

# escape=` (backtick)

如果在dockerfile中未指定,则默认为\,这会导致一些麻烦,因为\是windows的文件路径分割符,如果我们如下使用:

FROM microsoft/nanoserverCOPY testfile.txt c:\\RUN dir c:\

二、三行命令会被翻译成:

COPY testfile.txt c:\RUN dir c:

因此在windows上最好使用 ` 号

ENV

dockerfile中可以使用环境变量:$variable_name或是${variable_name},这两种格式被同等对待,但是括号可以处理空格的状态,

${variable_name}具有更多的用法:

${variable:-word}:如果variable值没有被指定,那么将使用word作为值

${variable:+word}:如果variable值被指定,那variable最终的值将是word,如果没有被指定,则默认为空字符串

环境变量也会被转义字符转义:

COPY \$foo /quux 等效于 COPY $foo /quux

环境变量的值可以通过ENV指令指定,ENV定义的值只有在ENV命令外才会生效,例如:

ENV abc=hello

ENV abc=bye def=$abcENV ghi=$abc

def的值将是hello(由于abc=hello在第一条ENV命令中,位于第二条ENV命令的def外),ghi的值将是bye

.dockerignore文件

前面说过docker build指令会将工作目录的所有文件发送给docker守护进程,在此之前,docker会检查工作目录下是否存在.dockerignore文件,通过.dockerignore文件,我们可以指定哪些文件不用发送给docker守护进程,.dockerignore文件以一行为单位,表示哪些文件不需要添加到docker守护进程,.dockerignore中的文件路径都是相对于工作目录而言,如果我们在一行的前面标记#,则改行被当作注释:

# comment*/temp*

*/*/temp*temp?

上述文件每行意义如下:

Docker学习——Dockerfile中的构建命令

.dockerignore也支持通配符,**表示匹配任意数目的目录,!表示排除在外的意思,例如:

dockerignore也支持通配符,**表示匹配任意数目的目录,!表示排除在外的意思,例如:

*.md!README.md

除了README.md以外的所有.md文件都会被排除在外,.dorckerignore文件的最后一行具有最高的优先级,它会覆盖之前的匹配,例如:

*.md

!README*.md

README-secret.md

即使README-secret.md匹配!README*.md,由于README-secret.md在最后一行,因此仍然会将README—secret.md移除,再如:

*.md

README-secret.md

!README*.md

README-secret.md不会被移除,因为!README*.md在最后一行

docker一定会见工作目录的Dockerfile文件发完docker守护进程,即使在.dockeringnore中声明Dockerfile

FROM

FROM指令具有三种格式:

FROM <image> [AS <name>]

FROM <image>[:<tag>] [AS <name>]

FROM <image>[@<digest>] [AS <name>]

要点:

FROM为接下来的构建指令指定基础镜像,一个有效的Dockerfile文件必须以FROM指令开头

除arg指令以外的构建指令都不能出现在FROM之前,Dockerfile文件可以出现多个FROM指令,此时一个Dockerfile可以构建多个image,并且输出这些image的ID,在执行FROM指令时,之前构建命令生成的状态会被清空,若arg指令位于FROM之外,FROM内部的指令不能引用arg,如果想使用,必须声明一遍:

ARG VERSION=latest

FROM busybox:$VERSION#声明过后,才能替换RUN指令为的$VERSION为latestARG VERSION

RUN echo $VERSION > image_version

FROM指令允许添加镜像的别名(指令中的[AS <name>]),在接下来的FROM指令或是COPY --from=<name|index>指令中可以引用这个镜像

tag与digest指镜像的版本信息,如果不指定,则默认为latest

RUN

RUN指令有两种形式:

#shell形式,命令会运行在shell脚本上,在linux上默认为/bin/sh -c,在windwos上默认为cmd /S /CRUN <command>#exec形式RUN["executable","param1","param2"]

要点:

RUN指令会在当前镜像的基础上执行命令

如果当前镜像不支持默认的shell,可以使用第二种形式,exec形式会解析成JSON数组,所以不能使用单引号

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

可以通过SHELL指令更改默认的shell环境

exec形式不会默认调用shell,类似于RUN [ "echo", "$HOME" ]的指令(需要环境变量解析)将不会得到解析,环境变量解析的工作由shell进行,不是docker

RUN命令的执行结果会进行缓存,以便加快二次构建的速度,可以使用docker build --no-cache,禁止使用缓存

CMD指令具有三种形式

#exec形式,推荐使用CMD ["executable","param1","param2"]#作为ENTRYPOINT指令的参数CMD ["param1","param2"]#shell形式CMD command param1 param2

要点:

dockerfile中只有一条CMD指令会生效,如果有多条,只有最后一条CMD指令生效

CMD指令主要为ENTRYPOINT提供默认值,此时CMD指令与ENTRYPOINT指令都必须使用exec格式

exec指令会解析成JSON格式,因此必须使用双引号

exec形式不会调用shell,这意味着CMD [ "echo", "$HOME" ]不会进行环境变量解析,如果想要使用,更改为CMD [ "sh", "-c", "echo $HOME" ],环境变量的解析均有shell负责,而不是docker

当镜像运行时才会运行CMD指令,而RUN是在构建阶段运行

如果使用shell形式,命令会运行在/bin/sh -c上

如果不想运行在shell上,就使用exec形式,但是要指明可执行文件的位置

如果在docker run命令上添加了参数,会覆盖CMD中对应的默认值

LABEL

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

要点:

LABEL命令增加元数据到image中(描述作用)

LABEL命令使用key-value模式

子image会继承父image的LABEL,同时可以覆盖其中的值

可以使用docker inspect命令查看Labels

MAINTAINER

MAINTAINER <name>

要点:

指定image的创作者

EXPOSE

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

要点:

EXPOSE让container在运行时监听特定的端口,可以指定端口运行tcp或是udp命令,默认情况下为tcp

EXPOSE 80/udp

EXPOSE命令不会真正发布端口,只是image构建者告诉container运行者应该如何映射端口

docker run指令的-p参数可以指定端口映射,以及对应的协议,此时才会发布端口

docker run -p 80:80/tcp -p 80:80/udp ...

ENV

#一行只能设置一个全局变量,双引号会被去除ENV <key> <value>#一行可以设置多个全局变量ENV <key>=<value> ...

要点:

ENV指令设置的key-value相当于全局变量

ENV指令设置的全局变量会持久化,可以使用docker inspect查看,可以使用docker run --env <key>=<value>更改其中的值

ADD

ADD [--chown=<user>:<group>] <src>... <dest>#当存在空格时,只能使用这种形式ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

要点:

[--chown=<user>:<group>]只在linux上有效,用于指定用户以及用户组

ADD指令将文件、文件夹、URL处的文件(夹)拷贝到<dest>指示的位置

如果<src>不是URL,则文件与文件夹的位置都是是相对于工作目录的,即相对目录

<src>可以使用通配符

ADD hom* /mydir/ # adds all files starting with "hom"ADD hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"

.<dest>只能是绝对路径或相对于WORDIR(稍后会介绍的指令)

ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/

ADD test /absoluteDir/ # adds "test" to /absoluteDir/

除非指定了--chown,否则所有新创建的文件以及目录的UID与GID均为0,--chown允许UID以及GID为数字或是字符串,也可以是两者的组合,/etc/passwd和/etc/group文件会被用来将字符串转换为对应的数字形式的UID以及GID,如果只指定了UID,GID默认取UID的值

如果URL需要验证登陆,ADD指令需要与RUN wget、RUN curl指令搭配使用(提供账号密码)

如果使用STDIN给出Dockerfile,可能会没有上下文,此时ADD指令只能通过URL获取资源

#没有上下文docker build -< Dockerfile#解压后的文件作文上下文docker build - < archive.tar.gz

如果<src>发生了改变,包括ADD在内的接下来的所有构建指令的缓存都会无效化

如果<src>是URL,而<dest>是文件夹,则文件名有URL中自动推算得知,在/下对应的文件名为foobar

如果<src>是文件夹,则文件夹的所有内容都会被拷贝到<dest>

如果<src>是压缩文件,则ADD会将其解压,URL对应的压缩文件不会被解压,一个文件会不会被解压不是由文件后缀决定,而是文件内容

将多个文件或是文件夹ADD到<dest>,<dest>必须是一个文件夹,并且以/结尾,否则会被认为是一个文件

如果<dest>不存在,则ADD指令会创建对应的文件路径

COPY

copy有两种形式:

COPY [--chown=<user>:<group>] <src>... <dest>#如果路径包括空格,则使用这种格式COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

要点:

copy将文件从<src>拷贝到<dest>,与ADD不同,它不支持从URL获取文件或文件夹,其余使用与ADD基本一致

copy指令可以使用--from=<name|index>引用之前构建阶段的镜像(FROM ..AS<name>创建的镜像),如果在之前的构建阶段找不到对应的镜像,则会尝试使用相同名字的镜像(从远程下载),也可以指定index,从而引用某个镜像(name指定)某个构建阶段(index指定)对应的镜像

ENTRYPOINT

ENTRYPOINT具有两种写法:

#exec形式ENTRYPOINT ["executable", "param1", "param2"]#shell形式ENTRYPOINT command param1 param2

要点:

运行docker run指令后会运行的指令

docker run指令后面的参数或是命令会附加到ENTRYPOINT上,会覆盖cmd对应的值,例如cmd指令指定了-name的值,如果使用docker run -name....,则容器最终的name为docker run指定的值

通过docker run --entrypoint会替换ENTRYPOINT的值

shell形式不接受cmd指定的参数,shell形式有一个缺点,就是启动的可执行程序不会接收信号,如果我们运行docker stop指令,可执行程序将不会接收到停止信号

如果定义了多条entrypoint指令,只有最后一条可以生效

cmd指令与ENTRYPOINT指令的关系

两者均制定了容器运行时执行的命令

Dockerfile应该至少指定CMD或是ENTRYPOINT中的一个

CMD可以为ENTRYPOINT指定参数值

CMD指定的值可以被命令中对应的参数值替换,两者的协作效果如下

 

Docker学习——Dockerfile中的构建命令

 

VOLUME

命令格式:

#定义匿名数据卷VOLUME ["/data"]

要点:

VOLUME指令用于创建一个挂载点,挂载点用于存储持久化数据,一般不建议将数据存储在Docker容器的可写层(原因我还没看),挂载点可以理解为就是一个用于存储数据的文件,只是这个文件由docker统一管理,当然,也可以将本地文件映射成挂载点,此时数据将写入到本地文件中

volume分为两种,一种是匿名卷,一种是实名卷,匿名数据卷就是未映射到本文将的卷,一般存储在/var/lib/docker/volumes中,当容器被删除时,对应的卷也会被删除,实名卷映射到了本地文件,当容器被删除时也不会被删除

实名卷只可以通过docker run -v指定,例如docker run -v /var/temp:/app

docker run指令会保留作为卷的文件之前的数据,例如:

FROM ubuntu

RUN mkdir /myvol

RUN echo "hello world" > /myvol/greeting

VOLUME /myvol

/myvol中将存在greeting文件,并且该文件保留hello world的字符

在windows上使用卷时,挂载文件必须存储在空文件夹或是一开始不存在的文件夹下,并且文件夹不能存储在C盘

使用实名卷需要注意,实名卷可能会影响容器的移植性,因为实名卷对应的文件结构受操作系统的影响,因此,我们不能在Dockerfile中指定使用实名卷(影响移植性),只能通过docker run -v指令

USER

格式:

USER <user>[:<group>] orUSER <UID>[:<GID>]

要点:

用于指定运行image、RUN、CMD、ENTRYPOINT的user和group,只有对应的user以及group可以运行image

如果user没有对应的group,则默认使用root

WORKDIR

命令格式:

WORKDIR /path/to/workdir

要点:

RUN、CMD、ENTRYPOINT、COPY、ADD指令可以使用,当这些指令未指出工作目录时,则默认使用WORKDIR指定的目录作为工作目录

如果WORKDIR指定的目录不存在,则会默认创建

WORKDIR指令可以使用多次,如果使用相对路径,会承接到之前的WORKDIR指定的路径中,例如:

WORKDIR /aWORKDIR bWORKDIR cRUN pwdRUN指令指定的文件为 /a/b/c/pwd

WORKDIR指令可以使用ENV指定的环境变量,例如:

ENV DIRPATH /path

WORKDIR $DIRPATH/b

RUN pwdRUN指令指定的文件为/path/b/pwd

ARG

命令格式:

ARG <name>[=<default value>]

要点:

ARG定义参数的值必须通过docker build --build-arg <varname>=<value> 指定

dockerfile文件可以使用多个ARG参数

FROM busybox

ARG user1

ARG buildno

...

一般不建议将账号密码等敏感信息作为ARG参数的值,因为可以通过docker history命令查看到

ARG指令可以指定参数的默认值

ARG指令只对位于自己下方的指令有效,镜像构建完毕后就会失效,如果想在多个镜像的构建过程中使用,则在多个镜像的构建过程中指定相同的ARG:

FROM busybox

ARG SETTINGS

RUN ./run/setup $SETTINGS

FROM busybox

ARG SETTINGS

RUN ./run/other $SETTINGS

ENV指令会覆盖ARG指令中相应的参数,例如:

1 FROM ubuntu2 ARG CONT_IMG_VER3 ENV CONT_IMG_VER v1.0.04 RUN echo $CONT_IMG_VER

运行指令docker build --build-arg CONT_IMG_VER=v2.0.1 .

CONT_IMG_VER的值将是v1.0.0

有些ARG参数是预定义的:

HTTP_PROXY

http_proxy

HTTPS_PROXY

https_proxy

FTP_PROXY

ftp_proxy

NO_PROXY

no_proxy

可以直接使用,这些参数的值不会被docker history输出,我们也可以自己覆盖这些参数

如果指定的ARG指令与之前构建阶段的不同,如果在其他指令中使用过ARG,则会出现缓存缺失,但如果它的值杯ENV指令覆盖,则不会出现:

1 FROM ubuntu2 ARG CONT_IMG_VER3 ENV CONT_IMG_VER hello4 RUN echo $CONT_IMG_VER

因为ARG指令的CONT_IMG_VER杯ENV覆盖了,所以不会出现缓存确实

ONBUILD

ONBUILD [INSTRUCTION]

要点:

指定运行镜像后紧接着执行的命令

这个指令是如何起作用的呢?在构建镜像时,builder会将ONBUILD指令的值添加到镜像的OnBuild中,可以通过运行docker inspect查看,当其他镜像使用这个镜像时,builder会查看OnBuild区域,并且按顺序执行它们,OnBuild区域的值不会被继承

STOPSIGNAL

STOPSIGNAL signal

要点:

使用这个指令允许用户自定义应用在收到 docker stop 时所发送的信号

HEALTHCHECK

#通过运行容器中的指令来判断容器是否健康,CMD可以是shell或是exec形式HEALTHCHECK [OPTIONS] CMD command#禁止所有父镜像的健康检查HEALTHCHECK NONE

要点:

这条指令用于检查容器的健康状况

如果容器指定了健康检查,会添加额外的health字段,这个字段一开始是starting,当健康检查通过以后,会更改为healthy,如果几次尝试都失败,则会设置为unhealthy

OPTIONS字段

#两次健康检查的间隔--interval=DURATION(默认为30s)健康检查命令运行超时时间,如果超过这个时间,本次健康检查视为失败--timeout=DURATION(默认为30s)#应用启动的初始化时间,在启动过程中的健康检查失效不会计入--start--period=DURATION(默认为0s)#当连续失败指定次数后,则将容器状态视为unhealthy--retries=N(默认为3次)

如果有多条HEALTHCHECK,只有最后一条才会起作用

这条命令执行完毕后,会输出一个数字:

#成功0:success#失败1:unhealthy#保留字段,没有意义2:reserved

SHELL

SHELL ["executable", "parameters"]

要点:

用于指定shell形式的命令运行的shell环境,linux默认的shell环境为["/bin/sh","-c"],windwos默认为

["cmd","/S","/C"]

 

 

Docker学习——Dockerfile中的构建命令

END

Docker学习——Dockerfile中的构建命令

碧茂课堂精彩课程推荐:

1.Cloudera数据分析课;

2.Spark和Hadoop开发员培训;

3.大数据机器学习之推荐系统;

4.Python数据分析与机器学习实战;

Docker学习——Dockerfile中的构建命令

详情请关注我们公众号:碧茂大数据-课程产品-碧茂课堂

现在注册互动得海量学币,大量精品课程免费送!