TOC

Dockerfile基础概念

    如果我们从Docker Hub上面拉下来的一个镜像,那么这个镜像是不符合我们的生产需要的,比如说我们pull下来一个centos镜像,但是我们需要一个可以运行httpd服务器的名称空间,所以我们又需要在这个centos之上安装一个httpd服务,所以在此看他,默认在docker hub上下载下来的镜像是不符合我们的生产需要的;

    比如我们之前运行一个容器然后使用docker的exec命令再去改,改完了之后再去commit,这样就比较麻烦了,或者说,假设我们把httpd配置文件的路径做成volume,从宿主机上加载文件,启动之前给它编辑好,然后启动容器时,把httpd程序应用默认加载配置文件的路径与宿主机上的目录建立关联关系,然后去启动容器,它也能够加载我们在外面定制的配置文件,但是这样一来有一个坏处在于,我们在宿主机当中所做的编辑是无法立即生效的,还是需要重载应用程序;

    还有一种实现方式,那就是基于镜像做容器,首先在容器内部,配置好程序所运行需要的环境,然后使用commit来做镜像,但是这样又有一个缺陷,假设我们有三个环节,一个线上一个测试一个开发,那么这就说明我们要为一个项目做三个镜像,如果我们又有新版本更新对配置文件做了修改,那么我们又需要重新修改我们的镜像,所以直接把容器做成镜像这种方式也是不妥的;

    那么docker在配置文件这个问题上,的解决办法是。比如以nginx为例,假设这个nginx服务器启动只做一件事,就是配置一个虚拟主机,提供一个server,但是这个虚拟主机它所服务的主机名叫什么,监听的端口,家目录等在各种环境可能各不一样,但是它的配置文件的格式是一致的,那么就可以利用环节变量来解决端口、家目录等配置,我们就可以在容器内部的主进程启动之前,先启动一个别的程序,这个程序就把用户传递进来的每一个变量的值替换在配置文件中并且保存,保存完了之后再启动内部的主进程,它就可以退出,从而能够达到通过传递参数来配置容器内部信息的功能,适用于多种不同环境配合,而默认情况下我们的nginx不支持这种功能,所以我们自己做镜像就是来做这些事情;

    Dockerfile就是用来我们构建docker镜像的源码,在命令行中去调用这个配置文件,这个配置文件是去生成一个Docker image的,所以说从这个角度来讲,Dockerfile实际上就是一个纯文本文件,里面包含一些指令而已,指令不区分大小写,它是顺序执行的指令,自上而下依次执行。

    Dockerfile工作逻辑,我们去只做Docker镜像时它必须则某一个特定的目录来执行,在这个目录中创建Dcokerfile,那么Dcokerfile文件名首字母必须大写,如果在Dcokerfile当中需要将一些文件打包到镜像当中,那么这些文件也必须存放在与Dcokerfile同级的目录当中,不能是其他目录,只能与Dcokerfile同级或Dcokerfile同级目录下才行;

    有时候可能有这种场景,与Dockerfile同级还有一个数据目录,这个数据目录里面的部分数据需要写入docker镜像,由于文件太多,手动指定写哪个文件太过于复杂,所以docker还支持一个隐藏文件.dockeringore用这个文件可以写一些文件的路径,代表这些文件无需写入镜像;

注意:Dcokerfile里面CMD、ENTRYPOINT一律用双引号来做字符串的标识

DockerFile环节变量
    $variable_name/${variable_name}:变量命名;
    ${variable_name:-word}:设定默认值,如果没有值那么显示word;
    ${variable_name:+word}:和上面相反,如果有值那么显示word,如果没有值那么就为空;

Dockerfile指令

FROM
    FROM指令是最重要的一个且必须为Dockerfile文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境;
    实践中,基准镜像可以是任何可下载的镜像文件,默认情况下,docker buile会在当前主机找这个镜像,如果不存在会自动从Docker Hub或者用户自定义指明的镜像仓库去下载,如果找不到镜像则会返回一个错误;
语法
FROM <repository>[:<tag>] 或 FROM <repository>@<digest>
    repository:指定作为base image的名字;
    tag:base image的标签,为可选,省略默认为latest;
    digest:镜像的hash码;
MAINTANIER(较新版本已经废弃用LABEL代替)
    用于让Dockerfile制作者提供本人的详细信息;
    Dockerfile并不限制MAINTANIER指令出现的位置,但推荐将其放在FROM后面;
语法
MAINTANIER <author detail>
<author detail>:是需要利用括号括起来的字符串;
LABEL
    它是MAINTANIER指令的加强版,可以为镜像指定各种各样的元数据,利用kv数据进行表示;
语法
LABEL <key>=<value> <key>=<value> <key>=<value>...
COPY
用于从docker宿主机复制文件至创建的新镜像文件内;
COPY指定则以WORKDIR为起始路径;
如果src是目录,其内部文件或子目录会递归复制,但src目录自身不会被复制;
如果指定了多个src,或在src中使用了通配符,则dest必须是一个目录,且必须以/结尾;
如果dest事先不存在,它将会被自动创建,这包括其父目录路径;
语法
COPY ["<src>",... "<dest>"] 或 COPY  "<src>",..."<dest>"
ADD
    ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径;
    如果src为URL且dest不以/结尾,则src指定的文件,将被下载下来,并直接创建为dest,如果dest以/结尾,则文件名URL指定的文件将被直接下载并保存为dest/filename
    如果src是一个本地文件系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于tar -x命令,然而通过URL获取到的tar文件将不会自动展开;
    如果src有多个,或其间接或直接使用了通配符,则dest必须是一个以/结尾的目录路径,如果dest不以/结尾,则视作一个普通文件,src的内容将被直接写入到dest;
语法
ADD <src>,...<dest> 或 ADD ["<src>",..."<dest>"]
WROKDIR
    用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD设定工作目录,可以出现多次,其路径也可以是一个相对路径,不过是相对此前一个WORKDIR指定的路径;
    另外,WORKDIR也可以调用ENV指定的变量来定义;
语法
WORKDIR <dirpath>
WORKDIR $STATEPATH
VOLUME
    用于在image中创建一个挂载点目录,以挂载Docker Host上的卷或其他容器上的卷;
    如果挂载点目录路径下此前有文件存在,docker run命令会在卷挂载完成后将此前的所有文件复制到新的挂载点卷中;
语法
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]
EXPOSE
    用于为容器打开指定要监听的端口以实现外部通信,如果在Dockerfile中指定了EXPOSE当docker run起来之后不会直接暴露,只是表明,可以暴露这个端口,如果要暴露这个端口,那么可以在docker run使用指令-P命令,它会直接读取docker镜像内部命令指明暴露的端口进行暴露;
    重点:EXPOSE是指明待暴露的端口,而不会真正的暴露,docker run -P 这样会将待暴露的端口进行暴露;
语法
EXPOSE <port>[/<protocol>] [<port>[/<protocol>]...]
    protocol:用于指定传输层协议,可为TCP或UDP二者之一,默认为TCP协议
实例:
    EXPOSE 11211/udp 11211/tcp
ENV
    它是在Build用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其他指令,如ENV 、ADD、COPY等所调用;
    调用格式为$variable_name或${variable_name};
语法
ENV <key> <value>  或  ENV <key>=<value> ...
    第一种格式中,key之后所有内容均会被视作其value的组成部分,因此,一次只能设置一个变量;
    第二种格式中,可用一次设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果value包含空格,可以以反斜线(\)进行转义,也可以通过对value加引号进行标识,另外,反斜线也可用于续行;
    定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能;
    printenv可以打印出容器内所有的环境变量;
    ENV给定的变量也可以交给RUN或者CMD命令来引用;
RUN
    用于指定docker build过程中运行的程序,其可以是任何命令;
    第一种格式中,command通常是一个shell命令,并且以/bin/sh -c 来运行它,这意味着此进程在容器中PID部位1,不能接受Unix信号,因此,当使用docker stop <conntainer>命令停止时,此进程接收不到SIGTER信号;
    第二种格式中的参数是一个JSON格式的数组,其中command为要运行的命令,后面的paramN为传递给命令的参数,然而此种格式指定的命令不会以/bin/sh -c 来发起,因此常见的shell操作如变量替换以及通配符替换将不会进行,不过,如果要运行的命令依赖此shell环境特性的话,可以将其改为 RUN ["/bin/sh","-c","<executable>","<param1>"];
语法
RUN <command> 或 RUN ["<command>","<param1>","<param2>"...]
CMD
    类似于RUN指令,CMD指令也可以用于运行任何命令或应用程序,不过,CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束之后,容器也将终止;并且CMD指定的命令可以被docker run的命令选项覆盖;
    Dockerfile存在多个CMD指令,仅最后一个会生效;
    第三种用于为ENTRYPEOINT指令提供默认参数;
语法
CMD <command> 或 CMD ["<command>","<param1>","<param2>"...] 或 CMD ["<param1>","<param2>"...]
ENTRYPOINT
    类似CMD命令的功能,用于为容器指定默认的运行程序,从而使得容器像是一个单独的可执行程序;
    与CMD不同的是,由ENTYPPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令(也可以理解为CMD)会被当做参数传递给ENTRYPOINT指定的指定的程序执行;
            不过docker run命令的--entrypoint选项的参数可覆盖ENTRYPOINT指令的程序;
    docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令的后面作为其参数;
    Dcokerfile文件也可以存在多个ENTRYPOINT,但是仅最后一个会生效;
语法
ENTRYPOINT <command>
ENTRYPOINT ["<executable>","<param1>","<param2>"]

注意:ENTRYPOINT一般用来指定shell程序,用什么shell来执行程序,CMD指令的命令最终会传递给ENTRYPOINT后面,一般来讲都会执行一个脚本,脚本可以有变量,在docker run的时候指定-e的环境变量来结合生成配置,并且这个脚本最后会执行一条命令 exec $@ 也就是用CMD传过来的命令替换当前进程,详细请看附件

ONBUILD
    用于在Dockerfile中定义一个触发器;
    Dockerfile用于build镜像文件,此镜像文件也可作为基础镜像,被另一个Dockerfile用作FROM指令的参数,并以之构建新的镜像文件;
    在后面的这个Dockerfile中的FROM指令在build过程执行时,将会触发创建其基础镜像Dockerfile文件中的OLDBUILD指令定义的触发器;
    尽管任何指令都可以注册成为触发指令,但是ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令;
    使用包含ONBUILD指令的Dockerfile构建镜像时,应该使用特殊的标签,例如:ruby:2.0-onbuild;
    在ONBUILD指令中使用ADD 或 COPY 指令,应该格外小心,因为构建过程中的上下文在缺少指定的源文件时会报错;
语法
    ONBUILD <指令>
    示例:
        ADD https://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm /usr/local/src/
ARG
    ARG和ENV很像,它也是一个变量,但是这个变量只会在docker build的时候使用,而且能够为build命令执行时,使用--build-arg传递,这个功能就使得一个Dockerfile能够适用于不同的场景了;
    先定义后使用;
语法
    ARG <name>[=<default value>]
    示例
        ARG version=1.1.0
USER
    指定运行镜像时,或运行dockerfile文件中的RUN/CMD/ENTRYPOINT指令的程序时间,所运行的用户,也就是指定RUN/CMD/ENTRYPOINT/COPY等指令的运行的用户。
    <uid>应该使用/etc/passwd文件中存在的用户uid,否则docker run会报错。
语法格式
    USER <username>|<uid>
HEALTHCHECK
    HEALTHCHECK指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。

    当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy。

    在 HEALTHCHECK [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样,分为 shell 格式,和 exec 格式。命令的返回值决定了该次健康检查的成功与否:0:成功;1:失败;2:保留,不要使用这个值。

    如果写多个最后一个生效;
状态
starting:初始状态;
healthy:成功状态;
unhealthy:非成功状态;
选项
--interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
--start-period=<时长>:docker run进程起来之后多久才开始检测,默认0秒;
--retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次;
语法
HEALTHCHECK [options] CMD [command]
示例
    HEALTHCHECK --start-period=3s CMD ls /etc/noexists||exit 1
SHELL
    用来指定容器默认运行的shell程序,默认为["/bin/sh","-c"]
语法
    SHELL ["<executable>","<parametes>"]
    windows:
        SHELL ["cmd","/S","/C"]
STOPSIGNAL
    进程号ID为1的,是能够docker stop命令,从而停止主进程的。主进程一停,容器就会停止,所以这个就是为什么docekr stop能停止容器的原因;
语法
    STOPSIGNAL 9

附件列表

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注