英文原文链接地址: Engine reference–>Dockerfile reference
推荐大家直接看官方英文文档就好了
以下内容会帮助大家了解什么是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
文件的格式如下
# Comment
INSTRUCTION arguments
#
井号开头的表示注释行;
INSTRUCTION
指令是不区分大小写的,但是为了便于区分建议使用全大写格式。
这个文件的作用是根据文件内定义的规则过滤掉不需要 push
的文件。
过滤规则:
*.md
!README.md
*.md
过滤掉所有以.md结尾的文件, !README.md
不过滤README.md文件。
设置环境变量指令,用法 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
设置基础镜像,一个有效的Dockerfile必须有一个 FROM
指令指定一个基础镜像,这个镜像可以是任何你可以从共用仓库获取到的镜像。执行命令格式 FROM <image>
或者 FROM <image>:<tag>
或者 FROM <image>@<digest>
,下面是一些注意信息:
- `FROM`指令必须是第一个非注释的指令;
- `FROM`可以在一个Dockerfile中出现多次,比如我们创建多个镜像时,在新的`FROM`指令之前只需要输出最后一次提交的镜像ID。
- `tag`和`digest`值是可选的,如果都没有填写这两个值则取默认的`latest`.如果无法匹配`tag`值会返回一个错误。
设置创建镜像的作者信息。
这个指令有两种格式.
第一种形式: 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`参数。
此命令主要目的是为一个正在执行的容器提供一些默认值,这些默认命令可以包含一个可执行命令 executable
,也可以没有(此时需要指定一个 ENTRYPOINT
指令,此时 CMD
和 ENTRYPOINT
都必须使用 exec
格式,即JSON数组格式)。
此命令有3种执行格式:
- `CMD ["executable","param1","param2"]` ,`exec`格式,推荐使用这种格式。
- `CMD ["param1","param2"]`,作为`ENTRYPOINT`的默认参数。
- `CMD command param1 param2`,`shell`格式。
为镜像填写元数据 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"
此命令通知Docker容器监听指定的网络端口 <port>
, EXPOSE
指令不会让容器的端口访问host主机,如果想要这样做就需要在运行容器的时候指定 -p
flag发布一个端口范围或者 -P
flag发布所有打开的端口, 详细介绍参考 expose-incoming-ports。
用法为 EXPOSE <port> [<port>...]
。
此指令用于复制新文件、目录或者远程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/
UID
和 GID
都是 0。<src>
为URL时, <dest>
路径需要是600权限。如果远端获取的文件有HTTP Last-Modified
头部信息,将会使用此时间戳作为文件的最后修改时间 mtime
。ADD
遵循如下规则:
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 <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/
所有的文件和目录添加到容器中时的 UID
和 GID
都是 0。
Note: 如果使用STDIN创建build镜像( docker build - < somefile
), 由于没有build上下文,所以 COPY
指令无法使用。
COPY
遵循如下规则:
COPY ../something /something
,因为创建镜像build的第一步是将buid的环境目录内容发送给 docker daemon
进程。<src>
是一个目录,则整个目录下(目录本身不被复制)内容和元数据信息也会被复制到 <dest>
中。<src>
是任何其他种类的文件,它被单独连同其元数据复制。这种场景下,如果 <dest>
是斜线(/)结尾,它会被当作目录,然后被写到 <dest>/base(<src>)
.<src>
资源指定时, <dest>
必须是一个目录,并且以斜线(/)结尾。<dest>
不是以斜线(/)结尾,它将被当作一个普通文件,并且 <src>
将会写到 <dest>
中。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 1
UNIX信号,所以进程将无法通过 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"]
我们可以为一个单独的可执行命令编写一个启动脚本,这样我们就可以通过 gosu
和 exec
来确保这个最后执行的命令可以获取到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
理解 CMD
和 ENTRYPOINT
是如何相互作用的
CMD
和 ENTRYPOINT
两个指令都定义了当运行一个容器时要执行的命令。有如下几条规则描述它们之间的相互作用:
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 [ "/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 daemon
USER
指令用来设置再 Dockerfile
中要使用的用户名或者 UID
。
WORKDIR /path/to/workdir
WORKDIR
指令用来设置 Dockerfile
中任何使用目录的命令的当前工作目录,此目录如果不存在就会被自动创建,即使这个目录不被使用。此命令也可以使用 Dockerfile
中用 ENV
定义的环境变量,例如:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
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 [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 signal
STOPSIGNAL
指令用来设置用来发送给容器退出的系统调用信号。这个信号可以是系统调用表中的数字,也可以是信号名称,格式为 SIGNAME
,例如 SIGKILL
.
以下内容是一些简单的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.
(adsbygoogle = window.adsbygoogle || []).push({});