Dockerfile指令详解&镜像构建实例说明

Dockerfile使用总结

  • Dockerfile是用来构建镜像的文本文件,里面包含了一条条用于构建镜像所需的指令和说明。
  • Dockerfiel文件中的每一层指令都是描述如何在上一层的基础上进行该层的构建。

操作环境

  • 阿里云 ubuntu 20.04

构建镜像的简单实例

  • 以ubuntu为例,构建一个简单的镜像:

    # 新建Dockerfile文件夹,并建文件Dockerfile
    $ mkdir Dockerfile
    $ cd Dockerfile
    $ vi Dockerfile
    

    在Dockerfile文件内写入:

    FROM ubuntu
    MAINTAINER test-user
    RUN echo "测试构建镜像" > /test.txt
    

    第一行指令:本镜像以ubuntu为基础构建;

    第二行指令:本镜像维护人员信息;

    第三行指令:在 docker build的过程中将“测试构建镜像”写入到test.txt文本文件中。

    构建镜像:

    $ docker build -t ubuntu:test .
    >>>
    Sending build context to Docker daemon  2.048kB
    Step 1/2 : FROM ubuntu
     ---> ba6acccedd29
    Step 2/2 : RUN echo "测试构建镜像" > /test.txt
     ---> Running in 3eea459154d9
    Removing intermediate container 3eea459154d9
     ---> 5279a6de02d5
    Successfully built 5279a6de02d5
    Successfully tagged ubuntu:test
    

    可以看到,在step2里面。RUN指令启动了容器 ba6acccedd29 ,运行了RUN指令的命令,之后删除了此镜像。

    查看镜像是否构建成功:

    $ docker run -it ubuntu:test /bin/bash
    >>>
    root@fa8a1142b131:/ cat test.txt 
    测试构建镜像
    

    镜像构建成功。

上下文路径

  • 上例中构建镜像的命令是:docker build -t ubuntu:test . ,-t属性是指明构建的镜像的仓库以及标签,.是指Dockerfile文件就在当前文件夹下,如果在根目录执行构建命令,则路径是 /Dockerfile,这个路径就是上下文路径,表示的是构建镜像需要的文件所处的路径,默认使用Dockerfile所处的路径。
  • 在构建镜像的过程中,如果有COPY指令,待复制的文件也放在上下文路径里面。
  • 上下文路径最好不要放与构建镜像无关的文件,因为Docker构建镜像的过程实际上发生在Docker引擎里面,在构建过程中这个路径下的文件都会打包给引擎,文件过多会导致构建过程缓慢。

指令含义详解

FROM

  • 定制的镜像都是基于FROM的镜像
  • DOcker有一个特殊的镜像 scratch,即空白镜像,如果FROM scratch,则意味着接下来的指令为镜像的第一层。

RUN

  • 每一个RUN指令都是新建一层,然后执行命令,并提交这一层的修改,构成新的镜像。

  • Dockerfile和shell脚本不同,每一个RUN指令都会构建一层,因此尽量将多个RUN指令的命令写在同一个RUN指令中,不同命令之间可用 \ 换行,用 &&连接,为了减少镜像的大小,在指令中需要删除额外不需要的内容,例如:

    FROM debian:stretch
    
    RUN set -x; buildDeps='gcc libc6-dev make wget' \
        && apt-get update \
        && apt-get install -y $buildDeps \
        && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
        && mkdir -p /usr/src/redis \
        && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
        && make -C /usr/src/redis \
        && make -C /usr/src/redis install \
        && rm -rf /var/lib/apt/lists/* \
        && rm redis.tar.gz \
        && rm -r /usr/src/redis \
        && apt-get purge -y --auto-remove $buildDeps
    

    首先指令的内容是编译、安装 redis 可执行文件,其次,可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。

  • 用于在构建过程中执行后续的命令,有两种格式:

#shell 格式
RUN echo "test" > ./test.txt
#exec格式
RUN ["echo", "test", "./test.txt"]

COPY

  • 从上下文路径中复制文件或目录到容器里的指定目录。
COPY <源路径> <目标路径>
  • 目标路径不需要提前建好,如果不存在,会自动创建

ADD

  • 功能和用法与COPY类似,官方推荐使用COPY。

  • 优点:在执行 <源文件> 是tar压缩文件的时候,文件格式是bzip\gzip2\xz时,会将源文件复制并解压到目标路径;

  • 缺点:在不解压的前提下无法复制tar压缩文件,会使镜像构建缓存失效,从而使镜像构建变得很缓慢。

CMD

  • 类似RUN指令,用于运行程序,但是两者的运行时间不一样:

  • RUN指令在构建镜像时运行,即docker build时运行;

  • CMD指令在以构建的镜像启动容器时运行,即docker run时运行。

  • CMD有三种写法:

# exec模式
CMD ["top"]
# shell模式
CMD top
# 第三种写法是为ENTRYPOINT指令指定的默认程序提供参数
CMD ["param1", "param2"]
  • CMD的exec模式和shell模式的不同在于容器启动时的1号进程不同,这个详解见"Dockerfile#CMD的exec模式和shell模式不同点.md"。

  • CMD指令的作用是:指定容器启动时默认要运行的程序,程序运行结束时容器也结束。不过docker run命令中指定的运行的程序可覆盖CMD指定的默认运行程序。

  • CMD的缺点在于如果在docker run的时候想给CMD指定运行的程序传入其他参数时,需要将CMD的指令从新输入一次,即参数不能传入给构建时编写的CMD指令。

#假设镜像ubuntu:test的Dockerfile中指定了默认程序是运行top
...
CMD ["top"]
...
#构建镜像ubuntu:test
#以ubuntu:test启动容器
docker run -it ubuntu:test /bin/bash

#容器运行后实际运行的是 /bin/bash 命令,而不是默认的 top 命令
  • 如果Dockerfile中有多个CMD指令,则只有最后一个指令生效。

ENTRYPOINT

  • 功能类似CMD,但是在未使用–entrypoint参数的情况下,docker run命令行指令的指令不会像对CMD指令那样覆盖ENTRYPOINT指令,而是将docker run指定的参数及命令作为参数传给ENTRYPOINT指令。

  • 如果Dockerfile里面有ENTRYPOINT,那么CMD指令内容将作为ENTRYPOINT指令的参数。

  • docker run --entrypoint <command> inamge_name,–entrypoint后面的command会覆盖ENTRYPOINT指令。

  • 优点:不同于docker run时想修改CMD指令只能重新在docker run后面重新制定CMD指令指定的命令,ENTRYPOINT指令支持在docker run时给ENTRYPOINT指令传入参数。

  • 如果Dockerfile中有多个ENTRYPOINT,那么只有最后一个会生效。

  • 编写Dockerfile文件:

    FROM ubuntu
    MAINTAINER klw
    RUN ["echo", "测试镜像",">","./test.txt"]
    ENTRYPOINT ["echo", "Hello"]
    CMD ["World"]
    

    构建测试镜像 test/ubuntu:v1

    $ docker build -t test/ubuntu:v1 .
    >>>
    Sending build context to Docker daemon  2.048kB
    Step 1/5 : FROM ubuntu
     ---> ba6acccedd29
    Step 2/5 : MAINTAINER klw
     ---> Running in df34ca2e01c0
    Removing intermediate container df34ca2e01c0
     ---> 9c154c5d6c72
    Step 3/5 : RUN ["echo", "测试镜像",">","./test.txt"]
     ---> Running in 66a7bb885da8
    测试镜像 > ./test.txt
    Removing intermediate container 66a7bb885da8
     ---> 2f2b89bf850c
    Step 4/5 : ENTRYPOINT ["echo", "Hello"]
     ---> Running in 8fd14a3ae197
    Removing intermediate container 8fd14a3ae197
     ---> 4cb474df91b5
    Step 5/5 : CMD ["World"]
     ---> Running in 5b2381e460b9
    Removing intermediate container 5b2381e460b9
     ---> b26e8bcc53aa
    Successfully built b26e8bcc53aa
    Successfully tagged test/ubuntu:v1
    
    $ docker images
    >>>
    REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
    test/ubuntu         v1                  b26e8bcc53aa        About a minute ago   72.8MB
    

    运行测试镜像:

    $ docker run -it test/ubuntu:v1
    >>>
    Hello World
    

    此时在镜像构建结束后,直接运行容器时,默认运行ENTRYPOINT指令,此时CMD指令是作为ENTRYPOINT指令的参数,即运行的命令是 echo "Hello World"

    启动容器时加上–entrypoint覆盖ENTRYPOINT指令:

    $ docker run -it --entrypoint hostname test/ubuntu:v1 -f
    >>>
    658aad538c50
    

    –entrypoint 指定的是命令而不是命令行,即是 hostname 而不是 hostaname -f ,如果需要为–entrypoint之后的命令添加参数,参数放在docker run的最后面。

ENV

  • ENV baidu "www.baidu.com"
    ENV baidu="www.baidu.com"
    
  • 作用是设置环境变量,处于ENC指令之后的其他指令,如RUN,或者应用程序都可以访问这里定义的环境变量。

  • Dockerfile文件:

    FROM ubuntu
    MAINTAINER klw
    ENV c curl
    RUN apt-get update && apt-get -y install $c
    CMD ["curl", "www.baidu.com"]
    

    构建镜像:

    $ docker build -t test/ubuntu:v1 .
    >>>
    Sending build context to Docker daemon  2.048kB
    Step 1/5 : FROM ubuntu
     ---> ba6acccedd29
    Step 2/5 : MAINTAINER klw
     ---> Running in 8d0cc79cad40
    Removing intermediate container 8d0cc79cad40
     ---> 1b122061de5d
    Step 3/5 : ENV c curl
     ---> Running in 5c889cfc2b9f
    Removing intermediate container 5c889cfc2b9f
     ---> f9c354eb3023
    Step 4/5 : RUN apt-get update && apt-get -y install $c
     ---> Running in 2b3c1e2fd480
    Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
    Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
    ...
    Unpacking curl (7.68.0-1ubuntu2.7)
    ...
    Removing intermediate container 2b3c1e2fd480
     ---> 1db52424a5ff
    Step 5/5 : CMD ["curl", "www.baidu.com"]
     ---> Running in dabb0264466d
    Removing intermediate container dabb0264466d
     ---> 38c76ce0b788
    Successfully built 38c76ce0b788
    Successfully tagged test/ubuntu:v1
    

    可见RUN指令在运行时识别到了设置的环境变量 ENV c curl。

    启动容器:

    $ docker run -it --rm --name test test/ubuntu:v1
    >>>
    <!DOCTYPE html>
    <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
    

ARG

  • 作用与ENV相同都是设置环境变量,不过ARG设置的环境变量只在Dockfile内有效,即在docker build命令运行期间生效。

  • ARG baidu "baidu.com"
    

VOLUME

  • 作用是定义匿名数据卷,在启动容器时如果忘记挂载数据卷,会自动挂载到匿名数据卷。

  • 容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。为了防止在启动容器时忘记挂载数据卷,可以在Dockerfile中使用VOLUME将某些路径设为默认匿名数据卷,即使启动容器时未指定数据卷也不会有影响,不会向容器的存储层写入大量数据。

  • 格式:

    VOLUME <路径>
    VOLUME ["路径1", "路径2"]
    

EXPOSE

  • 作用是暴露端口.

  • EXPOSE仅仅是一个声明,即表明容器运行时通过哪个端口向外部网络提供服务,容器运行时并不会因为这个声明就开启这个端口的服务。如果需要暴露EXPOSE指定的端口给外界,可以在docker run -p <宿主机端口>:<容器端口> 来指定将容器的端口暴露给宿主机的端口,此时可通过此端口来访问容器的服务。

  • 优点:1.便于镜像开发人员理解这个镜像服务守护的端口;

    ​ 2.在 docker run -P会将EXPOSE的端口随即映射到宿主机上面。

  • 格式:

    EXPOSE <端口1> [<端口2>...]
    
  • 区分EXPOSE和运行时的 -p <宿主机端口>:<镜像端口>,-p是映射容器端口与宿主机端口,也就是将容器对应的端口公开给外界访问,而EXPOSE仅仅是声明镜像服务打算使用该端口作为镜像服务的端口,并不会自动在宿主机进行映射。

WORKDIR

  • 作用是指定工作目录。

  • 使用WORKDIR指令可以来指定工作目录,或者说当前目录,以后各层的工作目录就改为指令指定的目录,如果该目录不存在,WORKDIR会建立此目录。

  • Dockerfile的编写不同于shell,由于shell运行时,脚本内所有的语句都是在同一个进程执行环境,语句之间相互影响;而Dockerfile中,每一行指令都是一个单独的容器在执行(容器的实质是进程),每一行指令仅仅改变的是当前进程的变化,下一行指令修改的一个全新的容器(进程)的内存,和上一个指令的容器(进程)没有关系。

    错误的写法

    #此时在容器1(进程1),RUN指令修改了容器1内的工作目录变更
    RUN cd /app
    #此时在一个新的容器2(进程2),并不会继承上一层的工作目录变更
    RUN echo "test" > test.txt
    

    正确的写法

    #此时在容器1
    WORKDIR /app
    #此时在容器2,由于WORKDIR已经指定了工作目录,此时RUN指令运行在 /app 路径下。
    RUN echo "test" > test.txt
    
  • 如果WORKDIR使用的是相对路径,那么切换的路径与之前的WORKDIR有关:

    WORKDIR /app
    WORKDIR b
    WORKDIR c
    RUN pwd
    

    RUN pwd的工作目录是 /app/b/c

USER

  • 格式:

    USER <用户名>[:<用户组>]
    
  • 作用和WORKDIR类似,不过WORKDIR是改变之后层的工作目录,USER是改变之后层指令的执行人。

  • 如果在指令执行过程中需要由root用户切换至其他用户,不要使用sudo su,因为需要很麻烦的配置,建议使用 gosu:

    # 下载 gosu
    RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.12/gosu-amd64" \
        && chmod +x /usr/local/bin/gosu \
        && gosu nobody true
    

HEALTHCHECK

  • 格式:

    #如果FROM的基础镜像有健康检查指令,可以用这一行屏蔽基础镜像的健康检查指令
    HEALTHCHECK NONE
    
    #设置容器健康检查的指令
    HEALTHCHECK [option] CMD <命令>
    

    option有三项:

    • --interval=<时间间隔> :两次健康检查的时间间隔,默认为30s。
    • --retries=<次数> : 当连续失败指定次数后,容器状态判定为 unhealthy,默认次数3次。
    • --timeout=<时长> :健康检查指令运行超时时间,如果健康检查指令耗时超过这个时长,则本次健康检查视为失败,默认时长30s。

    CMD 后面是健康检查的命令,返回值是:0 健康;1 不健康;2 保留,不要使用这个值。

  • 作用是告诉容器健康检查的指令,用于判断此容器是否正常运行。

  • 当一个镜像制定了 HEALTHCHECK 指令后,用这个镜像启动容器,容器的初始状态是 starting,如果健康检查指令通过,则容器状态变更为 healthy;连续失败指定次数后,容器状态变更为 unhealthy

ONBUILD

  • 格式:

    ONBUILd <其他指令>
    

    例如 ONBUILD RUN apr-get update && apt-get insatll curl

  • ONBUILD的指令并不在使用它所处的Dockerfile构建镜像的过程中生效,而是在以Dockerfile构建的镜像为基础镜像构建镜像的过程中生效。

LABEL

  • 格式:

    LABEL <key>=<value>
    
  • LABEL用于以键值对的形式给镜像添加一些元数据,例如给镜像添加作者信息:

    LABEL org.opencontainers.image.authors="test-user"
    

SHELL

  • 格式:

    SHELL ["bin/sh", "-c"]
    

    在Linux里面,SHELL指令默认是 ["bin/sh", "-c"]

  • 作用是指定RUN CMD ENRTRYPOINT指令shell模式下运行的shell。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值