Docker学习——Dockerfile中的构建命令
目录 前言 Dockerfile的作用Parser directivesescapeENV.dockerignore文件FROMRUNCMDLABELMAINTAINEREXPOSEENVADDCOPYENTRYPOINT VOLUMEUSERWORKDIRARGONBUILDSTOPSIGNAL...
目录
前言
docker的有些文档写的真的有点糟糕.............
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=value2 FROM ImageName
- 出现在构建命令之后
FROM ImageName # directive=value
- 出现在非parser directive之后
# About my dockerfile # directive=value FROM ImageName
- 不是合法的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}具有更多的用法:
- ${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=$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>]
要点:
- FROM为接下来的构建指令指定基础镜像,一个有效的Dockerfile文件必须以FROM指令开头
- 除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
- FROM指令允许添加镜像的别名(指令中的[AS <name>]),在接下来的FROM指令或是COPY --from=<name|index>指令中可以引用这个镜像
- 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指令的关系
- 两者均制定了容器运行时执行的命令
- Dockerfile应该至少指定CMD或是ENTRYPOINT中的一个
- CMD可以为ENTRYPOINT指定参数值
- 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"]
更多推荐
所有评论(0)