docker
文章目录
1、The Docker platform
(https://docs.docker.com/get-started/overview/#the-docker-platform)
1、每个容器都是独立的
2、可使用容器进行独立 打包、运行应用程序
3、在同一台主机可运行多个容器
4、容器轻量,直接依赖主机内核
5、也可在虚拟机中运行
2、docker engine
https://docs.docker.com/get-started/overview/#docker-engine
一个连接 客户端-服务端 的应用程序,通过它与docker进行交互,主要的组件有:
- Docker Daemon Server,其实是一个一直运行的守护进程
- REST API, 供外部调用、发送指令
- CLI, 客户端命令行接口
CLI 使用REST API, 通过调用脚本 或 直接CLI命令来控制Docker守护进程或与之交互
调用顺序:CLI <=> REST API <=> Docker Daemon Server
Dockerfile reference
docker build 构建
docker image的构建命令,所需要准备的是一个 Dockerfile文件和context
Dockerfile:
即包涵构建image所需要指令
context:
可以是文件目录或git 仓库URL地址。
如果是目录,最好是一个空目录,里面存放Dockerfile文件和构建image所需要的资源文件。
如果目录中有不是构建时所需要的文件,可以在该目录下添加 .dockerignore 文件并指明具体哪些文件不需要
构建时,context是递归处理的,比如你的目录中有子目录或者url git仓库中有子模块,都会递归处理。
构建时,如果Dockerfile不在context下,可以用 -f 参数指定Dockerfile的路径 ,如下:
docker build -f /path/to/a/Dockerfile .
构建时,使用 -t 参数 指定仓库和标签,来保存新的image,如下:
docker build -t shykes/myapp .
构建时,使用 -t 参数,来将生成的镜像标记到多个仓库中, 如下:
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
build命令是由Docker守护进程运行的,执行该命令前会先较验Dockerfile文件,如果语法有误,会返回错误信息。
docker守护进程会逐条执行Dockerfile文件里的命令,每个命令都是独立运行的,并且每条命令运行后会生成一个对应的境像,并在必要时返回每条的执行结果给新的境像,Dockerfile运行完后,会输出新生成的境像ID。最后,docker 守护进程会自动清除docker build时指定的context。
docker构建过程中,有时可能会重用中间的镜像缓存来提高构建的效率,在构建时控制台输出的信息中可以看到。这些镜像缓存其实就是在当下的构建中,每一条指令执行后,生成的镜像。此外,还可以用 –cache-from 来手动指定构建缓存镜像,这样操作就不需要依赖前个指令执行后生成的新境像
BuildKit 构建工具
Format 语法格式
Dockerfile 语法格式:
# comment
INSTRUCTION arguments
Parser directives 解析器指令
会影响Dockerfile中后续指令的执行, 解析器指令不会向生成中添加层,也不会显示为生成步骤。
可选,使用规则如下:
1、没有区分大小写,一般用小写,#后跟着一空格,不支持换行符
2、解析器指令以 #directive=value 的形式作为特殊类型的注释编写。一个指令只能使用一次。
3、解析器指令都必须位于Dockerfile的最顶端。(因为处理完注释、空行或生成器指令后,Docker不再查找解析器指令。相反,它将任何格式化为parser指令的内容作为注释处理,也不会验证它是否可能是parser指令)
支持的语法:
syntax: escape: 用于设置转义字符的字符, 默认的转义字符是 \
# syntax = [remote image reference]
# escape=\ (backslash)
Or
# escape=` (backtick) 将转义字符设置为 `
Environment replacement 环境变量替换
环境变量(用ENV表达式声明)也可以在确定的Dockerfile指令中被解析出来,Escapes 也被处理为类似变量的符号直接包含到表达式语句中。
环境变量在Dockerfile中表现形式可以是 $variable_name
或者 ${variable_name}
,有大括号的主要是用来处理没有空格的变量名问题,例如: ${foo}_bar
${variable_name} 语法上还支持一些标准的 bash
修饰符,如下:
-
${variable: -word} :如果variable有值,则结果就是variable的值,如果variable没有设置,则结果是word的值
-
${variable: +word}:如果variable有值,则结果就是word的值,如果variable没有设置,则结果是空字符串
在任何情况下,words可以是任何字符串,包括附加的环境变量
转义符可以通过在变量前添加
\
, 例如:\$foo
或者\${foo}
将会分别被转换成$foo
或${foo}
文字显示
环境变量在Dockerfile还支持下面的这些命令:
- `ADD`
- `COPY`
- `ENV`
- `EXPOSE`
- `FROM`
- `LABEL`
- `STOPSIGNAL`
- `USER`
- `VOLUME`
- `WORKDIR`
# 同时,1.4版本以后,ONBUILD 支持与上面任一一个指令结合使用
- `ONBUILD`
环境变量的替换在整个指令中是使用相同的值(有点迷糊),看下面:
#在这条指令中定义了 abc 的值是 hello
ENV abc=hello
#这又是一条单独的指令,这条指令用的abc的值,是在上一条指令中定义的,所以通过 $abc 解析,
#只能解析已经定义过的环境变量,即上一条中定义的abc, 不要被同一条指令中的abc给混淆
ENV abc=bye def=$abc
# 这里$abc的值就是上一条指令中定义的bye了
ENV ghi=$abc
.dockerignore file
这个其实就像git中的ignore文件一样,用来排除与项目非相关的文件或文件目录
FROM
FROM [--platform=<platform>] <image> [AS <name>]
Or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
Or
FROM [--platfrom=<platform>] <image>[@<digest>] [AS <name>]
FROM
指令初始化一个新的构建阶段,并设置一个基础镜像给后续的指令。就像这样,一个有效的Dockerfile文件必需以FROM
指令开头。基础镜像可以是任何的有效镜像–从公共仓库中拉取是特别方便的开始。
ARG
在Dockerfile中唯一一个能在FROM
前面使用的指令FROM
可以在Dockerfile中多次使用创建多个镜像,或者使用一次做为一个构建阶段 做为另一个阶段的依赖。所以只需要在每个新的FROM
指令提交之前 记录好上个指令输出的镜像ID 来做为依赖。每条From
指令都会清除前一条指令过程中所创建的任何声明、状态。- 可选命名,在
FROM
指令中通过添加AS name
来给新的构建过程命名。可以在后续的FROM
和COPY--FROM=<name | index>
指令中使用该名称来引用此阶段中生成的镜像。 tag
或者digest
变量也是可选的。如果省略不用,默认会把latest
做为tag的值。如果找不到tag值,构建器就会返回错误。
--platform
可选,FROM
引用的镜像支持多平台时,用来指定镜像的具体平台。例如 linux/amd64, linux/arm64 或者 windows/amd64。默认情况下根据请求的平台类型而定。全局的构建参数可以做为值这个标志的值,例如 允许您强制将阶段转换为本机生成平台(--platform=$BUILDPLAFORM
), 并使用它交叉编译到阶段内的目标平台。
理解 ARG
和 FROM
是如何交互的
FROM
指令支持变量(在FROM
之前,被 ARG
指令声明的变量)
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
ARG
在FROM
之前是声明,它是在构建过程之外的(Dockerfile 构建过程中,每条指令都独立的),因此它不能在FROM
后的任何一条构建指令引用(正常情况下只能被紧跟其后的FROM
指令中使用)。如果要在FROM
之后的指令中引用FROM
之前ARG
定义的默认变量,需要在引用前用ARG
指令做下声明(不需要赋值):
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
RUN
RUN
有两种格式,
RUN <command>
(shell form,在shell脚本中执行的命令,在linux中默认是/bin/sh -c
, 在 windows中是cmd /S /C
)RUN ["executable", "param1", "param2"]
(exec form)
RUN
指令会在当前镜像的最上层执行并提交结果,生成的镜像将会做为下一步执行使用。
分层运行指令和生成提交符合Docker的核心概念,在Docker中,提交很轻量,可以从映像历史记录的任何一点创建容器,就像源代码管理一样(说法太官方,应该就是说 镜像容器 在每个指令运行提交 后,就很容易创建生成)。
exec格式可以避免shell一系列的累积,并使用不包含指定的shell可执行文件的基映像运行命令
可以使用SHELL
命令, 更改默认shell 格式
在 shell 格式中,可能用 \
来连接换行的RUN
指令,例如
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
注意:要使用不同的shell,而不是’/bin/sh’,请使用传入所需shell的exec格式,例如:
RUN ["/bin/bash", "-c", "echo hello"]
注意:exec格式被解析成JSON数组,因此在得用双引号而不是用单引号
注意:与shell格式不同,exec格式不调用命令shell,这意味着一般的shellg不会被执行处理。例如:
RUN ["echo", "$HOME"]
$HOME
不会被有效地替换。如果想要用shell处理,要么有shell格式,要么直接执行一个shell指令,例如:RUN ["sh", "-c", "echo $HOME"]
,这是环境变量的扩展,而不 docker。
RUN
指令的缓存在下次构建还会有效,不会自动清除。例如 RUN apt-get dist-upgrade -y
在下一次构建时,会被重新使用。--no--cache
可以用来使缓存无效,例如: docker build --no-cache
CMD
CMD
命令有三种格式:
CMD ["executable", "param1", "param2"]
(exec格式,推荐)CMD ["param1", "param2"]
(默认的入口参数)CMD command param1 param2
(shell格式)
在Dockerfile中,只能有一个CMD命令,如果有多个,那最终只有最有一个会生效。
CMD
的主要功能是给可执行容器提供默认值。这些默认值可以是可执行脚本,或者省略掉(在这种情况下必须指定一个 ENTRYPOINT
命令)
注意:如果
CMD
用来提供默认参数 给ENTRYPOINT
指令,所有的CMD
和ENTRYPOINT
指令必须指定用JSON数组格式。注意:exec格式被解析成JSON数组,因此在得用双引号而不是用单引号
注意:与shell格式不同,exec格式不调用命令shell,这意味着一般的shellg不会被执行处理。例如:
RUN ["echo", "$HOME"]
$HOME
不会被有效地替换。如果想要用shell处理,要么有shell格式,要么直接执行一个shell指令,例如:RUN ["sh", "-c", "echo $HOME"]
,这是环境变量的扩展,而不 docker。
当使用shell 或者 exec 格式, 在运行境像时,CMD
指令设置的命令就会执行。
如果你用shell格式,这些命令将会在 /bin/sh -c
中执行。
FROM ubuntu
CMD echo "This is a test." | wc -
如果你不想通过shell运行你的命令,就得把指令写成JSON数组格式,然后给可执行脚本 指定全路径。 这个数组格式是 CMD
的推荐方式。任何其他参数都必须在数组中单独表示为字符串:
FROM ubuntu
CMD ["/usr/bin/wc", "--help"]
如果想要容器运行同样的可执行脚本,那么您应该考虑结合使用ENTRYPOINT
和CMD
如果用户指定docker run的参数,那么它们将覆盖在CMD中指定的默认值
注意:不要混淆RUN和CMD。RUN实际运行一个命令并提交结果;CMD在构建时不执行任何操作,但指定境像即将运行的命令。
LABEL
LABEL <key>=<value> <key>=<value> <key>=<value> ...
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,可以在同一行,可以用 、
连接成多行
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
Labels 包涵从基础境像或者父境像中继承的信息。如果标签已存在但具有不同的值,则最近应用的值将覆盖以前设置的任何值。
用 docker inspect
查看境像的labels信息:
"Labels": {
"com.example.vendor": "ACME Incorporated"
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
},
EXPOSE
EXPOSE <port> [<port>/<protocol>...]
EXPOSE
显示Docker容器在运行时所监听的网络商品。可以指定TCP或者UDP, 不指定默认是TCP
EXPOSE
实际上没有开放外网端口。它在构建镜像的人和运行容器的人之间起一种文档的作用,告诉关于要发布的端口。实际发布端口在运行容器时,在运行docker run
时添加-p
来发布一个或多个端口。或者用-P来暴露所有的端口,并映射他们到高有序的端口。
EXPOSE 80/udp
EXPOSE 80/tcp
ENV
ENV <KEY> <VALUE> #环境变量单条设置
ENV <KEY>=<VALUE> <KEY>=<VALUE> ... #环境变量多条设置
当境像运行容器时,使用ENV设置的环境变量将保持不变。可以通过docker inspect
来查看,docker run --env <key>=<value>
可以改变环境变量
ADD
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] #这种格式在路径中需要加空隔
注意:
--chown
只能在Linux容器中使用
ADD
指令复制新的文件、目录或都远程文件<src>
,然后添加到境像文件系统的目标路径中dest
可以指定多个src
,但如果是文件或目录,它们的路径就被当成相对于当前构建境像的上下文目录
每个<src>
可以包含通配符,并且会根据 文件配置规则进行匹配,如下:
ADD hom* /mydir/ #把hom开头的所有文件加到目录
ADD hom?.txt /mydir/
<dest>
是一个绝对路径或者相对于 WORKDIR
的路径 。
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # adds "test" to /absoluteDir/
当添加的文件或目录带有特殊符号时,要根据 golang的规则进行转义。如
ADD arr[[]0].text /mydir/ # copy a file named "arr[0].txt" to /mydir/
所有新文件和目录都是使用UID和GID 0创建的,除非可选的–chown标志指定给定的用户名、组名或UID/GID组合以请求所添加内容的特定所有权。–chown标志的格式允许username和groupname字符串,或者以任何组合使用直接整数UID和GID。提供没有groupname的用户名或没有GID的UID将使用与GID相同的数字UID。如果提供用户名或组名,则容器的根文件系统/etc/passwd和/etc/group文件将分别用于执行从名称到整数UID或GID的转换
COPY
COPY
有两种格式:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] #这个格式要求路径包含空格
–chown只适合Linux容器,在Windows容器中不会生效。
该命令用于在构建镜像时,把构建目录下的源文件或目录复制到镜像容器的指定目录去
<src>
如果是多个文件或目录,那它的路径是相对于构建目录的
<scr>
可以包包含通配符,并根据go的filepath.Match规则进行配置。
COPY hom* /mydir/
COPY hom?.txt /mydir/
<dest>
是绝对路径,或者一个相对于 WORKDIR
的路径
#这是使用相对路径,把文件复制到<WORKDIR>/relativeDIR/
COPY text.txt relativeDir/
#这是使用绝对路径,把文件复制到/absoluteDIR/
CPPY text.txt /absoluteDir/
当复制的文件或目录名包含特殊字符时,需要根据golang的规则使用用转义字符
#添加一个名字为 arr[0].txt 的文件
ADD arr[[]0].txt /mydir
所有新生成的文件或目录名字默认是UID或都GID,除非用可选标识 --chown
来指定名字,组名,或者UID/GID组合以请求复制内容的特定所有权。–chown标志的格式允许username和groupname字符串,或者以任何组合使用直接整数UID和GID。提供没有groupname的用户名或没有GID的UID将使用与GID相同的数字UID。如果提供用户名或组名,则容器的根文件系统/etc/passwd和/etc/group文件将分别用于执行从名称到整数UID或GID的转换。以下示例显示–chown标志的有效定义:
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
如果容器根文件系统不包含/etc/passwd或/etc/group文件,并且在–chown标志中使用了用户名或组名,则生成将在复制操作中失败。使用数字标识不需要查找,也不依赖于容器根文件系统内容。
可选地,COPY接受一个标志--from=<name | index>
可用于将源位置设置为将要使用的前一个生成阶段(使用FROM .. AS <name>
创建),而不是由用户发送的生成上下文。该标志还接受为以FROM指令开始的所有先前生成阶段分配的数字索引。如果找不到具有指定名称的生成阶段,则尝试使用具有相同名称的映像。
COYP
遵循的规则:
<src>
路径必须是构建的上下文里面,不能是../something/something
,因为docker build
的第一步是把构建的上下文发送给docker 的守护进程。- 如果
<src>
是一个目录,目录里面的所有内容,包括元数据,将会全部被复制(注意,是目录里面的内容,目录是不会被复制的) - 如果
<src>
是任何其他类型的文件,则它将与元数据一起单独复制。在这种情况下,如果以斜杠结尾,则它将被视为一个目录,<src>
的内容将写入<dest>
/base(<src>
)。 - 如果直接或由于使用通配符而指定了多个
<src>
资源,则<dest>
必须是一个目录,并且必须以斜线/结尾。 - 如果
<dest>
没有以斜杠结尾,则它将被视为常规文件,<src>
的内容将在<dest>
写入。 - 如果
<dest>
不存在,它将与路径中所有缺少的目录一起创建。
ENTRYPOINT
ENTRYPOINT
有两种格式
ENTRYPOINT ["executable", "param1", "param2"]
(exec格式,推荐)ENTRYPOINT command param1 param2
(shell格式)
ENTRYPOINT
让你可以配置一个可运行的容器,如下:
docker run -i -t --rm -p 80:80 nginx #将使用nginx的默认内容启动nginx
在exec格式的ENTRYPOINT
中,docker run <image>
的命令行参数会加在所有的元素后面,并且覆盖在CMD
中所指定的元素。它允许将参数传递到入口点,例如:docker run <image> -d
将会所参数-d
传到入口中。你可以用docker run --entrypint
重写ENTRYPOINT
指令。
shell格式防止使用任何命令行参数或运行命令行参数,但它的缺点是入口点将作为/bin/sh -c
的子命令启动,该子命令不传递信号。这意味着可执行文件将不是容器的PID 1,也不会接收Unix信号,因此您的可执行文件将不会从docker stop <container>
接收SIGTERM
。
在Dockerfile中,只有最后一个ENTRYPOINT
指令会生效。
exec形式的 ENTRYPOINT
例子
您可以使用ENTRYPOINT的exec形式来设置相当稳定的默认命令和参数,然后使用CMD的任一形式来设置更可能更改的其他默认值
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
如上运行后,可以看到top是唯一的进程,可以用docker exec
进一步检验
docker exec -it test ps aux
你可以通过docker stop test
请求top关闭
下面的Dockerfile展示了如何使用ENTRYPOINT在前台运行Apache:
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"]
如果需要为单个可执行文件编写启动脚本,可以使用exec
和gosu
命令确保最终可执行文件接收Unix信号:
#!/usr/bin/env 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
shell形式的 ENTRYPOINT
例子
您可以为ENTRYPOINT
指定一个纯字符串,它将在/bin/sh -c
中执行。这种形式将使用shell处理替换shell环境变量,并忽略任何CMD
或docker run
命令行参数。要确保docker stop
将正确地发出任何长时间运行的ENTRYPOINT
可执行文件的信号,您需要记住使用exec启动它:
FROM ubuntu
ENTRYPOINT exec top -b
运行此镜像时,将看到单个PID 1进程:
docker run -it --rm --name test top
/usr/bin/time docker stop test
理解CMD
与 ENTRYPOINT
是如何交互的
CMD
和ENTRYPOINT
指令都定义了运行容器时执行的命令。很少有规则能描述他们的合作。
- Dockerfile应至少指定一个
CMD
或ENTRYPOINT
命令。 - 当将容器用作可执行文件时,应定义入口点。
CMD
应该用作定义ENTRYPOINT
命令或在容器中执行特殊命令的默认参数的方法。- 使用可选参数运行容器时,将重写
CMD
。
VOLUME
VOLUME ["/data"]
VOLUME
通过指定一个具体的名字来创建一个挂载点,并把它标记为用来保存来自本机或其它容器的外部的装入的数据卷(太官方了,其实也就是当你把镜像内的一个文件夹设置为一个挂载点时,镜像运行起来后,就会把这个挂载点中的数据保存镜像外部–即宿主机的文件夹下)。
它是值可以是一个JSON数组,VOLUME ["/var/log/"]
, 或者带有多个参数的纯字符串,例如 VOLUME /var/log
or VOLUME /var/log /var/db
。可通过Docker客户端了解更多的信息、例子和挂载相关指令。 Share Directories via Volumes 。
docker run
命令使用存在基础境中的指定位置的任何数据 初始化新创建的卷。例如:
FROM ubuntu
#在镜像内部的根目录下创建myvol文件夹
RUN mkdir /myvol
# 在文件下创建greeting文件,内容为hello world
RUN echo "hello world" > /myvol/greeting
#把myvol设置为挂载点,这样运行docker run后,在宿主机/var/lib/docker下面就会生成一个由docker随机命名的一个目录,里面的内容即为镜像内挂载点myvol中的内容(即gretting文件)
VOLUME /myvol
指定数据卷注意事项
1、Windows容器下的数据卷:当使用基于windows环境的容器,容器内的目标数据卷必须是符合以下两点中的其中一个:
- 一个不存在的空目录
- c盘以外
2、从Dockerfile中更改数据卷:如果在构建过种中,修改了已经在数据卷中声明过的数据,则这些更改将被丢弃。
3、JSON格式化:数组的内容会被解析成JSON数组,所以必须用双引号
4、主机目录在容器运行时就会被声明:主机目录(挂载点)本质上依赖于主机。这是为了保持映像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,不能从Dockerfile中装载主机目录。卷指令不支持指定主机目录参数。创建或运行容器时必须指定装入点。