Dockerfile参考(13) – VOLUME创建数据卷和USER指定容器内运行命令的用户

VOLUME

  1. VOLUME ["/data"]

VOLUME指令创建一个指定名称的挂载点,并设置此挂载点为本地主机或其它容器的外部挂载数据卷。VOLUME的值可以是一个JSON数组,如VOLUME [“/var/log/”],或者多个参数的纯字符串,如VOLUME /var/log或VOLUME /var/log /var/db。
docker run把在base镜像内指定路径已存在的数据复制到这个新创建的数据卷。例如:

  1. FROM ubuntu
  2. RUN mkdir /myvol
  3. RUN echo "hello world" > /myvol/greeting
  4. VOLUME /myvol

这个Dockerfile使得docker run创建一个新的挂载点/myvol,并复制greeting文件到新创建的数据卷。

注意:如果在volume声明之后的任何指令对其挂载点更改了数据,这些更改将会撤消。

USER

  1. USER daemon

USER指令设置运行镜像时使用的用户名或UID,以及Dockerfile中的任何RUN,CMD和ENTRYPOINT指令。

Dockerfile参考(12) – ENTRYPOINT配置创建容器后执行的容器命令

ENTRYPOINT有两种形式:

  • ENTRYPOINT [“executable”, “param1”, “param2”] [exec形式,首选]
  • ENTRYPOINT command param1 param2 [shell形式]
  • ENTRYPOINT指令用来配置创建容器时执行的容器命令。
    例如,下面的示例将以默认页启动nginx,在80端口监听:

    1. docker run -i -t –rm -p 80:80 nginx

    docker run [image]的命令行参数会附加到exec形式ENTRYPOINT所有元素的后面,且会覆盖使用CMD指定的所有元素。这允许命令行参数能够传递到entry point。如docker run [image] -d会传递-d参数到entry point。你可以你使用docker run –entrypoint标志来覆盖ENTRYPOINT指令。
    shell形式的ENTRYPOINT能够阻止使用任何CMD或run命令行参数,不过不好的地方是ENTRYPOINT将以/bin/sh -c的子命令执行,且ENTRYPOINT收不到信号。意味着执行的命令的PID不是容器的PID 1 – 收不到来自主机的Unix信号 – 所以无法使用docker stop [container]停止容器。
    仅是Dockerfile的最后一个ENTRYPOINT指令起作用。

    exec形式ENTRYPOINT示例

    你可以使用ENTRYPOINT的exec形式设置稳定的默认命令和参数,然后使用同样形式的CMD设置额外的可以修改的默认参数。

    1. FROM ubuntu
    2. ENTRYPOINT ["top", "-b"]
    3. CMD ["-c"]

    当你运行容器,你会看到top是唯一的进程:

    1. $ docker run -it –rm –name test  top -H
    2. top – 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
    3. Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
    4. %Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    5. KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
    6. KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem
    7.  
    8.   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    9.     1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top

    我们再看到top的具体参数:

    1. $ docker exec -it test ps aux
    2. USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    3. root         1  2.6  0.1  19752  2352 ?        Ss+  08:24   0:00 top -b -H
    4. root         7  0.0  0.1  15572  2164 ?        R+   08:25   0:00 ps aux

    并且可以使用docker stop test来停止这个容器。
    下面的Dockerfile显示了使用ENTRYPOINT来在前台运行Apache[作为PID 1运行]:

    1. FROM debian:stable
    2. RUN apt-get update && apt-get install -y –force-yes apache2
    3. EXPOSE 80 443
    4. VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
    5. ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

    如果你需要为单个可执行程序编辑一个启动脚本,你可以使用exec和gosu命令来确保最终的执行程序能收到Unix信号:

    1. #!/bin/bash
    2. set -e
    3.  
    4. if [ "$1" = ‘postgres’ ]; then
    5.     chown -R postgres "$PGDATA"
    6.  
    7.     if [ -z "$(ls -A "$PGDATA")" ]; then
    8.         gosu postgres initdb
    9.     fi
    10.  
    11.     exec gosu postgres "$@"
    12. fi
    13.  
    14. exec "$@"

    最后,如果你需要在关闭或协调多个可执行程序时做些额外的清理工作,你需要确保ENTRPOINT脚本能够收到Unix信号,捕获它们并做些额外的工作:

    1. #!/bin/sh
    2. # Note: I’ve written this using sh so it works in the busybox container too
    3.  
    4. # USE the trap if you need to also do manual cleanup after the service is stopped,
    5. #     or need to start multiple services in the one container
    6. trap "echo TRAPed signal" HUP INT QUIT TERM
    7.  
    8. # start service in background here
    9. /usr/sbin/apachectl start
    10.  
    11. echo "[hit enter key to exit] or run ‘docker stop <container>’"
    12. read
    13.  
    14. # stop service and clean up here
    15. echo "stopping apache"
    16. /usr/sbin/apachectl stop
    17.  
    18. echo "exited $0"

    如果使用docker run -it –rm -p 80:80 –name test apache运行镜像,可以用docker exec或docker top来检查容器进程,然后请求脚本停止Apache:

    1. $ docker exec -it test ps aux
    2. USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    3. root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
    4. root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
    5. www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
    6. www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
    7. root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux
    8. $ docker top test
    9. PID                 USER                COMMAND
    10. 10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
    11. 10054               root                /usr/sbin/apache2 -k start
    12. 10055               33                  /usr/sbin/apache2 -k start
    13. 10056               33                  /usr/sbin/apache2 -k start
    14. $ /usr/bin/time docker stop test
    15. test
    16. real    0m 0.27s
    17. user    0m 0.03s
    18. sys 0m 0.03s

    注意:你可以使用–entrypoint来覆盖ENTRPOINT的设置,不过仅可以二进制到exec,如/usr/sbin/apache2 -k start[不仅调用sh -c执行]

    shell形式ENTRYPOINT示例

    可以为ENTRYPOINT指定一个纯字符串,它将运行在/bin/sh -c。这种形式将使用shell处理shell环境变量替换,将忽略任何的CMD或docker run命令行参数。为了确保docker stop能够正确发送信号到ENTRPOINT的可执行程序,记得使用exec执行:

    1. FROM ubuntu
    2. ENTRYPOINT exec top -b

    运行这个镜像后,将会看到这个进程的PID为1:

    1. $ docker run -it –rm –name test top
    2. Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
    3. CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
    4. Load average: 0.08 0.03 0.05 2/98 6
    5.   PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    6.     1     0 root     R     3164   0%   0% top -b

    且可以使用docker stop停止进程:

    1. $ /usr/bin/time docker stop test
    2. test
    3. real    0m 0.20s
    4. user    0m 0.02s
    5. sys 0m 0.04s

    如果忘记使用exec执行命令:

    1. FROM ubuntu
    2. ENTRYPOINT top -b
    3. CMD –ignored-param1

    然后运行:

    1. $ docker run -it –name test top –ignored-param2
    2. Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
    3. CPU:   9% usr   2% sys   0% nic  88% idle   0% io   0% irq   0% sirq
    4. Load average: 0.01 0.02 0.05 2/101 7
    5.   PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    6.     1     0 root     S     3168   0%   0% /bin/sh -c top -b cmd cmd2
    7.     7     1 root     R     3164   0%   0% top -b

    可以看到top进程的PID为是1了。
    并且使用docker stop test不能停止进程:

    1. $ docker exec -it test ps aux
    2. PID   USER     COMMAND
    3.     1 root     /bin/sh -c top -b cmd cmd2
    4.     7 root     top -b
    5.     8 root     ps aux
    6. $ /usr/bin/time docker stop test
    7. test
    8. real    0m 10.19s
    9. user    0m 0.04s
    10. sys 0m 0.03s

    ENTRYPOINT和CMD使用建议

    1.Dockerfile应该至少指定一个CMD或ENTRYPOINT指令。
    2.当使用容器作为可执行程序时,应该定义ENTRYPOINT。
    3.CMD应该用来设置ENTRYPOINT的默认参数。
    4.当运行容器带有额外参数时,将会覆盖CMD的参数。

    Dockerfile参考(11) – ADD COPY添加文件和目录到镜像

    ADD有两种形式:

  • ADD <src>… <dest>
  • ADD [“<src>”,… “<dest>”] [这种形式通常用于包含空白字符的路径]
  • ADD指令从<src>复制新的文件,目录或远程文件URLs并添加到镜像文件系统的<dest>路径。
    可以指定多个<src>资源,这些资源必须是相对于正在构建的源目录(构建的上下文)。
    每个<src>可以包含通配符并且使用Go的filepath.Match规则匹配。例如:

    1. ADD hom* /mydir/        # adds all files starting with "hom"
    2. ADD hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"

    <dest>是一个绝对路径,或者是WORKDIR的相对路径。

    1. ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
    2. ADD test /absoluteDir/         # adds "test" to /absoluteDir/

    所有新创建的文件和目录的UID和GID都是0.
    当<src>为一个远程文件URL时,新创建的文件和目录权限为600。如果获取的远程文件的HTTP有一个Last-Modified响应头,那么这个响应头的时间戳将设置为新创建的文件和目录的mtime。不过,在ADD期间像其它任意文件的处理过程中,mtime不能决定文件是否已经更改和缓存是否应该更新。

    注意:如果通过标准输入传递Dockerfile构建镜像[docker build – < somefile],那么就没有构建上下文,所以Dockerfile中的ADD指令的<src>只能是远程URL。你也可以通过标准输入传递一个压缩文件:(docker build – < archive.tar.gz),在这个压缩文件根目录的Dockerfile和压缩文件的其它文件将作为构建的上下文使用。

    如果远程URL需要验证,你需要使用RUN wget,RUN curl或容器内的其它工具下载文件,因为ADD指令不支持验证功能。

    如果<src>的文件已经更改,那么涉及到这个路径的第一个ADD指令将使Dockerfile后面的所有指令缓存失效。其中RUN指令的缓存也将失效。

    ADD遵守如下规则:

  • <src>路径必须在构建上下文内;你不能ADD ../something /something,因为docker build的第一步已经把上下文目录和子目录发送到docker daemon了。
  • 如果<src>是一个远程URL,且<dest>路径后没有跟斜杠,那么这个文件从远程URL下载并复制到<dest>
  • 如果<src>是一个远程URL,且<dest>路径后有斜杠,那么filename从URL获取并把文件下载到<dest>/<filename>。例如,ADD http://example.com/foobar /将创建文件/foobar。远程URL是包括路径的URL,这样docker才能获取文件名,像http://example.com是不行的。
  • 如果<src>是一个目录,目录的所有内容都将复制,包括文件系统元数据。
  • 注意:不会复制目录本身,只是它的内容。

  • 如果<src>是一个本地的可识别的tar压缩文件[如gzip,bzip2或xz],那么将在容器内解压为目录。远程URL的压缩文件将不会解压。
  • 如果<src>是多个资源,不管是直接指定或使用通配符,那么<dest>必须是一个目录,并且已经以斜杠/结尾。
  • 如果<dest>不以斜杠/结尾,将视其为一个普通文件,<src>的内容将写到这个文件。
  • 如果<dest>不存在,则会与其路径中的所有缺少的目录一起创建。
  • 注意:ADD与COPY唯一的区别就是COPY添加本地压缩文件时不会自动解压到容器。对于其它不需要自动解压文件的情况,你应该始终使用COPY。

    Dockerfile参考(10) – EXPOSE暴露端口和ENV设置环境变量

    EXPOSE

    1. EXPOSE <port> [<port>…]

    EXPOSE指令通知Docker,容器运行期间监听指定的网络端口。EXPOSE不会自动使主机的容器端口可访问。要设置监听EXPOSE指定的端口,必须使用-p参数来发布一组端口或-P参数来发布所有EXPOSE的端口。你可以暴露容器的一个端口并在外部发布另一个不同的端口。比如发布容器的80端口到主机的8080端口。

    ENV

    1. ENV <key> <value>
    2. ENV <key>=<value> …

    ENV指令设置环境变量的值为。这个值可以用在之后的Dockerfile命令中并替换命令中所有的环境变量。
    ENV指令有两种形式,第一种是 ,将设置单个变量为一个值。第一个空格之后的整个字符将设置为这个变量的值 – 包括空格和引号等字符。
    第二种形式允许同时设置多个变量。设置这种形式使用了等于号,第一种是没有使用的。像命令行解析,引号和反斜杠可以用来包括空格。
    例如:

    1. ENV myName="John Doe" myDog=Rex The Dog
    2.     myCat=fluffy

    和:

    1. ENV myName John Doe
    2. ENV myDog Rex The Dog
    3. ENV myCat fluffy

    虽然这两种形式得到的结果一样,但推荐第一种,因为它只生成了一个缓存层。
    使用ENV设置环境变量将永久存在于从该镜像运行的容器中。你可以使用docker inspect查看这些值,也可以使用

    1. docker run –env <key>=<value>

    更改它们。

    注意:环境变量的存在可能引起意外的副作用。例如,设置ENV DEBIAN_FRONTEND noninteractive可能会使在基于Debian镜像的apt-get用户感到困惑。要在单个命令中使用变量,可以使用

    1. RUN <key>=<value> <command>

    Dockerfile参考(9) – LABEL添加元数据到镜像

    格式:

    1. LABEL <key>=<value> <key>=<value> <key>=<value> …

    LABEL指令添加元数据到一个镜像。一个LABEL是一个键值对。要在LABEL值中包含空格,使用双引号和反斜杠,就像在命令行解析中一样。一些可用的示例:

    1. LABEL "com.example.vendor"="ACME Incorporated"
    2. LABEL com.example.label-with-value="foo"
    3. LABEL version="1.0"
    4. LABEL description="This text illustrates
    5. that label-values can span multiple lines."

    一个镜像可以有多个label。要指定多个labels,Docker推荐尽可能地把多个labels合并到一个LABEL指令中去。每一个LABEL指令会生成一个新的镜像层,如果你使用多个label,将导致构建出一个低效的镜像。这个示例只生成单个镜像层。

    1. LABEL multi.label1="value1" multi.label2="value2" other="value3"

    上面的示例可以重写为:

    1. LABEL multi.label1="value1"
    2.       multi.label2="value2"
    3.       other="value3"

    label是累积的,包括FROM镜像的lable。如果Docker遇到一个label/key已经存在,那么新的值将覆盖这个label/key。
    要查看一个镜像的label,使用docker inspect命令。

    1. "Labels": {
    2.     "com.example.vendor": "ACME Incorporated"
    3.     "com.example.label-with-value": "foo",
    4.     "version": "1.0",
    5.     "description": "This text illustrates that label-values can span multiple lines.",
    6.     "multi.label1": "value1",
    7.     "multi.label2": "value2",
    8.     "other": "value3"
    9. },

    Dockerfile参考(8) – CMD设置运行容器时执行的命令

    CMD指令有三种形式:

  • CMD [“executable”,”param1″,”param2″] [exec形式,这是首选形式]
  • CMD [“param1″,”param2”] [作为ENTRYPIOINT的默认参数]
  • CMD command param1 param2 [shell形式]
  • Dockerfile只能使用一个CMD指令。如果你使用了多个CMD那么Docker仅使用最后一个。
    CMD的主要作用是为执行容器提供默认值。这些默认值可以包括一个可执行程序,或省略可执行程序(这种情况下需要指定一个ENTRYPOINT指令)。

    注意:如果CMD用来提供ENTRYPOINT的默认参数,那么CMD和ENTRYPOINT指令应该使用JSON数组的格式。

    注意:exec形式使用了JSON数组,意味着字符外使用双引号而不是单引号。

    不论使用是的shell或exec格式,CMD指令设置了当运行镜像时要执行的命令。
    如果使用CMD的shell形式,那么将在/bin/sh -c执行:

    1. FROM ubuntu
    2. CMD echo "This is a test." | wc –

    如果你想不调用shell运行你的,那么你必须以JSON数组表示你的命令并给出这个命令的完整路径。这个数组形式是CMD的首先格式。额外的参数必须单独地使用一个数组的字符串表示:

    1. FROM ubuntu
    2. CMD ["/usr/bin/wc","–help"]

    如果你想每次容器都是执行相同的执行程序,那么你应该考虑ENTRYPOINT和CMD一起使用。
    如果用户在docker run指定参数,那么将会覆盖CMD指定的默认参数。

    注意:不要把RUN和CMD搞混了。RUN实际上是在构建镜像时执行命令并提交执行结果到镜像;而CMD在构建期间不会执行任何命令,只是为镜像预先设置运行时容器时运行的命令或参数。

    Dockerfile参考(7) – RUN执行命令指令

    RUN指令有两种形式:

  • RUN [shell形式,运行在shell的命令,默认是Linux上的/bin/sh -c或windows的cmd /S /C]
  • RUN [“executable”, “param1”, “param2”][exec形式]
  • RUN指令将在当前镜像层上面的一个新数据层执行命令并提交其结果。这个新提交的镜像层将用于Dockerfile的下一步。
    分层RUN指令和生成提交符合Docker的核心概念,其中提交成本低,可以从镜像历史中的任何点创建容器,就像源代码控制一样。
    exec形式避免了shell字符的模糊处理,并且可以在没有特定shell的base镜像上使用RUN指令。
    shell形式中默认的shell可以使用SHELL命令更改。
    在shell形式中你可以使用反斜杠在下一行继续这个RUN指令。例如下面两行:

    1. RUN /bin/bash -c ‘source $HOME/.bashrc;
    2. echo $HOME’

    两行相当于:

    1. RUN /bin/bash -c ‘source $HOME/.bashrc; echo $HOME’

    注意:要在exec形式中使用不同的shell,而不是/bin/sh,传递想用的shell即可,如,[“/bin/bash”, “-c”, “echo hello”]

    注意:exec形式是接一个JSON数组,意味着在单词之外使用双引号而不是单引号。

    注意:不像shell形式,exec形式不会调用一个shell再执行命令。意味着常规shell处理不会发生。例如,RUN [ “echo”, “$HOME” ]不会替换$HOME为家目录。如果你想处理此情况,可以直接使用shell形式或直接执行一个shell。如RUN [ “sh”, “-c”, “echo $HOME” ]。

    注意:在JSON格式中,有必要转义反斜杠,特别是windows中使用反斜杠作为路径分隔符的情况。[“c:windowssystem32tasklist.exe”] 会视为shell形式,因为这不是一个有效的JSON格式,正确的语法是RUN [“c:\windows\system32\tasklist.exe”]。

    RUN指令的缓存不会在下一次构建镜像时自动失效。一个指令的缓存如RUN apt-get dist-upgrade -y会在下一次构建中重用。可以使用–no-cache参数来使RUN指令的缓存失效,例如docker build –no-cache。

    Dockerfile参考(6) – FROM和MAINTAINER指令

    FROM

    1. FROM <image>

    1. FROM <image>:<tag>

    1. FROM <image>@<digest>

    FROM指令为随后的指令设置一个Base Image。因此,一个有效的Dockerfile的第一个指令必须是FROM。镜像可以是任意有效的镜像 – 这个很容易从公共仓库拉取一个镜像。

  • FROM必须是Dockerfile中的首个非注释指令。
  • FROM可以在单个Dockerfile中出现多次,这样可以创建多个镜像。只需记下在每个新的FROM命令之前由提交输出的最后一个图像ID。
  • tag或digest值是可选的。如果都不指定,那么默认是latest。如果找不到tag的值将返回错误。
  • MAINTAINER

    1. MAINTAINER <name>

    MAINTAINER指令允许你设置生成这个镜像的作者。

    Dockerfile参考(5) – .dockerignore文件

    在docker CLi发送上下文到docker daemon之前,它首先先在上下文的根目录查找名为.dockerignore的文件。如果这个文件存在,CLI则更改上下文来排除与这文件里的模式匹配的文件和目录。这帮助避免了发送不必要的大的或敏感的文件和目录到daemon,以及有可能使用ADD或COPY把不必要的文件添加到镜像。
    CLI解析.dockerignore为以行为分隔的模式列表,类似于Unix shells的golbs文件。基于匹配的目的,上下文的根目录可以是工作目录或根目录。例如,/foo/bar和foo/bar模式都是排除PATH或URL git仓库根目录的子目录foo下的bar文件。
    如果.dockerignore中的一行以#开始,那么这行视为注释,会以CLI解析前忽略。
    例如:

    1. # comment
    2.     */temp*
    3.     */*/temp*
    4.     temp?

    行为解释如下:

  • # comment:忽略
  • <*/temp*:排除在任意根目录的子目录下以temp开始的文件和目录。例如,/somedir/temporary.txt文件和/somedir/temp目录都被排除。
  • */*/temp*:排除从根目录两级目录的子目录下以temp开始的文件或目录。例如,/somedir/subdir/temporary.txt文件会被排除。
  • temp?:排除temp后加一个字符的文件或目录。如/tempa和/tempb被排除。
  • 使用Go的filepath.Match规则完成匹配。除了Go的filepath.Match规则,docker也支持一个特殊的通配符**,用来匹配任意数量的目录。如**/*.go将排除在所有目录中以.go后缀的文件,包括根目录。
    以!开始的行表示与排除相反的动作,即包括。如下:

    1. *.md
    2.     !README.md

    除了README.md,其它的.md文件排除。
    放置!例外规则影响的行为:.dockerignore的最后行匹配的文件决定是否是包含或排除。如下:

    1. *.md
    2.     !README*.md
    3.     README-secret.md

    除了README markdown文件(不含README-secret.md),其它的markdown文件包括全都排除。
    再看一个例子:

    1. *.md
    2.     README-secret.md
    3.     !README*.md

    包括所有的README文件。中间的行没有影响是因为!README*.md匹配了README-secret.md。
    你甚至可以使用.dockerignore文件来排除Dockerfile和.dockerignore文件。这些排除的文件仍然会发送到daemon,因为需要它们来工作。不过ADD和COPY命令不再复制它们到镜像。
    最后,你可能想指定包括一些文件,而不是排除。那么可以在第一行输入*,随后的行使用!来包括。

    注意:由于历史原因,模式.会被忽略。

    Dockerfile参考(4) – 使用环境变量

    由EVN指令声明的环境变量也可以用在Dockerfile的一些指令中作为变量使用。转义符也将类似变量的语法转义为语句。
    在Dockerfile引用环境变量可以使用$variable_name或${variable_name}。它们是等同的,其中大括号的变量是用在没有空格的变量名中的,如${foo}_bar。
    ${variable_name}变量也支持一些标准的bash修饰符,如:

  • ${variable:-word}表示如果variable设置了,那么结果就是设置的值。否则设置值为word
  • ${variable:+word}表示如果variable设置了,那么结果是word值,否则为空值。
  • word可以是任意的字符,包括额外的环境变量。
    转义符可以添加在变量前面:$foo or ${foo},例如,会分别转换为$foor和${foo}。
    示例:

    1. FROM busybox
    2. ENV foo /bar
    3. WORKDIR ${foo}   # WORKDIR /bar
    4. ADD . $foo       # ADD . /bar
    5. COPY $foo /quux # COPY $foo /quux

    环境变量支持在下面的指令中使用:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • LABEL
  • USER
  • WORKDIR
  • VOLUME
  • STOPSIGNAL
  • 也包括:

  • ONBUILD
  • 注意:1.4之前的版本,ONBUILD指令不支持环境变量,即使是与上面列出的指令一起使用。

    环境变量的替换在整个命令使用的值是一样的。例如:

    1. ENV abc=hello
    2. ENV abc=bye def=$abc
    3. ENV ghi=$abc

    def的值是hello,不是bye,不过,ghi的值为bye,因为设置abc为bye的命令与设置ghi命令不同。