Dockerfile文件说明

Dockerfile #生成的镜像的时候会在执行命 令的当前目录查找 Dockerfile 文件,所以名称不可写错,而且 D 必须大写
如果在从该镜像启动容器的时候也指定了命令(如 docker run -d nginx nginx),那么(nginx)指定的命令会覆盖 Dockerfile 构建的镜像里面的 CMD 命令,即指定的命令优先级更高,Dockerfile 的 优先级较低一些,重新指定的指令优先级要高一些。
除了注释行之外的第一行,必须是 From。
一般Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。

Dockerfile指令
FROM    MAINTRINER       RUN        EXPOSE
CMD     ENTRYPOINT       WORKDIR    ENV
USER    VOLUME           ADD        COPY       
LABEL   STOPSIGNAL       ARG        ONBUILD   HEALTHCHECK


一、From #第一行先定义基础镜像,后面的本地有效的镜像名,如果本地没 有会从远程仓库下载.
FROM nginx

二、MAINTAINER   #镜像维护者的信息
MAINTAINER Jack.Zhang 123456@qq.com

三、LABEL #将元数据添加到镜像。一个镜像可以有多个标签。
基础镜像或父镜像(FROM行中的图像)中包含的标签由镜像继承。如果一个标签已经存在,但是有不同的值,那么最近应用的值将覆盖以前设置的任何值。

两种方法
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"


四、USER  #指定该容器运行时的用户名和 UID,后续的 RUN 命令也会使用这面指定的用户执行
USER user
USER user:group
USER uid
USER uid:gid



五、WORKDIR   指令用来执行容器运行时,执行容器运行时的工作目录,ENTRYPOINT指令和/和CMD指令运行的命令就是在这个指令所指定的工作目录下执行的
#指定工作目录,最终工作路径为/a/b 
WORKDIR /a 
WORKDIR b  
                             
一个Dockerfile如下所示,其将容器的工作路径切换到/opt/webapp/db目录下,然后执行了RUN指令
WORKDIR /opt/webapp/db
RUN bundle install

指定多个WORKDIR,表示在构建镜像时切换工作目录。例如,下面构建镜像时,先将工作目录切换到/opt/webapp/db执行RUN指令,然后再将工作目录切换到/opt/webapp执行rackup命令
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp/
ENTRYPOINT [ "rackup" ]




六、VOLUME    #将容器中的一个或多个目录挂载到宿主机中/var/lib/docker/volumes/, 删除容器时文件也存在  ,常用于日志。  docker inspect  容器ID   | grep  volume  或 docker inspect 挂载目录ID
VOLUME ["/dir_1", "/dir_2" ..]   

docker run  -it -v /date/:/date  nginx   两者区别在于,VOLUME挂载到宿主机中/var/lib/docker/volumes/ 是固定的, 而-v 是可自定义路径。  

从Dockerfile中更改卷:如果任何构建步骤在卷声明后更改了数据,这些更改将被丢弃。
JSON格式:列表被解析为一个JSON数组。必须用双引号(")括起来,而不是单引号(')。
宿主目录是在容器运行时声明的:宿主目录(挂载点)本质上依赖于宿主。这是为了保持映像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。由于这个原因,您不能从Dockerfile中挂载主机目录。卷指令不支持指定host-dir参数。创建或运行容器时,必须指定挂载点

下面这条指令会为基于此镜像创建的任何容器创建一个名为/opt/project的挂载点
VOLUME ["/opt/project"]

也可以以数组的形式指定多个卷。例如:
VOLUME ["/opt/project", "/data"]





七、ENV  #设置容器变量,常用于想容器内传递用户密码等  

从Docker 1.4开始,可以指定多个环境变量。例如,下面设置了两个环境变量:
ENV RVM_PATH=/home/rmv RVM_ARCHFLAGS="-arch i386"
1一些注意点:
当设置了环境变量之后,后面的任何RUN指令等操作都是基于这个环境变量进行操作
如果需要,可以通过在环境变量前加上一个反斜线来进行转义
该指令设置的环境变量,会在镜像中永久保存,因此基于该镜像创建的任何容器都会使用这个环境变量
-e选项
默认情况下,ENV设置的环境变量是在镜像中永久保存的,因此每次创建容器,进入系统之后变量都会在系统中
"docker run"的-e选项可以指定:只在本次创建的容器运行时有效,容器退出或删除后环境变量失效

例如,下面启动时指定容器的WEN_PORT环境变量为8080,当该容器退出后变量失效 
sudo docker run -it -e "WEB_PORT=8080" dongshao/images


环境变量默认会在镜像中持久化。如果仅在构建过程中需要环境变量,而在最终映像中则不需要,则执行如下:
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...

或者使用ARG,它不会在最终镜像中持久化
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...




七、ADD   #指令用来将Dockerfile构建环境下的文件和目录复制到镜像中,

格式说明:
在ADD文件时,Docker通过目的地址(后面的路径)参数末尾的字符来判断文件源(前面的路径)是目录还是文件
如果目标地址以/结尾,那么Docker就认为源位置指向的是一个目录,那么会在文件源放置在这个目录下
如果目标地址不是以/结尾,那么Docker就认为源位置指向的是文件,那么表示将文件源拷贝成该指定的文件
压缩文件会自动解压

ADD遵循以下规则:
<src>路径必须在构建的上下文中;你不能添加../某物/某物,因为docker构建的第一步是发送上下文目录(和子目录)给docker守护进程。
如果<src>是一个URL,而<dest>不以末尾的斜杠结束,则从URL下载一个文件并复制到<dest>。
如果<src>是一个URL,而<dest>确实以一个末尾的斜杠结尾,那么文件名将从URL推断出来,文件将被下载到<dest>/<filename>。例如,添加http://example.com/foobar /将创建文件/foobar。URL必须有一个重要的路径,这样才能在这种情况下发现合适的文件名(http://example.com不起作用)。
如果<src>是一个目录,则复制该目录的全部内容,包括文件系统元数据。目录本身不会被复制,只是它的内容。
如果<src>是一个以可识别的压缩格式(identity、gzip、bzip2或xz)的本地tar存档,那么它将被解压缩为一个目录。远程url中的资源不会解压缩。识别为可识别的压缩格式完全取决于文件的内容,而不是文件的名称。解压缩行为与tar -x相同。
如果<src>是任何其他类型的文件,它会和它的元数据一起单独复制。在这种情况下,如果<dest>以一个末尾的斜杠/结束,它将被认为是一个目录,并且<src>的内容将写入<dest>/base(<src>)。
如果指定了多个<src>资源,或者直接指定,或者使用通配符指定,那么<dest>必须是一个目录,并且必须以斜杠/结束。
如果<dest>没有以尾斜杠结束,它将被认为是一个常规文件,并且<src>的内容将写入<dest>如果<dest>不存在,将在其路径中创建所有缺失的目录(目标路径可以不存在,由ADD创建路径)。

# 添加所有以“hom”开头的文件:
ADD hom* /mydir/

# 添加所有hom开头的txt文件 ?被替换为任何单个字符,例如,“home.txt”
ADD hom?.txt /mydir/

# <dest>是一个相对路径,或一个相对于WORKDIR的路径,源文件将被复制到目标容器中。
# 添加test.txt文件到 <WORKDIR>/relativeDir/
ADD test.txt relativeDir/

# <dest>是一个绝对路径,添加test.txt文件到 /absoluteDir/
ADD test.txt /absoluteDir/

# 添加包含特殊字符的文件,arr[0].txt
ADD arr[[]0].txt /mydir/

 添加上容器的文件权限自动设置为 600 ,如果需要更改权限,需要在增加一层 RUN 进行权限修改。
 如果下载的是一个压缩包,一般情况是 RUN 指令,然后使用wget或curl去下载,然后更改权限、解压、清理下载的源文件。
 需要注意的是,ADD 指令会令镜像构建缓存失效,从而可能会镜像构建变得非常缓慢。
 在使用 ADD 指令的时候,还可以加上 --chown=<user>:<group> 选项来改变文件的所属用户和所属组。
 ADD --chown=55:mygroup files* /mydir/
 ADD --chown=bin files* /mydir/
 ADD --chown=1 files* /mydir/
 ADD --chown=10:11 files* /mydir/




七、EXPOSE  #仅仅只是声明向外开放的端口,多个端口用空格做间隔,启动容器时候-p需要使用此端向外映射,如:-p 8081:80

帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

EXPOSE  80 443



八、COPY   #非常类似于ADD指令,它们根本的不同是COPY只关心在构建上下文中复制本地文件,而不会去做文件提取(extraction)和解压(decomposition)的工作
相关说明:
文件源路径必须是一个与当前构建环境相对的文件或者目录,本地文件都放到和Dockerfile同一个目录下。不能复制该目录之外的任何文件,因为构建环境将会上传到Docker守护进程,而复制是在Docker守护进程中运行的。任何位于构建环境之外的东西都是不可用的
COPY指令的目的位置必须是容器内部的一个绝对路径
与ADD指令一样,如果目的位置不存在,那么也会进行创建,任何由该指令创建的文件或者目录的UID和GID都会设置为0
如果源路径是一个目录,那么这个目录将整个复制到容器中,包括文件系统元数据
如果源路径是一个文件,则该文件会随同元数据一起被复制

例如:
下面是将宿主机当前构建目录下的conf.d/目录拷贝到镜像的/etc/apache2/目录下
COPY conf.d/ /etc/apache2/


九、STOPSIGNAL
这个指令笔者也并不常用,我查询了docker-nginx在github上的issues里面给出的答案是,之所以加上STOPSIGNAL信号的目的是:避免docker容器停止后,nginx服务不能正确终止造成僵尸进程的存在。
STOPSIGNAL指令用来设置停止容器时发送什么系统调用信号给容器
这个信号必须是内核系统调用表中合法的数,比如9,或者SIGNAME格式中的信号名称,如SIGKILL
STOPSIGNAL指令是在Docker 1.9版本中引入的
STOPSIGNAL SIGQUIT  

停止信号,类似kill -9。
STOPSIGNAL signal



十、ARG:  构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
可以使用ARG或ENV指令来指定RUN指令可用的变量,使用ENV指令定义的环境变量总是覆盖同名的ARG指令。
不建议在构建镜像时使用-build-arg参数来传递如密钥,用户凭据等信息,因为使用docker history命令可以看得到
构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。
如果ARG指令有一个默认值,并且在构建时没有传递值,构建器使用默认值。

ARG user1=someuser

十一、ONBUILD  #为镜像添加一个触发器指令,以便在稍后将该映像用作另一个构建的基础时执行。触发器将在下游构建的上下文中执行,就像它在下游Dockerfile中的FROM指令之后被立即插入一样。

任何构建指令都可以注册为触发器。

ONBUILD <INSTRUCTION>
如果正在构建一个映像,它将作为构建其他映像的基础,那么这是非常有用的。
不允许使用ONBUILD ONBUILD链接ONBUILD指令。




十一、HEALTHCHECK  #检查运行状况。在一定数量的连续失败之后,它就变得不健康。
Dockerfile中只有最后一个健康检查才会生效。
HEALTHCHECK NONE(禁用从基本映像继承的任何健康检查)
HEALTHCHECK [OPTIONS] CMD command (通过在容器中运行命令来检查容器的健康状况)
在CMD之前可以显示的选项是:
    --interval = DURATION(时间间隔=持续;默认值:30s)
    --timeout = DURATION(超时=持续;默认值:30s)
    --start-period = DURATION(开始时间;默认值:0s)
    --retries = N(重试;默认值:3)
CMD关键字后面的命令可以是shell命令或exec数组。

健康状态码:
    0:成功—容器运行良好,可以使用
    1:不健康—容器不能正常工作
    2:保留-不使用此退出码
示例:# 每隔五分钟左右检查一下网络服务器是否能在三秒内提供网站主页,当容器的健康状态发生变化时,将生成一个带有新状态的health_status事件。
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1




十二、RUN  #用于执行后面跟着的命令行命令,有以下俩种格式:
1.shell 格式:
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。

2.exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:


FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:

FROM centos
RUN yum -y install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz
如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。



十三、ENTRYPOINT
一个Dockerfile中如果定义多个ENTRYPOINT,只有最后一条ENTRYPOINT生效,并且每次启动docker容器,都会执行ENTRYPOINT指定的脚本。对于nginx:1.20.2而言,"/docker-entrypoint.sh"脚本中定义了nginx配置检查及nginx服务的启动指令。所以通常情况下ENTRYPOINT指定的脚本通常都是镜像内核心服务的启动脚本。

ENTRYPOINT ["/docker-entrypoint.sh"]
这个脚本中最后执行了nginx服务启动,需要配合CMD命令完成,参考下文的CMD命令。如:
ENTRYPOINT ["nginx"]
CMD ["-g","daemon off;"]


十四、CMD ["nginx","-g","daemon off;"]         #运行的命令,每个 Dockerfile 只能有一条, 如果有多条则只有最后一条被执行 .
CMD指令也是用来执行linux命令或脚本,这一点和RUN指令是一致的。二者的区别在于
CMD指令是在执行docker run指令时被执行,也就是创建容器时执行;而RUN指令实在镜像构建的时候执行,即docker bulid时候执行。
也正因为指令的执行期不同,RUN命名执行的写入操作被写入到镜像层。CMD指令执行结果包含写入操作,被写入到容器层。(可以参考我之前文章《镜像分层原理》学习理解)。
所以CMD和ENTRYPOINT 指令有点相似,都是在创建容器时运行。需要注意的是:一旦Dockerfile中包含ENTRYPOINT指令,CMD指令就作为ENTRYPOINT指定的脚本的参数存在。参考下文格式语法。

CMD包含三种格式:
第一种为ENTRYPOINT指定的脚本传参,上文中的ENTRYPOINT脚本最后一行的exec "$@"就是在执行CMD传递的命令及参数。来完成nginx服务的启动。这种用法是各官方Dockerfile常用的做法(如:nginx、redis),就是在ENTRYPOINT指定的脚本中做一些配置准备工作,
然后在ENTRYPOINT脚本的最后一行通过exec "$@"调用CMD命令进行容器服务的启动。
CMD ["nginx", "-g", "daemon off;"]

第二种 执行一个命令或shell脚本,可以传参,注意是双引号。与第一种格式语法是一样的,只是没有ENTRYPOINT指定脚本,所以不作为ENTRYPOINT指定脚本的参数存在。
CMD ["executable","param1","param2"]

第三种为普通的执行shell脚本的语法,如执行echo "This is a test." | wc -cshell命令行。笔者说:在Dockerfile中这种方法不要用,没必要知道为什么。
CMD echo "This is a test." | wc -c

CMD指令与"docker run"的关系:
我们知道,在执行"docker run"让容器运行时,可以在"docker run"命令的最后加上命令让容器去运行
CMD指令与上面的功能是一样的。不过"docker run"的命令会覆盖CMD指令




SHELL形式与EXEC形式的注意事项1
Dockerfile的RUN、CMD、ENTRYPOINT等指令可以用来执行命令。执行命令的方式有2种:
    shell形式(默认方式):默认的情况下,Docker是使用shell来执行这些命令的,例如ENTRYPOINT node app.js
    exec形式:如果你想要以exec形式运行命令,那么可以使用数组的形式运行命令,例如,如ENTRYPOINT["node","app.js"]


shell形式与exec形式有什么区别:
    shell:会启动一个shell,然后在shell上面运行程序。也就是说,该形式运行的命令,会自动在前面加上/bin/sh -c
    exec:不启动shell,直接运行程序
    如果在一个不支持shell的平台上运行或者不希望在shell中运行(比如避免shell字符串篡改),也可以使用exec格式的RUN指令
例如,下面以exec形式运行ENTRYPOINT指令:
ENTRYPOINT["node","app.js"]

之后查看容器中的运行进程列表,可以看到这里是直接运行node进程,而并非在shell中执行
docker exec dfg8sga  ps x

如果采用shell形式(ENTRYPOINT node app.js),容器进程如下所示,可以看到其是在shell上运行的程序
docker exec  -it  dfg8sga  ps x



SHELL形式与EXEC形式的注意事项2
但是当涉及到的命令涉及一些变量时,那么EXEC是不能识别常量的。于是就需要使用shell形式来执行
例如,执行下面的ENTRYPOINT指令,那么容器运行时,打印的是"hello &name",不会识别出name变量
FROM ubuntu
ENV name Docker
ENTRYPOINT ["/bin/echo", "hello $name"]

对于上面问题的更改就是改用/bin/bash来执行,例如下面就是正确的了,最终会打印"hello Docker"
FROM ubuntu
ENV name Docker
ENTRYPOINT ["/bin/bash", "-c", "echo hello $name"]

 

posted @ 2022-10-17 16:39  yuanbangchen  阅读(174)  评论(0编辑  收藏  举报