Docker 笔记(5)

Docker 可以通过 Dockerfile 的内容来自动构建镜像。Dockerfile 是一个包含创建镜像所有命令的文本文件,通过 docker build 命令可以根据 Dockerfile 的内容构建镜像,在介绍如何构建之前先介绍下 Dockerfile 的基本语法结构。

Dockerfile 有以下指令选项:

FROMMAINTAINERRUNCMDEXPOSEENVADDCOPYENTRYPOINTVOLUMEUSERWORKDIRONBUILD

FROM

用法:

1
FROM <image>
  • FROM 指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。
  • FROM 必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从 FROM 语句开始。
  • FROM 可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。
  • 如果 FROM 语句没有指定镜像标签,则默认使用 latest 标签。

MAINTAINER

用法:

1
MAINTAINER <name>

指定创建镜像的用户

RUN 有两种使用方式

1
2
RUN (the command is run in a shell - /bin/sh -c - shell form)
RUN [“executable”, “param1”, “param2”] (exec form)

每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的 RUN 都在之前 RUN 提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。

exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:

1
RUN [ "echo", "$HOME" ]

这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的

1
RUN [ "sh", "-c", "echo", "$HOME" ]

RUN 产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用 –no-cache 选项,即 docker build –no-cache,如此便不会缓存。

CMD

CMD 有三种使用方式:

1
2
3
CMD ["executable","param1","param2"] (exec form, this is the preferred form, 优先选择)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)

CMD 指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。

CMD 的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉 CMD 指定的命令。

CMD 会在启动容器的时候执行,build 时不执行,而 RUN 只是在构建镜像的时候执行,后续镜像构建完成之后,启动容器就与 RUN 无关了,这个初学者容易弄混这个概念,这里简单注解一下。

EXPOSE

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

告诉 Docker 服务端容器对外映射的本地端口,需要在 docker run 的时候使用 -p 或者 -P 选项生效。

ENV

1
2
ENV <key> <value>       # 只能设置一个变量
ENV <key>=<value> ... # 允许一次设置多个变量

指定一个环节变量,会被后续 RUN 指令使用,并在容器运行时保留。

例子:

1
2
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy

等同于

1
2
3
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

ADD

1
ADD <src>... <dest>

ADD 复制本地主机文件、目录或者远程文件 URLS 从 并且添加到容器指定路径中

支持通过 GO 的正则模糊匹配

1
2
ADD hom* /mydir/        # adds all files starting with "hom"
ADD hom?.txt /mydir/ # ? is replaced with any single character
  • 路径必须是绝对路径,如果 不存在,会自动创建对应目录
  • 路径必须是 Dockerfile 所在路径的相对路径
  • 如果是一个目录,只会复制目录下的内容,而目录本身则不会被复制

COPY

1
COPY <src>... <dest>

COPY 复制新文件或者目录从 添加到容器指定路径中 。用法同 ADD,唯一的不同是不能指定远程文件 URLS。

ENTRYPOINT

1
2
ENTRYPOINT ["executable", "param1", "param2"] (the preferred exec form,优先选择)
ENTRYPOINT command param1 param2 (shell form)

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而 CMD 是可以被覆盖的。如果需要覆盖,则可以使用 docker run –entrypoint 选项。

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效。

Exec form ENTRYPOINT 例子

通过 ENTRYPOINT 使用 exec form 方式设置稳定的默认命令和选项,而使用 CMD 添加默认之外经常被改动的选项。

1
2
3
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

通过 Dockerfile 使用 ENTRYPOINT 展示前台运行 Apache 服务

1
2
3
4
5
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"]

Shell form ENTRYPOINT

这种方式会在 /bin/sh -c 中执行,会忽略任何 CMD 或者 docker run 命令行选项,为了确保 docker stop 能够停止长时间运行 ENTRYPOINT 的容器,确保执行的时候使用 exec 选项。

1
2
FROM ubuntu
ENTRYPOINT exec top -b

如果在 ENTRYPOINT 忘记使用 exec 选项,则可以使用 CMD 补上:

1
2
3
FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此类推

VOLUME

创建一个可以从本地主机或其他容器挂载的挂载点

1
VOLUME ["/data"]

USER

1
2
3
USER daemon

#: 指定运行容器时的用户名或 UID,后续的 RUN、CMD、ENTRYPOINT 也会使用指定用户。

WORKDIR

1
WORKDIR /path/to/workdir

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。

1
2
3
4
5
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#: 最终路径是 /a/b/c。

WORKDIR 指令可以在 ENV 设置变量之后调用环境变量:

1
2
3
4
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME

#: 最终路径是 /path/$DIRNAME。

ONBUILD

1
ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

例如,Dockerfile 使用如下的内容创建了镜像 image-A:

1
2
3
4
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。

1
2
3
# Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src