以下内容会帮助大家了解什么是Dockerfile,以及如何编写Dockerfile,通过一些示例来帮助大家更快速理解和掌握编写Dockerfile的方法。

什么是Dockerfile文件

Dockerfile是一种可以用于创建镜像的文件,类似于makefile,它是一个包含了所有用于创建镜像的命令的有序序列的文本文件。

使用 Dockerfile的方法是:


$ > docker build -t 'ubuntu:git' .
Sending build context to Docker daemon 59.39 kB
Step 1 : FROM ubuntu
---> c5f1cf30c96b
Step 2 : CMD apt-get update && apt-get upgrade
---> Running in ccb83c0f7e1d
---> a35ebd488b21
Removing intermediate container ccb83c0f7e1d
Step 3 : CMD apt-get install git
---> Running in 39a8134ca865
---> e516586de6ce
Removing intermediate container 39a8134ca865
Successfully built e516586de6ce

$ > docker images
REPOSITORY           TAG                 IMAGE ID            CREATED              SIZE
ubuntu               git                 e516586de6ce        About a minute ago   120.8 MB


编写Dockerfile文件

Dockerfile文件的格式如下


# Comment
INSTRUCTION arguments


#井号开头的表示注释行;

INSTRUCTION指令是不区分大小写的,但是为了便于区分建议使用全大写格式。


.dockerignore 文件


这个文件的作用是根据文件内定义的规则过滤掉不需要 push的文件。

过滤规则:


*.md
!README.md


*.md过滤掉所有以.md结尾的文件, !README.md不过滤README.md文件。

命令列表介绍

ENV


设置环境变量指令,用法 ENV WORKPATH /tmp,也可以这样: ENV abc=bye def=$abc。第一种用法用于设置单个变量(第一个空格前为key,之后都是value,包括后面的空格),第二种用于同时设置多个变量(空格为分隔符,value中包含空格时可以用双引号把value括起来,或者在空格前加\反斜线),当需要同时设置多个环境变量时推荐使用第二种格式。

ENV用法如下:


ENV <key> <value>
ENV <key>=<value> ...

示例:
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy


FROM

设置基础镜像,一个有效的Dockerfile必须有一个 FROM指令指定一个基础镜像,这个镜像可以是任何你可以从共用仓库获取到的镜像。执行命令格式 FROM <image>或者 FROM <image>:<tag>或者 FROM <image>@<digest>,下面是一些注意信息:

- `FROM`指令必须是第一个非注释的指令;
- `FROM`可以在一个Dockerfile中出现多次,比如我们创建多个镜像时,在新的`FROM`指令之前只需要输出最后一次提交的镜像ID。
- `tag`和`digest`值是可选的,如果都没有填写这两个值则取默认的`latest`.如果无法匹配`tag`值会返回一个错误。

MAINTAINER

设置创建镜像的作者信息。

RUN


这个指令有两种格式.

第一种形式: RUN <command>(以shell形式执行命令,等同于 /bin/sh -c);

第二种形式: RUN ["executable", "param1", "param2"](等同于exec命令形式),注意此处必须是双引号(“),因为这种格式被解析为JSON数组。

关于 RUN指令的说明:


- 每一个RUN指令都会在最顶层上新建一层上执行并且提交执行结果.提交后的iamge将会用于`Dockerfile`中的下一步操作。
- 分层的`RUN`指令和生成提交符合Docker的核心概念,提交是廉价的,容器可以再一个历史镜像的任意层,这个很像源码控制。
- 第二种`exec`格式可以避免了shell字符串的改写问题,以及避免了`RUN`命令执行所在的基础镜像中没有`/bin/sh`的问题。
- 在`shell`格式中我们可以使用\(反斜杠)把一个`RUN`指令切换到下一行。


RUN /bin/bash -c 'source $HOME/.bashrc ;\
echo $HOME'


等同于


RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'


- `RUN`指令的cache对于下一次的创建是有效的,对于`RUN apt-get dist-upgrade -y`指令的cache将会再下次创建的时候使用,如果想要使用取消使用cache需要使用`--no-cache`参数。

- `RUN`指令的cache对于下一次的创建是有效的,对于`RUN apt-get dist-upgrade -y`指令的cache将会再下次创建的时候使用,如果想要使用取消使用cache需要使用`--no-cache`参数。

CMD


此命令主要目的是为一个正在执行的容器提供一些默认值,这些默认命令可以包含一个可执行命令 executable,也可以没有(此时需要指定一个 ENTRYPOINT指令,此时 CMDENTRYPOINT都必须使用 exec格式,即JSON数组格式)。

此命令有3种执行格式:


- `CMD ["executable","param1","param2"]` ,`exec`格式,推荐使用这种格式。
- `CMD ["param1","param2"]`,作为`ENTRYPOINT`的默认参数。
- `CMD command param1 param2`,`shell`格式。

LABEL

为镜像填写元数据 metadata信息,格式为key-value对格式,一个镜像可以有多个 LABEL,但是建议尽量放到同一个 LABEL指令下,因为每一个 LABEL指令的执行都会产生新的一层,这样会让我们得到镜像执行起来很低效。 LABEL定义的key可以覆盖镜像之前定义国的key值,我们可以通过 docker inspect命令查看镜像已经定义的标签 LABEL信息。以下是一些示例:


LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"


EXPOSE

此命令通知Docker容器监听指定的网络端口 <port>EXPOSE指令不会让容器的端口访问host主机,如果想要这样做就需要在运行容器的时候指定 -pflag发布一个端口范围或者 -Pflag发布所有打开的端口, 详细介绍参考 expose-incoming-ports

EXPOSE

用法为 EXPOSE <port> [<port>...]

ADD

此指令用于复制新文件、目录或者远程URL地址添加到容器的指定 <dest>路径下,指令包含两种形式:


ADD <src>... <dest>
ADD ["<src>",... "<dest>"] (this form is required for paths containing whitespace)


  • 对于 ADD指令,可以有多个 <src>资源被指定,假如这些资源是文件或者目录,则这些资源的路径必须为相对与build的工作路径,而不是绝对路径,而且每个 <src>可以包含通配符,匹配规则是由Go语言的filepath.Match函数完成。
ADD hom* /mydir/        # adds all files starting with "hom"
ADD hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"


  • 对于 <dest>是一个绝对路径或者是相对于 WORKDIR的相对路径。
ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # adds "test" to /absoluteDir/


  • 所有的文件和目录添加到容器中时的 UIDGID都是 0。
  • <src>为URL时, <dest>路径需要是600权限。如果远端获取的文件有HTTP Last-Modified头部信息,将会使用此时间戳作为文件的最后修改时间 mtime

ADD遵循如下规则:

  • 路径必须在buid的环境内;不可以指定 ADD ../something /something,因为创建镜像build的第一步是将buid的环境目录内容发送给 docker daemon进程。
  • 如果 <src>是URL并且 <dest>不是以斜杠(/)结束,那么一个文件将会下载下来后再复制到 <dest>
  • 如果 <src>是URL并且 <dest>以斜杠(/)结束,那么将会下载下来URL指定的文件名到 <dest>下。
  • 如果 <src>是一个目录,则整个目录下(目录本身不被复制)内容和元数据信息也会被复制到 <dest>中。
  • 如果 <src>是一个本地tar包的压缩包(gzip/bzip2/xz),则此压缩包会先被解压处理,而远端的URL不会被解压。当一个目录

COPY

COPY有两种格式:

  • COPY <src> ... <dest>
  • COPY [ "<src>",..."<dest>"](这种格式需要路径包含空格)

COPY指令从 <src>复制新文件或者目录然后添加到容器文件系统中的 <dest>路径。

也可以有多个 <src>资源被指定,假如这些资源是文件或者目录,则这些资源的路径必须为相对与build的工作路径,而不是绝对路径,而且每个 <src>可以包含通配符,匹配规则是由Go语言的filepath.Match函数完成。以下是示例:


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


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


COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # adds "test" to /absoluteDir/


所有的文件和目录添加到容器中时的 UIDGID都是 0。


Note: 如果使用STDIN创建build镜像( docker build - < somefile), 由于没有build上下文,所以 COPY指令无法使用。


COPY遵循如下规则:

  • 路径必须在buid的环境内;不可以指定 COPY ../something /something,因为创建镜像build的第一步是将buid的环境目录内容发送给 docker daemon进程。
  • 如果 <src>是一个目录,则整个目录下(目录本身不被复制)内容和元数据信息也会被复制到 <dest>中。
  • 如果 <src>是任何其他种类的文件,它被单独连同其元数据复制。这种场景下,如果 <dest>是斜线(/)结尾,它会被当作目录,然后被写到 <dest>/base(<src>).
  • 对于多个 <src>资源指定时, <dest>必须是一个目录,并且以斜线(/)结尾。
  • 如果 <dest>不是以斜线(/)结尾,它将被当作一个普通文件,并且 <src>将会写到 <dest>中。

ENTRYPOINT

ENTRYPOINT有两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"](exec 格式,推荐)
  • ENTRYPOINT command param1 param2(shell 格式)

ENTRYPOINT指令允许我们为配置一个作为运行某个命令的容器。例如下面将启动nginx作为运行容器的默认内容,监听80端口:


docker run -i -t --rm -p 80:80 nginx


docker run <image>的命令行参数会在 exec格式的 ENTRYPOINT指令后追加,并且覆盖所有 CMD指令指定的所有元素。所以这使得参数可以传递到入口点,例如, docker run <image> -d将会吧 -d参数传给入口点 ENTRYPOINT. 我们可以使用 docker run --entrypoint标识来覆盖 ENTRYPOINT指令。

shell形式的指令避免任何 CMD或者 run使用命令行参数,但是缺点是 ENTRYPOINT指令会作为/bin/sh -c进程的子进程来运行,这意味着无法收到容器中来自 PID 1UNIX信号,所以进程将无法通过 docker stop <container>收到 SIGTERM信号。

记住一个Dockerfile中只有最后一个 ENTRYPOINT指令会生效。

Exec 格式的 ENTRYPOINT示例


FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]


我们运行容器后,会看到 top将是唯一一个进程。


$ docker run -it --rm --name test  image-top -H
top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%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
KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top


我们可以使用 docker exec来深入的了解下具体情况:


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


我们可以使用 docker stop test来停止 top进程。

下面的 Dockerfile显示的是使用 ENTRYPOINT来前台运行Apache(PID为1):


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


我们可以为一个单独的可执行命令编写一个启动脚本,这样我们就可以通过 gosuexec来确保这个最后执行的命令可以获取到Unix信号,下面就是一个脚本示例


#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"


最后,如果我们需要在关机或者与其他可执行程序进行协调时进行一些额外清理工作(或者与其他容器通信),我们就需要确保 ENTRYPOINT脚本可以获取到Unix信号,然后通过他们我们可以做更多的工作:


#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too

# USE the trap if you need to also do manual cleanup after the service is stopped,
#     or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM

# start service in background here
/usr/sbin/apachectl start

echo "[hit enter key to exit] or run 'docker stop <container>'"
read

# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop

echo "exited $0"


我们可以执行命令 docker run -it --rm -p 80:80 --name test apache启动一个容器,然后通过 docker exec或者 docker top来检查容器中的进程,然后通过脚本停止Apache(此时停止时间很快) :


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


注释说明:

> Note:我们可以使用`--entrypoint`参数来覆盖`ENTRYPOINT`设置,但是这智能设置exec模式(不会执行sh -c)  
> Note:还是说明`exec`格式的指令会被解析为JSON数组格式,我们使用的应该是双引号(")而不是单引号(');  
> Note:跟 *shell* 格式不同, *exec* 格式不会调用一个shell命令,例如执行`ENTRYPOINT [ "echo", "$HOME" ]`就不会获取到变量`$HOME`信息。不过我们可以执行这个命令来执行shell命令`ENTRYPOINT [ "sh", "-c", "echo", "$HOME" ]` 。另外我们在`Dockerfile`中需要使用`ENV`来定义我们需要使用的变量。

Shell格式的 ENTRYPOINT示例


我们可以为 ENTRYPOINT指定一个文本字符串作为命令,然后它将在 /bin/sh -c下被执行。这种格式会使用shell处理来替代shell环境变量,并且会忽略任何 CMD或者 docker run命令行参数。

为了确保 docker stop可以正确的发送号给所有在运行中的 ENTRYPOINT可执行程序,我们需要记住要使用 exec作为 ENTRYPOINT命令的开始,如下示例:


FROM ubuntu
ENTRYPOINT exec top -b


当我们运行这个镜像时,我们会看到一个 PID 1的进程:


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


当我们停止容器运行时,它将会彻底的退出:


$ /usr/bin/time docker stop test
test
real    0m 0.20s
user    0m 0.02s
sys 0m 0.04s


如果我们忘记添加 exec到命令的开始位置 :


FROM ubuntu
ENTRYPOINT top -b


我们运行后的效果会是这样的:


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


我们看到了 PID 1的进程不是 top命令,而是 /bin/sh -c,然后我们执行 docker stop test停止容器的运行,容器不会彻底的退出,stop命令将会被迫在超时后发送 SIGKILL信号:


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


理解 CMDENTRYPOINT是如何相互作用的

CMDENTRYPOINT两个指令都定义了当运行一个容器时要执行的命令。有如下几条规则描述它们之间的相互作用:

1. `Dockerfile`应该指定至少`CMD`或者`ENTRYPOINT`中的一个指令。
2. 当容器作为一个可执行程序时应该指定`ENTRYPOINT`指令。
3. `CMD`应当被用来作为一种为`ENTRYPOINT`提供默认参数的方式来使用,或者执行容器中的特定(ad-hoc)命令。
4. `CMD`指令参数可以被运行容器时传入的替代参数覆盖掉。

下表展示了 CMD/ ENTRYPOINT命令执行情况 :

 

No ENTRYPOINT

ENTRYPOINT exec_entry p1_entry

ENTRYPOINT [“exec_entry”, “p1_entry”]

No CMD

error,not allowed

/bin/sh -c exec_entry p1_entry

exec_entry p1_entry

CMD [“exec_cmd”, “p1_cmd”]

exec_cmd p1_cmd

/bin/sh -c exec_entry p1_entry exec_cmd p1_cmd

exec_entry p1_entry exec_cmd p1_cmd

CMD [“p1_cmd”, “p2_cmd”]

p1_cmd p2_cmd

/bin/sh -c exec_entry p1_entry p1_cmd p2_cmd

exec_entry p1_entry p1_cmd p2_cmd

CMD exec_cmd p1_cmd

/bin/sh -c exec_cmd p1_cmd

/bin/sh -c exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

VOLUME


VOLUME [ "/data"]      (exec格式指令)
VOLUME /data             (shell格式指令)


VOLUME指令创建一个可以从本地主机或其他容器挂载的挂载点,该指令参数可以是JSON数组(如 VOLUMN ["/var/log/"]),也可以是多个参数的文本字符串(如: VOLUMN /var/log /var/db)。更多关于挂载信息可访问 Share Directories via Volumes

docker run命令用基础镜像中指定目录位置数据来初始化新创建卷。如下示例:


FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
RUN echo "hello world" > /myvol/greeting.discarded   (这条数据修改内容最终会被丢弃)


这个Dockerfile执行的结果是创建了一个新挂载点/myvol,然后复制greetging文件到新创建的卷。


Note:在build创建镜像的步骤中, 如果是在Volume声明后修改卷数据,那么这些修改最终会被丢弃。Note: 需要说明在使用JSON数组格式指令时要使用双引号(“),而不是单引号(‘)。


USER


USER daemon


USER指令用来设置再 Dockerfile中要使用的用户名或者 UID

WORKDIR


WORKDIR /path/to/workdir


WORKDIR指令用来设置 Dockerfile中任何使用目录的命令的当前工作目录,此目录如果不存在就会被自动创建,即使这个目录不被使用。此命令也可以使用 Dockerfile中用 ENV定义的环境变量,例如:


ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd


ARG


ARG <name>[=<default value>]


ARG指令设置一些创建镜像时的参数,这些参数可以在执行 docker build命令时通过 --build-arg <varname>=<value>设置,如果指定的创建参数在 Dockerfile中没有指定,创建时会输出错误信息: One or more build-args were not consumed, failing build.

Dockerfile 作者可以为 ARG设置一个默认参数值,当创建镜像时如果没有传入参数就会使用默认值:


FROM busybox
ARG user1=someuser
ARG buildno=1


我们可以使用 ARG或者 ENV指令来指定 RUN指令使用的变量。我们可以使用 ENV定义与 ARG定义名称相同的变量来覆盖 ARG定义的变量值。如下示例,我们执行 $ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile后将获取到的CONT_IMG_VER变量值为 v1.0.0:


FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER


我们还可以变换一下 ENV指令内容来增加一些更丰富的功能(比如: ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}).

另外,Docker有些预定义的 ARG变量,不需要提前声明就可以使用,比如如下几个:

- HTTP_PROXY
- http_proxy
- HTTPS_PROXY
- https_proxy
- FTP_RPOXY
- ftp_proxy
- NO_PROXY
- no_proxy

我们可以通过使用 --build-arg <varname>=<value>来传递参数值到 Dockerfile中。

ONBUILD


ONBUILD [INSTRUCTION]


ONBUILD指令为镜像提供了一个触发器 trigger指令功能,当这个镜像被作为基础镜像执行创建时会自动触发设定的指令被执行,执行过程就相当于在 Dockerfile中的 FROM指令后面立即插入了这条指令一样。

这个命令非常有帮助,比如对于应用创建环境我们可以用它来自定义用户指定的配置。

需要注意的是:

1. 当使用`ONBUILD`创建镜像时,这个触发器会添加到创建后的镜像上,以后以这个新的镜像作为基础镜像进行创建新镜像时才会触发这个触发器。
2. 在创建镜像的最后,添加的触发器会存储到镜像的`manifest`清单中,我们可以通过`docker inspect`查看`OnBuild`的信息。
3. 当带有`ONBUILD`信息的镜像通过`FROM`作为新镜像的基础镜像时,首先builder会查找`OnBuild`触发器信息,然后按照注册的先后顺序依次执行每一个触发器,如果任何一个触发器执行失败,则`FROM`中止build的执行。如果所有触发器都执行成功,则`FROM`指令执行完成,并且继续向下执行。
4. 触发器在执行一次之后生成的镜像中就会被清理掉,它不会被继承到下一个镜像中。


Note: ONBUILD不支持嵌套 ONBUILD,另外 ONBUILD指令不可以触发 FROM或者 MAINTAINER


STOPSIGNAL


STOPSIGNAL signal


STOPSIGNAL指令用来设置用来发送给容器退出的系统调用信号。这个信号可以是系统调用表中的数字,也可以是信号名称,格式为 SIGNAME,例如 SIGKILL.

Dockerfile 示例

以下内容是一些简单的Dockerfile示例,如果感兴趣可以访问 Dockerization examples了解更多内容:

示例1:


# Nginx
#
# VERSION               0.0.1

FROM      ubuntu
MAINTAINER Victor Vieux <victor@docker.com>

LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server


示例2:


# Firefox over VNC
#
# VERSION               0.3

FROM ubuntu

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'

EXPOSE 5900
CMD    ["x11vnc", "-forever", "-usepw", "-create"]


示例3:


# Multiple images example
#
# VERSION               0.1

FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f

FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4

# You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.