2017年05月04日 20:08:33 阅读数:2046更多nginx
我的分类: 服务器运维git
英文原文连接地址: Engine reference–>Dockerfile referencedocker
推荐你们直接看官方英文文档就行了shell
如下内容会帮助你们了解什么是Dockerfile,以及如何编写Dockerfile,经过一些示例来帮助你们更快速理解和掌握编写Dockerfile的方法。apache
Dockerfile
是一种能够用于建立镜像的文件,相似于makefile,它是一个包含了全部用于建立镜像的命令的有序序列的文本文件。ubuntu
使用 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
文件的格式以下bash
# 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.