RUN: 执行shell命令
    RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:shell和exec

    • shell 格式:RUN <command>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。

      • PS:&&的用处是,前一句命令执行成功才会执行之后的命令。
        1. RUN apt-get update \
        2. && apt-get install -y --no-install-recommends \
        3. bzip2 \
        4. g++ \
        5. git \
        6. graphviz \
        7. libgl1-mesa-glx \
        8. libhdf5-dev \
        9. openmpi-bin \
        10. wget \
        11. && rm -rf /var/lib/apt/lists/*
    • exec 格式:RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。

    Example:

    FROM debian:jessie
    RUN apt-get update
    RUN apt-get install -y gcc libc6-dev make
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
    RUN mkdir -p /usr/src/redis
    RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
    RUN make -C /usr/src/redis
    RUN make -C /usr/src/redis install
    

    如Example,一种常见的错误写法是,每一条shell命令都写一个RUN。
    Dockerfile 中每一个指令都会建立一层镜像,RUN 也不例外。每一个 RUN 的行为都会新建立一层镜像,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。
    而上面的这种写法,创建了 7 层镜像,这是没有意义的。而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生多层、臃肿的镜像,不仅仅增加了构建部署的时间,也很容易出错。

    Example2:

    FROM debian:jessie
    RUN buildDeps='gcc libc6-dev make' \
        && apt-get update \
        && apt-get install -y $buildDeps \
        && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.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
    

    首先,所有的命令只有一个目的,就是编译、安装可执行文件,没有必要建立多层镜像。因此,这里没有使用很多个 RUN 对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来,将之前的 7 层简化为了 1 层。写 Dockerfile 并不是在写 Shell 脚本,而是在定义每一层该如何构建。
    并且,这里还做了格式化。Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,有利于后续维护。
    此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt缓存文件。这是很重要的一步,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。制作出了很臃肿的镜像的原因之一,就是忘记了在每一层构建的最后清理掉无关文件。