边用边学 docker 系列 之 Dockerfile及其指令

162 阅读7分钟

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroupnamespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。

1 FROM指定基础镜像

FROM 指令用于指定其后构建新镜像所使用的基础镜像。该指令必须是Dockerfile文件中的首条命令(非注释行)。启动构建流程后,Docker将会基于该镜像构建新的镜像,FROM后的命令也会基于这个基础镜像.

FROM语法格式为

FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>

注意事项:

  • FROM 必须 是 Dockerfile 中第一条非注释命令
  • 在一个 Dockerfile 文件中创建多个镜像时,FROM 可以多次出现。只需在每个新命令 FROM 之前,记录提交上次的镜像 ID。
  • tag 或 digest 是可选的,如果不使用这两个值时,会使用 latest 版本的基础镜像

2 WORKDIR 指定工作目录

WORKDIR 用于在容器内设置一个工作目录:

WORKDIR /path/to/workdir

通过WORKDIR设置工作目录后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。 如,使用WORKDIR设置工作目录:

WORKDIR /test
WORKDIR test1
WORKDIR test2
RUN pwd

在以上示例中,pwd 最终将会在 /test/test1/test2 目录中执行。在使用 docker run 运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。

3 RUN 命令执行

在构建镜像的过程中需要执行的特定命令,生成一个中间的镜像

#shell格式
RUN <command> #RUN apk updat
#exec格式
RUN ["executable", "param1", "param2"]  #RUN ["/bin/bash","-c","yum install -y gcc"]  #json序列

注意事项

  • RUN命令将在当前image中执行任意合法命令并提交结果,命令执行提交后,就会自动执行Dockerfile中的下一个命令
  • 层级RUN指令和生成提交是符合Dockerh核心理念,它允许像版本控制那样在任意一个点对镜像进行定制化构建
  • RUN指令创建的中间镜像会被缓存,并在下次构建中使用,如不想使用缓存镜像,可以在构建过程中指定--no-cache 参数

4 COPY&ADD 复制文件

命令使用方式

COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
ADD <源路径>... <目标路径>
ADD ["<源路径>",... "<目标路径>"]

对于COPY 和ADD命令来说如果要把本地文件copy到镜像中,本地文件必须包含在上下文目录中(该命令不能copy上下文之外的本地文件到镜像中)。

COPY 和ADD命令与WORKDIR协同工作,在设置了 WORKDIR 命令后,接下来的 COPY 和 ADD 命令中的相对路径就是相对于 WORKDIR 指定的路径

WORKDIR /app
COPY test.sh .

构建后生产的镜像中文件路径/app/test.sh,test.sh 文件就是被复制到了 WORKDIR /app 目录下。

4.1 COPY 复制文件

如果仅仅是把本地的文件拷贝到容器镜像中,COPY 命令是最合适不过的。其命令的格式为:
COPY

除了指定完整的文件名外,COPY 命令还支持 Go 风格的通配符,比如:

COPY test* /testdir/           # 拷贝所有 test 开头的文件
COPY test?.log /testdir/       # ? 是单个字符的占位符,比如匹配文件 test1.log

对于目录而言,COPY 和 ADD 命令具有相同的特点:只复制目录中的内容而不包含目录自身。 比如我们在 Dockerfile 中添加下面的命令:

image.png

WORKDIR /app
COPY test .

构建后生产的镜像中文件路径

/app
test1.log, test2.log

test1.log, test2.log文件就是被复制到了 WORKDIR /app 目录下,但是并没有包含test目录本身。如果想让 test1.log 和 test2.log 还保存在 test目录中,需要在目标路径中指定这个目录的名称,如

WORKDIR /app/test  #方式1

======================================
WORKDIR /app       #方式2
COPY test ./test

COPY 命令区别于 ADD 命令的一个用法是在 multistage (多阶段构建)场景下,在 multistage 的用法中,可以使用 COPY 命令把前一阶段构建的产物拷贝到另一个镜像中,比如: image.png

4.2 ADD 复制文件

ADD 命令的格式和 COPY 命令相同,也是: ADD <src> <dest>

但是ADD命令不能用在 multistage 的场景下,不过除了这一点外,ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类超酷的功能:

  • 解压压缩文件并把它们添加到镜像中,当是可识别的压缩包如gzip、bzip2等tar包时,首先会将包添加到镜像中,然后自动解压。这可以说是与COPY命令在使用中的最大的区别,最佳使用场景:
WORKDIR /app
ADD .tar.gz .
  • 从 url 拷贝文件到镜像中,但是docker官方文档最佳实践并不建议这么用,docker 官方建议我们当需要从远程复制文件时,最好使用 curl 或 wget 命令来代替 ADD 命令。原因是,当使用 ADD 命令时,会创建更多的镜像层,当然镜像的 size 也会更大

image.png

5 MAINTAINER

格式为 MAINTAINER <name>,指定维护者信息。

注意:MAINTAINER 指令已经被抛弃,建议使用 LABEL 指令。

LABEL

格式为:LABEL <key>=<value> <key>=<value> <key>=<value>

6 ENV设置环境变量

格式有两种:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。

image.png

7 EXPOSE 容器监听端口

为构建的镜像设置监听端口,使容器在运行时监听。格式:

EXPOSE <port> [<port>...]

EXPOSE 指令并不会让容器监听宿主机的端口,监听主机端口需要在 docker run 时使用 -p-P 参数来发布容器端口到宿主机的某个端口上。

8 VOLUME 定义卷

VOLUME用于创建挂载点,即向基于所构建镜像创始的容器添加卷,格式为:

VOLUME ["/data"]

也可以使用 VOLUME 指令添加多个数据卷:

VOLUME ["/data1", "/data2"]

一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:

  • 卷可以容器间共享和重用
  • 容器并不一定要和其它容器共享卷
  • 修改卷后会立即生效
  • 对卷的修改不会对镜像产生影响
  • 卷会一直存在,直到没有任何容器在使用它

VOLUME 让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容。

9 CMD

CMD用于指定在容器启动时所要执行的命令。CMD 有以下三种格式:

CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2

省略可执行文件的 exec 格式,这种写法使 CMD 中的参数当做 ENTRYPOINT 的默认参数,此时 ENTRYPOINT 也应该是 exec 格式,具体与 ENTRYPOINT 的组合使用,参考 ENTRYPOINT。

注意事项: 与 RUN 指令的区别:RUN 在构建的时候执行,并生成一个新的镜像,CMD 在容器运行的时候执行,在构建时不进行任何操作。

10 ENTRYPOINT

ENTRYPOINT 用于给容器配置一个可执行程序。也就是说,每次使用镜像创建容器时,通过 ENTRYPOINT 指定的程序都会被设置为默认程序。ENTRYPOINT 有以下两种形式:

ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2

ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数(经典操作:ENTRYPOINT + CMD = 默认容器命令参数),不同的是通过docker run执行的命令不会覆盖 ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给 ENTRYPOINT。Dockerfile 中只允许有一个 ENTRYPOINT 命令,多指定时会覆盖前面的设置,而只执行最后的 ENTRYPOINT 指令。

docker run运行容器时指定的参数都会被传递给 ENTRYPOINT ,且会覆盖 CMD 命令指定的参数。如,执行docker run <image> -d时,-d 参数将被传递给入口点。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为: <ENTRYPOINT> "<CMD>"

也可以通过docker run --entrypoint重写 ENTRYPOINT 入口点。

image.png