Dockerfile

本文详细介绍了Dockerfile的使用,包括指令解释器、环境替换、文件管理及各种指令的用法,如FROM、ENV、COPY、RUN、CMD、EXPOSE等,旨在帮助读者深入理解Dockerfile的构建过程和提升Docker镜像构建效率。
摘要由CSDN通过智能技术生成

1. 关于docker build

docker build可以基于Dockerfile和context打包出一个镜像,其中context是一系列在PATHURL中指定的位置中的文件(context是递归的,包含子目录下的文件,build时会将context中的全部内容传递给docker daemon)。其中PATH是指本地文件系统,URL则是git仓库的地址。比如在当前目录下创建镜像:docker build .,此时context就是当前目录.。build的运行是由docker daemon操作的,并不是CLI(Command-Lin Interface)。通常build时强烈建议创建一个空目录,然后将Dockerfile文件放入,最后只将build需要用到的文件放到该目录下。注:千万不要用root或者/作为PATH,docker daemon会将整个目录下的文件读取。为了尽可能提高Docker的build性能,和git一样可以在上述目录中添加.dockerignore文件排除一些不要的文件,比如正常打包时都会排除掉Dockerfile:

./Dockerfile*

 默认build时会基于context的根目录下Dockerfile文件打包(如果不存在会报错),当然可以通过-f DockerfilePath的方式指定任意位置的Dockerfile位置,但后面的context必须在Dockerfile所在位置的目录或者父目录(指定为其他目录会报错,比如指定为~/k8s/),比如:

# ~/docker/test1/Dockerfil指定Dockerfile位置,~/docker/指定context一定是Dockerfile所在目录或父目录
docker build -f ~/docker/test1/Dockerfile ~/docker/

 此外还可以指定如果镜像构建成功存放的仓库和标签(即repository和tag),如构建时打上阿里云的仓库标签:docker build -t registry.cn-shanghai.aliyuncs.com/hhu/redis:4.0-alpine3.9 .。注:这里可以不断追加-t 标签的方式打上多个仓库仓库标签,比如:docker build -t registry.cn-shanghai.aliyuncs.com/hhu/redis:4.0-alpine3.9 -t test1/redis:4.0-alpine3.9 -t test2/redis:4.0-alpine3.9 .,上述栗子一下就会出现3个标签的镜像,但它们的ID是相同的,仅仅是标签不同。

 和大部分的应用类似,docker daemon会在运行Dockerfile中的指令时会先验证文件的可行性,比如语法错误就会返回error。Dockerfile中的每条指令都是独立运行的(比如RUN cd /tmp并不会影响下一条指定的执行),并不会创建新的镜像。不论何时,docker都会尽可能重复应用缓存的中间镜像以便加快build的过程,所以有时在build时可能会在控制台看到Using cache的字样。可以使用的缓存镜像只有在本地具备父链才能使用(即缓存的镜像先前被创建过或者整个镜像链都使用docker load加载过)。如果在build时希望使用某个特定的镜像缓存可以使用--cache-from选项,注:使用--cache-from时不需要具备本地父链,可能会从其他的注册中心拉下来。

注:docker run如果不显式的指定-it进行交互的话,此时创建的容器会直接在后台运行,如果没有前台进行,docker会让容器自动停止并移除,所以很多时候为了让一些只有操作系统的镜像创建的容器不被docker自动移除,我们往往会在创建镜像dockerfile的时候加上一个CMD

# 创建 centOS 空系统工具经常加这个CMD
CMD /usr/sbin/init
# 创建 Alpine 空系统工具经常加这个CMD
CMD init

他们目的都是在系统中挂一个前台进程,这样docker就不会认为该容器处于空闲状态去移除它,目的都是一样的,当然我们也可以人为造出一些死循环来达到这种目的。

2. 语法

 Dockerfile的语法为INSTRUCTION arguments,不区分大小写,但为了区分指令和参数,约定指令全部大写、参数尽量小写。docker会按序执行Dockerfile中的指令,Dockerfile文件必须以FROM指令开头(除指令解释器外),它指定了当前构建镜像的基础镜像。通常Dockerfile中也允许注释,注释行以#开头,但指令解释器除外,比如``。

2.1 指令解释器

指令解释器是可选的,它会影响下面指令的处理方式,但不会在增加层layer,也不会作为构建的步骤展示出来。docker一旦处理了注释、空行或生成器指令,就不会再看是它否分析器指令了,就算下面还有指令解释器,docker也会将其视为注释(都是以#开头),所以Dockerfile中只要有指令解释器,请务必尽可能的靠前声明(约定解释器为小写),应该Dockerfile的第一行。指令解释器不支持行继续的行为(Linux中表现为\),同时指令解释器后应该空一行。总结一下,应该遵循如下的规则:

  1. #开头,指令解释器小写,如果有位于Dockerfile的第一行;
  2. 不支持行继续操作行为,即\
  3. 解释器后应留一个空白行;

下面是一些反例:

# 反例1:使用了行继续 \
# direc \
tive=value

# 反例2:只会识别第一个,value2不应该出现会被当成注释
# directive=value1
# directive=value2

FROM ImageName

# 反例3:指令解释器非首行
FROM ImageName
# directive=value

注:指令解释器允许使用空格符,比如下面的几种方式都是一样有效的:

#directive=value
# directive =value
#	directive= value
# directive = value

 Dockerfile支持的指令解释器有:syntaxescape。其中syntax仅支持下一代构建工具BuildKit,这里暂不进行捣鼓,直接看escape,它定义的是Dockerfile中的转义字符,有2种定义方式:

# escape=\

# 或者

# escape=`

如果不指定默认转义字符为\。转义字符既用于转义一行中的字符,也用于转义换行符。这使得Dockerfile指令可以跨越多行,无论转义分析器指令是否包含在Dockerfile中,转义都不会在RUN命令中执行,除非在行尾执行。在windows上,\ 是目录之间的分隔符,将转义字符设置为 ` 是非常有用的,它和 PowerShell 一致,比如在windows的powerShell中:

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

没有指定指令解释器,默认为\,那此时第二行中COPY testfile.txt c:\\就出现了转义字符(c:\\中的第一个\),实际意思就是COPY testfile.txt c:\,那剩下的那个\就变成了行继续的操作,所以第二行和第三行是一行实际为:COPY testfile.txt c:\RUN dir c:第三行的转义字符在行末属于可执行范围。在powershell中,为了避免这个问题可以使用 ` 作为转义字符就不会出现上述的问题:

# escape=`

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

上述Dockerfile拆解成了3个指令(除去指令解释器,之前是2条指令)。

2.2 环境替换

 docker中可以使用ENV为特定的指令声明环境变量,转义字符也被处理为将类似变量的语法包含到字面上的语句中。环境变量在Dockerfile中使用$variable_name${variable_name}标注,上述2标注方式是一样的,第2种{xx}的方式通常用于解决变量名没有空格的问题(不是很理解),比如${foo}_bar,此外这种大括号的方式还支持一些标准的bash修饰符,比如(word只是随意取的值):

  • ${variable:-word}表示如果variable设置了,那它的值就是设置的值;如果variable没有设置,那word就是它的值(有点三目运算的意思)。
  • ${variable:+word}表示如果variable设置了,那word就是它的值;如果variable没有设置,那它的值就是空字符串。

注:变量上也是可以使用转义字符的,比如使用的转义字符是默认的\,那在变量中\$foo\${foo},那就表示它就是个普通的字符串$foo${foo},而不是对应foo的值。

 环境变量支持如下的指令:ADDCOPYENVEXPOSEFROMLABELSTOPSIGNALUSERVOLUMEWORKDIRONBUILD(1.4版本之后该指令只有在和上述其他指令结合使用时才能使用环境变量)。下面是一个示例(解析的指令在#后已经标出):

FROM busybox
# 声明环境变量foo,表示的值为/bar
ENV foo /bar
# 等同于 WORKDIR /bar
WORKDIR ${foo}
# 等同于 ADD . /bar
ADD . $foo
# 等同于 COPY $foo /quux
COPY \$foo /quux

 环境变量的替换将在整个指令中对每个变量使用相同的值,比如下面的示例:

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

上述的定义过程中,def的值为hello,而不是byeghi的值为bye,因为它不是将abc设置为bye那条指令的一部分。可能有点绕人,但只要记住一点就行了,在使用变量时,变量的值永远都是前面最靠近使用该变量的指令的定义(但不包含本身),如第2条指令使用def=$abc,向前寻找变量abc,排除自身,发现最靠近的abc定义在第一条指令中abc=hello,所以def=hello;同样第3条指令使用ghi=$abc,向前寻找变量abc,发现第2条指令最靠近它,虽然第1条指令也有abc的声明,但没有第2条指令靠近,所以取第2条指令之中abc的声明bye,所以ghi=bye

2.3 .dockerignore文件

 和.gitignore类似,在CLI(Command-Lin Interface)将context发送给docker daemon时,它会关注一下context根目录下的.dockerignore文件,如果存在,CLI将修改context以排除与该文件中匹配的文件和目录。这样可以避免将一些不必要的大文件和敏感发送给docker daemon并通过ADDCOPY将这些文件加入到镜像中。同样的.dockerignore文件中也是允许注释的,行首用#标注即可,比如:

# comment
*/temp*
*/*/temp*
temp?

上述的# comment是一个注释,CLI将不

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值