目录

 

前言

 

Dockerfile的作用

Parser directives

escape

ENV

.dockerignore文件

FROM

RUN

CMD

LABEL

MAINTAINER

EXPOSE

ENV

ADD

COPY

ENTRYPOINT

 

VOLUME

USER

WORKDIR

ARG

ONBUILD

STOPSIGNAL

HEALTHCHECK

SHELL



 

前言

docker的有些文档写的真的有点糟糕.............

 

Dockerfile的作用

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

 

 

Parser directives

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

使用格式:#directive=value

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

  1. 换行
    # direc \
    tive=value
  2. 重复定义
    # directive=value1
    # directive=value2
    
    FROM ImageName
  3. 出现在构建命令之后
    FROM ImageName
    # directive=value
  4. 出现在非parser directive之后
    # About my dockerfile
    # directive=value
    FROM ImageName
  5. 不是合法的parser directive,目前docker支持的Parser directives只有escape,所以我在这里废话总结
    # unknowndirective=value
    # knowndirective=value

 

escape

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

# escape=\ (backslash)
# escape=` (backtick)

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

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

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

COPY testfile.txt c:\RUN dir c:

因此在windows上最好使用  ` 号

 

ENV

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

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

  1. ${variable:-word}:如果variable值没有被指定,那么将使用word作为值
  2. ${variable:+word}:如果variable值被指定,那variable最终的值将是word,如果没有被指定,则默认为空字符串

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

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

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

ENV abc=hello
ENV abc=bye def=$abc
ENV 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?

上述文件每行意义如下:

.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>]

要点:

  1. FROM为接下来的构建指令指定基础镜像,一个有效的Dockerfile文件必须以FROM指令开头
  2. 除arg指令以外的构建指令都不能出现在FROM之前,Dockerfile文件可以出现多个FROM指令,此时一个Dockerfile可以构建多个image,并且输出这些image的ID,在执行FROM指令时,之前构建命令生成的状态会被清空,若arg指令位于FROM之外,FROM内部的指令不能引用arg,如果想使用,必须声明一遍:
    ARG VERSION=latest
    FROM busybox:$VERSION
    #声明过后,才能替换RUN指令为的$VERSION为latest
    ARG VERSION
    RUN echo $VERSION > image_version
  3. FROM指令允许添加镜像的别名(指令中的[AS <name>]),在接下来的FROM指令或是COPY --from=<name|index>指令中可以引用这个镜像
  4. tag与digest指镜像的版本信息,如果不指定,则默认为latest

 

RUN

RUN指令有两种形式:

#shell形式,命令会运行在shell脚本上,在linux上默认为/bin/sh -c,在windwos上默认为cmd /S /C
RUN <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

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中自动推算得知,例如ADD http://example.com/foobar /,在/下对应的文件名为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指令的关系
  1. 两者均制定了容器运行时执行的命令
  2. Dockerfile应该至少指定CMD或是ENTRYPOINT中的一个
  3. CMD可以为ENTRYPOINT指定参数值
  4. CMD指定的值可以被命令中对应的参数值替换,两者的协作效果如下

 

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>] or
USER <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 /a
    WORKDIR b
    WORKDIR c
    RUN pwd
    
    RUN指令指定的文件为 /a/b/c/pwd
  • WORKDIR指令可以使用ENV指定的环境变量,例如:
    ENV DIRPATH /path
    WORKDIR $DIRPATH/b
    RUN pwd
    
    RUN指令指定的文件为/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 ubuntu
    2 ARG CONT_IMG_VER
    3 ENV CONT_IMG_VER v1.0.0
    4 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 ubuntu
    2 ARG CONT_IMG_VER
    3 ENV CONT_IMG_VER hello
    4 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"]
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐