dockerfile指令
目录
Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction), 每一条指令构建一层 ,因此每一条指令的内容,就是描述该层应当如何构建。
常用指令
ADD 和 COPY
COPY:
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则 <目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
ADD:将 host 上的文件拷贝到或者将网络上的文件下载到容器中的指定目录
ADD [source directory or URL] [destination directory]
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
ADD 和 COPY 的区别
- ADD 多了 2 个功能, 下载 URL 和对支持的压缩格式的包进行解压. 其他都一样。比如 ADD http://foo.com/bar.go
/tmp/main.go
会将文件从因特网上方下载下来,ADD /foo.tar.gz/tmp/
会将压缩文件解压再 COPY 过去 - 如果你不希望压缩文件拷贝到 container 后会被解压的话, 那么使用 COPY。
- 如果需要自动下载 URL 并拷贝到 container 的话, 请使用 ADD
在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。
ARG
ARG 构建参数
ARG <参数名>[=<默认值>]
构建参数和 ENV 的效果一样,都是设置环境变量。 所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。 但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。
CMD
CMD:在容器被创建后执行的命令,和 RUN 不同,它是在构造容器时候所执行的命令
CMD 有三种格式: * CMD ["executable","param1","param2"]
(like an exec, preferred form) * CMD ["param1","param2"]
(作为 ENTRYPOINT 的参数) * CMD command param1 param2
(作为 shell 运行)
一个 Dockerfile 里只能有一个 CMD,如果有多个,只有最后一个生效。
对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
ENTRYPOINT
ENTRYPOINT :设置默认应用(入口点),会保证每次容器被创建后该应用都会被执行。
ENTRYPOINT 格式与 CMD 类似。当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。
CMD 和 ENTRYPOINT 的关系会在下面详细解释。
ENV
ENV 有连各种格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
设置环境变量,可以使用多次。 设置了后,后续的 RUN 命令都可以使用,并且会作为容器的环境变量。
EXPOSE
声明端口
格式为 EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。 在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射; 另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
FROM
指定进行的基础镜像,必须是第一条指令
Usage: FROM [image name]
如果以 scratch
为基础镜像的话,意味着不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
MAINTAINER
可以在任意地方使用,设置镜像的作者
Usage: MAINTAINER [name]
ONBUILD
格式: ONBUILD <其它指令>
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。
RUN
运行命令,结果会生成镜像中的一个新层
格式:
- shell 格式:=RUN <命令>=,就像直接在命令行中输入的命令一样。
- exec 格式:=RUN ["可执行文件", "参数 1", "参数 2"]=,这更像是函数调用中的格式。
为了减少镜像大小起见,所有文件相关的操作,比如删除,释放和移动等,都需要尽可能地放在一个 RUN 指令中进行。 而且在镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉,包括缓存、临时文件等。
USER
设置该镜像的容器的主进程所使用的用户,以及后续 RUN, CMD 和 ENTRYPOINT 指令运行所使用的用户
格式: USER <用户名>[:<用户组>]
USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
Dockerfile 中的默认用户是基础镜像中所使用的用户。 比如,你的镜像是从一个使用非 root 用户 sammy 的镜像继承而来的,那么你的 Dockerfile 中 RUN 指定运行的命令的用户就会使用 sammy 用户。
VOLUME
定义匿名卷,允许容器访问 host 上某个目录
格式为: * VOLUME ["<路径1>", "<路径2>"...]
* VOLUME <路径>
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷。 这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
WORKDIR
指定工作目录
格式为 WORKDIR <工作目录路径>
使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
HEALTHCHECK
容器健康检查
格式:
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
和 CMD, ENTRYPOINT 一样,HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。
在 HEALTHCHECK [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样,分为 shell 格式,和 exec 格式。 命令的返回值决定了该次健康检查的成功与否:0:成功;1:失败;2:保留,不要使用这个值。
容易混淆的地方
EXPOSE 和 docker run -p -P 之间的关系
容器的端口必须被发出(publish)出来后才能被外界使用。Dockerfile 中的 EXPOSE 只是“标记”某个端口会被暴露出来,只有在使用了 docker run -p 或者 -P 后,端口才会被“发出”出来,此时端口才能被使用。
-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
- EXPOSE 或者–expose 只是为其他命令提供所需信息的元数据,或者只是告诉容器操作人员有哪些已知选择。它只是作为记录机制,也就是告诉用户哪些端口会提供服务。它保存在容器的元数据中。
- 使用 -p 发布特定端口。如果该端口已经被 exposed,则发布它;如果它还没有被 exposed,则它会被 exposed 和 published。Docker 不会检查容器端口的正确性。
- 使用 -P 时 Docker 会自动将所有已经被 exposed 的端口发出出来。
CMD 和 ENTRYPOINT
这两个指令都指定了运行容器时所运行的命令。以下是它们共存的一些规则:
- Dockerfile 至少需要指定一个 CMD 或者 ENTRYPOINT 指令
- CMD 可以用来指定 ENTRYPOINT 指令的参数
没有 ENTRYPOINT | ENTRYPOINT exec\_entry p1\_entry | ENTRYPOINT [“exec\_entry”, “p1\_entry”] | |
---|---|---|---|
没有 CMD | 错误,不允许 | /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 |
备注 | 只有 CMD 时,执行 CMD 定义的指令 | CMD 和 ENTRYPOINT 都存在时,CMD 的指令作为 ENTRYPOINT 的参数 |
镜像构建上下文(Context)
docker build [选项] <上下文路径/URL/->
docker build
命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。 当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。 这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
=ADD=和=COPY=等指令的源文件根目录也是上下文路径的目录。因此,=COPY=这类指令中的源文件的路径都是相对路径。
.dockerignore 文件可以用于剔除不需要的文件作为上下文传递给 Docker 引擎
在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile。
其他
多段式构建
我们可以使用 as 来为某一阶段命名,例如
FROM golang:1.9-alpine as builder
当我们只想构建 builder 阶段的镜像时,增加 –target=builder 参数即可
$ docker build --target builder -t username/imagename:tag .
从上一阶段的镜像中复制文件
COPY --from=0 /go/src/github.com/go/helloworld/app .
我们也可以复制任意镜像中的文件
$ COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf