DockerFile

一般来说,通用的Docker image没法满足公司的业务需求,需要根据公司的实际需求制作特定image,方法主要有两个:
1.基于容器创建
2. DockerFile

DockerFile Format

1. # 注释
2. INSTRUCTION 参数 (INSCTRUCTION最好用大写)
3. 顺序执行INSTRUCTION
4. 第一个非注释行的INSTRUCTION必须以FROM开始,去指定基础image
5. 必须以一个专用目录,Dockerfile文件必须存在,首字母必须大写,引用文件的路径必须是专用目录为开始,也可以用子目录来包含所有文件,一定不能是父目录。也可以用.dockerignore来指定文件路径,打包时所有在此文件中指定的文件都不包含,类似黑名单,起文件排除的作用。
6. 制作image的指令是docker build,docker build会隐式启动一个容器,同时在Dockerfile中能执行的命令必须是基础image中包含的。
7. 可以用环境变量Environment variables,Dockerfile中的变量和shell的变量非常类似
    ${variable:-word}:indicates that if variable is set then the result will be that value. If variable is not set then word will be the result.
    ${variable:+word} :indicates that if variable is set then word will be the result, otherwise the result is the empty string.

DockerFile INSTRUCTION

FROM

1:定义
指令是最重的一个且必须为Dockerfile文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境。实践中,基准镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry或指定的Registry上拉取所需的镜像文件。
2:语法
    • FROM <repository>[:<tag>] 或
    • FROM <resository>@<digest>
        • <reposotiry>:指定作为base image的名称;
        • <tag>:base image的标签,为可选项,省略时默认为latest;

LABEL

1.:定义
    用于让Dockerfile制作者提供本人的详细信息
    Dockerfile并不限制LABEL指令可在出现的位置,但推荐将其放置于FROM指令之后
2:语法
    - LABEL <key>=<value> <key>=<value> <key>=<value>
    - LABEL 是键值类型
    - 一个image可以有多个label
    - 可以在一行中指定多个label

COPY

1:定义
    用于从Docker主机复制文件至创建的新映像文件
2:语法
    - COPY <src> ... <dest> 或
    - COPY ["<src>",... "<dest>"]
        • <src>:要复制的源文件或目录,支持使用通配符
        • <dest>:目标路径,即正在创建的image的文件系统路径;建议为<dest>使用绝对路径,否则,COPY指定则以WORKDIR为其起始路径;
3:文件复制准则
    - <src>必须是build上下文中的路径,不能是其父目录中的文件
    - 如果<src>是目录,则其内部文件或子目录会被递归复制,但<src>目录自身不会被复制
    - 如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且必须以结尾
    - 如果<dest>事先不存在,它将会被自动创建,这包括其父目录路径
4:实例
    # vim Dockerfile
        #Description: test image
        FROM busybox
        LABEL maintainer="Evan <liangjindong0@qq.com>" app="httpd"
        COPY index.html /data/web/html/
    # 当前目录下,创建index.html: echo hello > index.html
    # docker build -t tinyhttpd:v0.1-1 ./
    # docker run --name tinyweb1 --rm tinyhttpd:v0.1-1 cat /data/web/html/index.html
    # cp -r /etc/yum.repos.d/ ./
    # vim Dockerfile
        #Description: test image
        FROM busybox
        LABEL maintainer="Evan <liangjindong0@qq.com>" app="httpd"
        COPY index.html /data/web/html/
        COPY yum.repos.d /etc/yum.repos.d/ (会把yum.repos.d下的所有文件copy到/etc/yum.repos.d/目录下,必须以/结尾)
    # docker build -t tinyhttpd:v0.1-2 ./
    # docker run --name tinyweb1 --rm tinyhttpd:v0.1-2 ls /etc/yum.repos.d/

ADD

1:定义
    ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径
2:语法
    • ADD <src> ... <dest> 或
    • ADD ["<src>",... "<dest>"]
3:操作准则
    - 同COPY指令
    - 如果<src>为URL且<dest>不以/结尾,则<src>指定的文件将被下载并直接被创建为<dest>;如果<dest>以/结尾,则文件名URL指定的文件将被直接下载并保存为<dest>/<filename>
    - 如果<src>是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于“tar -x”命令;然而,通过URL获取到的tar文件将不会自动展开;
    - 如果<src>有多个,或其间接或直接使用了通配符,则<dest>必须是一个以/结尾的目录路径;如果<dest>不以/结尾,则其被视作一个普通文件,<src>的内容将被直接写入到<dest>;
4:实例
    # vim Dockerfile
        ADD http://nginx.org/download/nginx-1.15.2.tar.gz /usr/local/src/
    # docker build -t tinyhttpd:v0.1-3 ./
    # docker run --name tinyweb1 --rm tinyhttpd:v0.1-3 ls /usr/local/src/  (只会上传,不会解压)
    # 如果nginx-1.15.2.tar.gz是本地文件,会在目标上解压

WORKDIR

1:定义
      用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指定设定工作目录
2: 语法
WORKDIR <dirpath>
    • 在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过,其是相对此前一个WORKDIR指令指定的路径
    • 另外,WORKDIR也可调用由ENV指定定义的变量
例如
    • WORKDIR /var/log
    • WORKDIR $STATEPATH

VOLUME

1: 定义
    用于在image中创建一个挂载点目录,以挂载Docker host上的卷或其它容器上的卷
2:语法
    • VOLUME <mountpoint> 或
    • VOLUME ["<mountpoint>"]
    如果挂载点目录路径下此前在文件存在,docker run命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中
3:实例
    # vim Dockerfile
        VOLUME /data/mysql/
        此volume是docker自动管理的类型
    # docker build -t tinyhttpd:v0.1-4 ./
    # docker run --name tinyweb1 --rm tinyhttpd:v0.1-4 sleep 60
    # docker inspect tinyweb1

EXPOSE

1:定义
    用于为容器打开指定要监听的端口以实现与外部通信
2:语法
    EXPOSE <port>[/<protocol>] [<port>[/<protocol>] ...]
        • <protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议
        • EXPOSE指令可一次指定多个端口,例如
            EXPOSE 11211/udp 11211/tcp (只是可以暴露,需要在docker run -P才会暴露在Dockerfile中定义的EXPOSE端口)
3:实例
# vim Dockerfile
    EXPOSE 80/tcp
# docker build -t tinyhttpd:v0.1-5 ./
# docker run --name tinyweb1 --rm tinyhttpd:v0.1-5 /bin/httpd -h /data/web/html
    只可以访问Docker IP的网页
# docker run --name tinyweb1 --rm -P tinyhttpd:v0.1-5 /bin/httpd -h /data/web/html
# docker port tinyweb1
    可以访问Docker宿主机IP的网页

ENV

1:定义
    用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用,调用格式为$variable_name或${variable_name}
2:语法
    • ENV <key> <value> 或
    • ENV <key>=<value> ...
    • 第一种格式中,<key>之后的所有内容均会被视作其<value>的组成部分,因此,一次只能设置一个变量
    • 第二种格式可用一次设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<value>中包含空格,可以以反斜线(\)进行转义,也可通过对<value>加引号进行标识;另外,反斜线也可用于续行
    • 定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能
3:实例
    # vim Dockerfile
        ENV DOC_ROOT=/data/web/html/ \
                WEB_SERVER_PACKAGE="nginx-1.15.2"
        COPY index.html ${DOC_ROOT:-/data/web/html/}
        ADD ${WEB_SERVER_PACKAGE}.tar.gz ./src/
    # image构建和启动是两个不同的过程,两个不同的阶段,Dockerfile中定义的EVN在启动时会注入到容器中
    # docker build -t tinyhttpd:v0.1-6 ./
    # docker run --name tinyweb1 --rm -e WEB_SERVER_PACKAGE="nginx-1.15.1" tinyhttpd:v0.1-6 printenv

RUN

1:定义
    用于指定docker build过程中运行的程序,其可以是任何命令
2:语法
    • RUN <command> 或
    • RUN ["<executable>", "<param1>", "<param2>"]
    • 第一种格式中,<command>通常是一个shell命令,且以“/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop container>命令停止容器时,此进程接收不到SIGTERM信号;
    • 第二种语法格式中的参数是一个JSON格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指定的命令不会“/bin/sh -c”来发起,以内核发起,不支持shell操作符如管道,重定向等。因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
            RUN ["/bin/bash", "-c", "<executable>", "<param1>"]
    • 注意:JSON数组中,要使用双引号
3:实例
# vim Dockerfile
    RUN    yum makecache &&\
                yum install nginx -y && \
                yum clean all
# 

CMD

1:定义
    类似于RUN指令,CMD指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同
        • RUN指令运行于映像文件构建过程中,而CMD指令运行于基于Dockerfile构建出的新映像文件启动一个容器时
        • CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD指定的命令其可以被docker run的命令行选项所覆盖
        • 在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效
2:语法
    • CMD <command> 或
    • CMD [“<executable>”, “<param1>”, “<param2>”] 或
    • CMD ["<param1>","<param2>"]
    • 前两种语法格式的意义同RUN
    • 第三种则用于为ENTRYPOINT指令提供默认参数
3:实例
    - 第一种格式:以shell启动

    # vim Dockerfile
        FROM busybox
        LABEL maintainer="Evan <liangjindong0@qq.com>" app="httpd"

        ENV WEB_DOC_ROOT="/data/web/html/"
        RUN mkdir -p ${WEB_DOC_ROOT} && \
        echo '<h1>Busybox httpd server.</h1>' > ${WEB_DOC_ROOT}/index.html

        CMD /bin/httpd -f -h ${WEB_DOC_ROOT}
    # docker build -t tinyhttpd:v0.1-7 ./
    # docker run --name tinyweb1 --rm -it tinyhttpd:v0.1-7 ()
    # docker ps (启动为/bin/sh的子进程)
         "/bin/sh -c '/bin/ht…" 
    #  docker exec -it tinyweb1 /bin/sh (shell中exec COMMAND,exec取代shell为1的进程,shell退出)
         / # ps
            PID   USER     TIME  COMMAND
            1 root      0:00 /bin/httpd -f -h /data/web/html/ ()
            6 root      0:00 /bin/sh
           11 root      0:00 ps
    - 第二种格式:不是以shell启动,可以指定用shell
    # vim Dockerfile
        CMD ["/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
    # docker build -t tinyhttpd:v0.1-8 ./
    # docker run --name tinyweb1 --rm -it tinyhttpd:v0.1-8 
        httpd: can't change directory to ' ${WEB_DOC_ROOT}': No such file or directory (不是以shell启动,所以无法识别${WEB_DOC_ROOT}'变量)
    指定用shell启动
    # vim Dockerfile
        CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
    # docker build -t tinyhttpd:v0.1-9 ./
    
    - 第三种格式: 带ENTRYPOINT
       # vim Dockerfile
        CMD ["/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
        ENTRYPOINT /bin/sh
        (CMD的命令会作为ENTRYPOINT的参数传递)
        #  docker run --name tinyweb1 --rm -it tinyhttpd:v1.4
        # docker inspect tinyweb1
            "Cmd": [
            "/bin/httpd",
            "-f",
            "-h ${WEB_DOC_ROOT}"
            ],
        "ArgsEscaped": true,
        "Image": "tinyhttpd:v1.4",
        "Volumes": null,
        "WorkingDir": "",
        "Entrypoint": [
            "/bin/sh",
            "-c",
            "/bin/sh"
            ],
        解释:用/bin/sh -c运行/bin/sh

        ENTRYPOINT ["/bin/sh","-c"]
         "Cmd": [
            "/bin/httpd",
            "-f",
            "-h ${WEB_DOC_ROOT}"
        ],
        "ArgsEscaped": true,
        "Image": "sha256:ba8350e53c013997d443f1f5ee68083a906b76a35e771fe89cf9426947ebc015",
        "Volumes": null,
        "WorkingDir": "",
        "Entrypoint": [
            "/bin/sh",
            "-c"
        ],

    # docker run --name tinyweb1 --rm -it tinyhttpd:v1.6 "ls /data" (能执行,CMD作为默认参数时传给ENTRYPOINT,但是docker run运行时传的参数会传给ENTRYPOINT,ls命令作为shell的子进程运行,此时docker run传的参数覆盖的是CMD参数)

注意:RUN基于基础image中的命令;RUN可以运行多次,同样属性的命令用一行描述
            docker run中CMD只能有一个
            运行的时间点不同,运行的行为不同

ENTRYPOINT

1:定义
    • 类似CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序
    • 与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序
    • 另外,docker run命令的--entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序
2:语法
        • ENTRYPOINT <command>
        • ENTRYPOINT ["<executable>", "<param1>", "<param2>"]
        • docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后做为其参数使用
        • Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效
3:实例
    # vim DockerfileENTRYPOINT /bin/httpd -f -h ${WEB_DOC_ROOT}
    # docker run --name tinyweb1 --rm -it tinyhttpd:v1.3
    # docker run --name tinyweb1 --rm -it tinyhttpd:v1.3 ls /data/web/html (ls /data/web/html不会覆盖ENTRYPOINT定义的)
    # docker run --name tinyweb1 --rm -it --entrypoint "ls /data/web/html" tinyhttpd:v1.3 (可以用--entrypoint来强制覆盖)
4:应用场景
   多数情况下,ENTRYPOINT是用来指定一个shell,作为用来启动别的进程的父进程,docker run的命令行参数会作为它的子进程来运行,达到灵活传递参数并且被shell解析
    空器接授变量需要通过环境变量
5:实例(Nginx接授变量生成配置文件,而且变量可以在启动容器时传递)
    # vim entrypoint.sh
        #!/bin/sh
        #
        cat > /etc/nginx/conf.d/www.conf << EOF
        server {
        server_name ${HOSTNAME};
        listen ${IP:-0.0.0.0}:${PORT:-80};
        root ${NGX_DOC_ROOT:-/user/share/nginx/html};
        }
        EOF
        exec "$@"
    # chmod +x entrypoint.sh
    # vim Dockerfile
        FROM nginx:1.14-alpine
        LABEL maintainer="Evan <liangjindong0@qq.com"
        ENV NGX_DOC_ROOT="/data/web/html/"
        ADD entrypoint.sh /bin/
        CMD ["/usr/sbin/nginx","-g","daemon off;"] (前台运行Nginx)
        ENTRYPOINT  ["/bin/entrypoint.sh"] (首先用此脚本会以shell运行初始化一个配置文件,然后运行CMD,顶替entrypoint.sh,即Nginx进行运行后,shell脚本退出,Nginx成为容器中的唯一进程,而且是主进程)
    # docker run --name tinyweb1 --rm -P -e "PORT=8080" -h Apache tinyhttpd:v1.8
    # docker run --name tinyweb1 --rm -p 80:80  -h Apache tinyhttpd:v1.8
    # docker exec -it tinyweb1 /bin/sh
    # / # netstat -tnl (port参数被docker run启动时指定参数8080成功)
        tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN
        tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
    # / # ps
        PID   USER     TIME   COMMAND
        1 root       0:00 nginx: master process /usr/sbin/nginx -g daemon off; (Nginx能以PID为1的主进程运行,是因为entrypoint.sh脚本中的exec "$@")
        8 nginx      0:00 nginx: worker process
        9 root       0:00 /bin/sh
        总结:ENTRYPOINT脚本来定义参数,在创建image时传入值,在docker run时接收参数来启动容器
posted @ 2018-08-03 18:06  Evan_Wolf  阅读(233)  评论(0编辑  收藏  举报