docker技术入门与实战 第3版学习笔记之第8章

375 阅读17分钟

第8章 使用Dockerfile创建镜像

Dockerfile ​是一个文本格式的配置文件,用户可以使用 Dockerfile ​来快速创建自定义的镜像。

基本结构

Dockerfile​ 由一行行命令语句组成,并且支持以#开头​的注释行。

一般而言, Dockerfile ​主体内容分为四部分:基础镜像信息、维护者信息、镜像操作指 令和容器启动时执行指令。

简单示例:

escape=\ (backslash)
# 转义字符 \ (backslash)

# This dockerfile uses the ubuntu:xeniel image
#此 dockerfile 使用 ubuntuxeniel 映像

# VERSION 2 - EDITION 1Author:docker userCommand format:Instruction [arguments / command]
# 命令格式:指令 [arguments command]Base image to use,this must be set as the first line
# 基础映像,必须作为第一行设置
FROM ubuntu:xenielMaintainer:docker user <docker user at email.com>(@docker user)
# 作者信息:docker user <docker user at email.com>(@docker user)
# 教程中存在问题
LABEL authors="作者名称"

RUN echo "deb http://archive.ubuntu.com/unubtu xeniel main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.confCommands when creating a new container
# 创建容器时运行的命令

CMD /usr/sbin/nginx

首行可以通过注释来指定解析器命令,后续通过注释说明镜像的相关信息。主体部分首先使用FROM​指令指明所基于的镜像名称​,接下来一般是使用LABEL​指令说明维护者信息​。后面则是镜像操作指令,例如RUN​指令将对镜像执行跟随的命令。每运行一条RUN指令​,镜像添加新的一层​,并提交。最后是CMD指令,来指定运行容器时的操作命令。

例子

debian:jessie​ 基础镜像基础上安装 Nginx ​环境

#debian:jessie基础镜像基础上安装 Nginx 环境 dockerfile

#基础镜像 使用 Debian 8.0 (jessie) 作为基础镜像。
FROM debian:jessie

#维护者信息 为镜像添加一个作者标签,标签名为 "author",值为你自己的用户名
LABEL author="username"

# 环境变量 定义一个环境变量 NGINX_VERSION,其值为 1.10.1-1~jessie。
ENV NGINX_VERSION 1.10.1-1~jessie

# 跟随指令
# 通过 GPG 密钥服务器获取 Nginx 公钥,以在安装过程中验证下载的 Nginx 软件包。
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABFSBD827BD9BF62 \ 
 # 将 Nginx 的 Debian 包源添加到系统列表中。
    && echo "deb http://nginx.org/packages/debian/ jessie nginx" >> /etc/apt/sources.list \
# 更新系统列表。
    && apt-get update \ 
# 安装 Nginx 及其依赖项,但不会安装推荐和建议的依赖项。
    && apt-get install --no-install-recommends --no-install-recommends --no-install-suggests -y \
# 安装根证书。
    ca-certificates \
# 安装指定的 Nginx 版本。
    nginx=${NGINX_VERSION} \
#装 Nginx 模块,如 XSLT、GeoIP、ImageFilter、Perl、NJS
    nginx-module-xslt \
    nginx-module-geoip \
    nginx-module-image-filter \
    nginx-module-perl \
    nginx-module-njs \
#安装 Nginx 基本组件。
    nginx-base \
#删除不再需要的包列表。
    && rm -rf /var/lib/apt/lists/*

# forward request and error logs to docker log collector
# 将请求和错误日志转发到 Docker 日志收集器 将访问日志转发到标准输出。
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
# 将错误日志转发到标准错误。
    && ln -sf /dev/stderr /var/log/nginx/error.log \


# 端口映射 暴露 80 和 443 端口。
EXPOSE 80 443

# 启动命令 启动 Nginx 服务。
CMD ["nginx""-g""daemon off;"]

buildpack-deps:jessie-scm​ 基础镜像,安装 Golang ​相关环境

# buildpack-deps:jessie-scm 基础镜像,安装 Golang 相关环境

#基础镜像
# 指定基础镜像为 Debian 8.10 (jessie) 的 buildpack-deps 镜像,该镜像包含了许多常用的依赖包。
FROM buildpack-deps:jessie-scm

# gcc for cgo
# gcc 编译 cgo 包
# 安装必要的编译器、库和构建工具。
RUN apt-get update && apt-get install -y --no-install-recommends \
 g++ \
    gcc \
    libc6-dev \
    make \
    && rm -rf /var/lib/apt/lists/*

# 环境变量 \
ENV GOLANG_VERSION 1.6.3
# 定义 Golang 下载 URL,其中 $GOLANG_VERSION 变量将替换为实际版本号。
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
# 定义 Golang 下载 SHA256 校验和。
ENV GOLANG_DOWNLOAD_SHA256 cdde5e08530c0579255d6153b08fdb3b8e47caabbe717bc7bcd7561275a87aeb

# 运行指令 下载并安装 Golang 包。
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
    && echo "$GOLANG_DOWNLOAD_SHA256  golang.tar.gz" | sha256sum -c - \
    && tar -C /usr/local -xzf golang.tar.gz \
    && rm golang.tar.gz

# 设置 GOROOT 环境变量
# 设置 Golang 根目录环境变量。
ENV GOROOT /go
# 设置 PATH 环境变量,以优先使用 Golang 工具。
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
# 运行指令
#  创建 Golang 工作目录和二进制目录,并确保所有目录具有可写权限。
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" WORKDIR $GOPATH

#拷贝文件 将名为 go-wrapper 的工具拷贝到 /usr/local/bin/ 目录。
COPY go-wrapper /usr/local/bin/

指令说明

Dockerfile 中指令的一般格式为 INSTRUCTION arguments​, 包括“配置指令"(配置 镜像信息)和“操作指令"具体执行操作)

分类指令描述
配置指令ARG定义创建镜像过程中使用的变量,设置构建时传递给构建器的变量
FROM指定所创建的镜像的基础镜像,设定初始镜像
LABEL标注构建者的信息及镜像说明
EXPOSE指定容器内服务监听的端口
ENV环境变量配置
ENTRYPOINT指定镜像的默认入口命令
VOLUME创建一个数据卷挂载点或挂载点目录
USER指定运行容器时的用户或UID
WORKDIR工作目录的设置
ONBUILD创建子镜像时指定自动执行的操作指令,
STOPSIGNAL设置终止或退出容器的信号
HEALTHCHECK配置所启动容器如何进行健康检查,健康检查指令,如设定运行状态检查命令
SHELL容器默认shell类型
操作指令RUN运行指定的命令
CMD启动容器时指定的默认操作指令
ADD添加内容到镜像
COPY复制内容到镜像

配置指令

ARG

定义创建镜像过程中使用的变量,格式是ARG <name>[=<默认值>]

构建参数和 ENV​ 的效果一样,都是设置环境变量。所不同的是,ARG​ 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG​ 保存密码之类的信息,因为 docker history​ 还是可以看到所有值的。

Dockerfile​ 中的 ARG​ 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build​ 中用 --build-arg <参数名>=<值>​ 来覆盖。当镜像编译成功后,ARG​指定的变量将不再存在 (ENV ​指定的变量将在镜像中保留)。

灵活的使用 ARG​ 指令,能够在不修改 Dockerfile 的情况下,构建出不同的镜像。

ARG ​指令有生效范围,如果在 FROM​ 指令之前指定,那么只能用于 FROM​ 指令中,FROM​ 指令可以是多个

Docker内置了一些镜像创建变量,用户可以直接使用而无须声明,包括(不区分大小写)HTTP PROXY​、HTTPS_PROXY​、FTP_PROXY​、NO_PROXY​。

ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo ${DOCKER_USERNAME}

使用上述 Dockerfile 会发现无法输出 ${DOCKER_USERNAME}​ 变量的值,要想正常输出,你必须在 FROM​ 之后再次指定 ARG​,如下示例

# 只在 FROM 中生效
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine

# 要想在 FROM 之后使用,必须再次指定
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}

如下示例,变量将会在每个 FROM​ 指令中生效

# 这个变量在每个 FROM 中都生效
ARG DOCKER_USERNAME=library

FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo 1

FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo 2

如下示例,对于在各个阶段中使用的变量都必须在每个阶段分别指定

ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine

# 在FROM 之后使用变量,必须在每个阶段分别指定
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}

FROM ${DOCKER_USERNAME}/alpine
# 在FROM 之后使用变量,必须在每个阶段分别指定
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}

由于ARG​是在构建时的变量,因此无法打印,将其设置为env​环境变量可以看到

# 只在 FROM 中生效
ARG DOCKER_USERNAME=library
FROM ubuntu

# 要想在 FROM 之后使用,必须再次指定
ARG DOCKER_USERNAME=bbb
ENV releaser=$DOCKER_USERNAME
RUN  echo ${releaser}

image.png

FROM

指定所创建镜像的基础镜像,格式是 FROM IMAGE[:TAG][@DIGEST]

# tag 默认使用 latest 
FROM alpine

# 指定 tag
FROM alpine:3.17.3

# 指定 digest
FROM alpine@sha256:b6ca290b6b4cdcca5b3db3ffa338ee0285c11744b4a6abaa9627746ee3291d8d

# 同时指定 tag 和 digest
FROM alpine:3.17.3@sha256:b6ca290b6b4cdcca5b3db3ffa338ee0285c11744b4a6abaa9627746ee3291d8d

任何 Dockerfile​ 中第一条指令必须为 FROM​ 指令。并且,如果在同一个 Dockerfile​ 中创 建多个镜像时,可以使用多个FROM​指令(每个镜像一次)。

为了保证镜像精简,可以选用体积较小的镜像如 AlpineDebian​ 作为基础镜像

LABEL

LABEL​ 指令用来给镜像以键值对的形式添加一些元数据(metadata)。这些信息可以用来辅助过滤出特定镜像。格式为LABEL <key>=<value> <key>=<value> <key>=<value> ...

还可以用一些标签来申明镜像的作者、文档地址等

#声明版本
LABEL version="1.0.1"
# 声明作者
LABEL author="作者"
#LABEL org.opencontainers.image.authors="作者"
# 声明镜像描述
LABEL description="镜像描述"
# 声明文档地址
LABEL org.opencontainers.image.documentation="文档地址"
LABEL org.opencontainers.image.vendor="镜像类型"

通过docker inspect​查看镜像的详细信息

image.png 具体可以参考 github.com/opencontain…

org.opencontainers.image​前缀org.label-schema​前缀兼容性说明
createdbuild-date相容
urlurl相容
sourcevcs-url相容
versionversion相容
revisionvcs-ref相容
vendorvendor相容
titlename相容
descriptiondescription相容
documentationusage如果文档通过 URL 定位,则值是兼容的
authors标签架构中没有等效项
licenses标签架构中没有等效项
ref.name标签架构中没有等效项
schema-versionOCI 镜像规范中没有等效项
docker.*​,rkt.*OCI 镜像规范中没有等效项
EXPOSE

声明镜像内服务监听的端口,EXPOSE <端口1> [端口2] ...​,例如EXPOSE 22 80 443

注意该指令只是起到声明作用,并不会自动完成端口映射。如果要映射端口出来,在启动容器时可以使用-p​参数.

在 Dockerfile 中写入这样的声明有两个好处:

  • 一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;
  • 另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P​ 时,会自动随机映射 EXPOSE​ 的端口。

要将 EXPOSE​ 和在运行时使用 -p <宿主端口>:<容器端口>​ 区分开来。-p​,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE​ 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。

ENV

指定环境变量,在镜像生成过程中会被后续RUN​指令使用,在镜像启动的容器中也会存在。

格式有两种:

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

ENV​ 用于设置环境变量,既可以在 Dockerfile​ 中调用,也可以在构建完的容器运行时中使用。

支持的指令: ADD​、COPY​、ENV​、EXPOSE​、FROM​、LABEL​、USER​、WORKDIR​、VOLUME​、STOPSIGNAL​、ONBUILD​、RUN

指令指定的环境变量在运行时可以被覆盖掉,后面的会覆盖前面的

ENTRYPOINT

指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传人值作为该命令的参数。

ENTRYPOINT​ 的格式和 RUN​ 指令格式一样,分为 exec​ 格式和 shell​ 格式。

  • shell​ 格式:ENTRYPOINT [command] <parameters>
  • exec​ 格式:ENTRYPOINT ["command", "<parameter1>", "<parameter2>", ...]

ENTRYPOINT​ 的目的和 CMD​ 一样,都是在指定容器启动程序及参数。ENTRYPOINT​ 在运行时也可以替代,不过比 CMD​ 要略显繁琐,需要通过 docker run​ 的参数 --entrypoint​ 来指定。

VOLUME

创建一个数据卷挂载点

格式为:

  • VOLUME ["<路径1>", "<路径2>"...]
  • VOLUME <路径>

运行容器时可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保持的数据等

容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。

为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile​ 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据,从而保证了容器存储层的无状态化。

VOLUME​ 创建的匿名卷会挂载到系统 /var/lib/docker/volumes/<CONTAINER-ID>/<_VOLUME>​ 目录下,且不会随着容器删除而删除,需要手动删除

USER

指定运行容器时的用户名或UID,后续的run​等指令也会使用指定的用户身份。

格式:USER <用户名>[:<用户组>]

USER​ 指令和 WORKDIR​ 相似,都是改变环境状态并影响以后的层。WORKDIR​ 是改变工作目录,USER​ 则是改变之后层的执行 RUN​, CMD​ 以及 ENTRYPOINT​ 这类命令的身份。

注意,USER​ 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。

如果以 root​ 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su​ 或者 sudo​,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 ``​

WORKDIR

为后续的RUN​、CMD​、ENTRYPOINT​指令配置工作目录

格式为 WORKDIR <路径>

使用 WORKDIR​ 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR​ 会帮你建立目录

如下示例,是一个常见的错误,world.txt​ 最终会在 /app​ 目录下,而不是期望的 /app/demo​ 目录

WORKDIR /app
RUN mkdir demo && cd demo
RUN echo "hello" > world.txt

上述需求可以进行如下优化,推荐使用第二种写法

WORKDIR /app/demo
RUN echo "hello" > world.txt

# 或者
WORKDIR /app
RUN mkdir demo \
    && echo "hello" > demo/world.txt

# 或者
WORKDIR /app
RUN mkdir demo \
    && cd demo \
    && echo "hello" > demo/world.txt

如果你的 WORKDIR​ 指令使用的相对路径,那么所切换的路径与之前的 WORKDIR​ 有关

ONBUILD

指定当基于所生成的镜像创建子镜像时,自动执行的操作指令

格式:ONBUILD <其它指令>​。

ONBUILD​ 是一个特殊的指令,它后面跟的是其它指令,比如 RUN​, COPY​ 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

Dockerfile​ 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD​ 是为了帮助别人定制自己而准备的。

STOPSIGNAL

指定所创建的镜像启动的容器接受退出的信号值

HEALTHCHECK

配置所启动容器如何进行健康检查

格式:

  • HEALTHCHECK [选项] CMD <命令>​:设置检查容器健康状况的命令
  • HEALTHCHECK NONE​:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK​ 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。

在没有 HEALTHCHECK​ 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。

HEALTHCHECK​ 支持下列选项:

  • --interval=<间隔>​:两次健康检查的间隔,默认为 30 秒;
  • --timeout=<时长>​:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
  • --retries=<次数>​:当连续失败指定次数后,则将容器状态视为 unhealthy​,默认 3 次。

CMD​, ENTRYPOINT​ 一样,HEALTHCHECK​ 只可以出现一次,如果写了多个,只有最后一个生效。

HEALTHCHECK [选项] CMD​ 后面的命令,格式和 ENTRYPOINT​ 一样,分为 shell​ 格式,和 exec​ 格式。命令的返回值决定了该次健康检查的成功与否:

  • 0​:成功;
  • 1​:失败;
  • 2​:保留,不要使用这个值。

SHELL

指定其他命令使用shell​时的默认shell​类型

格式:SHELL ["executable", "parameters"]

SHELL​ 指令可以指定 RUNENTRYPOINTCMD​ 指令的 shell,Linux 中默认为 ["/bin/sh", “-c”]

对于 Windows 系统​, Shel1 路径中使用了“\”​作为分隔符,建议在Dockerfile​开头添 加# escape='​来指定转义符。

操作指令

RUN

运行指定的命令

格式为RUN<command>​或RUN["executable","paraml","param2"]​。注意后者指令会被解析为JSON数组​,因此必须用双引号。前者默认将在shll终端中运行命令,即/bin/sh-c​;后者则使用exec执行,不会启动shel环境。

指定使用其他终端类型可以通过第二种方式实现,例如 RUN ["/bin/bash ", "-c", "echo hello"]`​

每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像层。当命令较长时 可以使用​来换行

CMD

CMD​指令用来指定启动容器时默认执行的命令

  • CMD ["executable","paraml","param2"]:相当于执行executable paraml param2,​推荐方式;
  • CMD command paraml param2:在默认的Shell​中执行,提供给需要交互的应用;
  • CMD ["param1","param2"]:提供给NTRYPOINT​的默认参数。

每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。 如果用户启动容器时候手动指定了运行的命令(作为 run 命令的参数),则会覆盖掉 CMD 指定的命令。

ADD

添加内容到镜像

格式为ADD <src> <dest>​,该命令将复制指定的<src>​路径下内容到容器中的<dest>​路径下

其中<src>​可以是Dockerfile​所在目录的一个相对路径(文件或目录)​;也可以是一个URL​;还可以是一个tax文件(自动解压为目录)<dest>​可以是镜像内绝对路径​,或者相对于工作目录(WORKDIR)的相对路径​。

COPY

复制内容到镜像

格式COPY <src> <dest>

复制本地主机的<src>​(为Dockerfile所在目录的相对路径,文件或目录)下内容到镜像中的<dest>​。目标路径不存在时,会自动创建。

路径同样支持正则格式。

COPY与ADD指令功能类似,当使用本地目录为源目录时,推荐使用COPY。

参考:docker | dockerfile指令详解-腾讯云开发者社区-腾讯云 (tencent.com)

创建镜像

编写完成 Dockerfile 之后,可以通过 docker [image] build​ 命令来创建镜像。

基本的格式为 docker build [OPTIONS] PATH |URL |-

该命令将读取指定路径下(包括子目录)的 Dockerfile, 并将该路径下所有数据作为上下 (Context) 发送给 Docker 服务端。Docker 服务端在校验 Dockerfile 格式通过后,逐条执行其中定义的指令,碰到 ADD COPY RUN 指令会生成一层新的镜像。最终如果创建镜像成功会返回最终镜像的ID

如果上下文过大,会导致发送大量数据给服务端,延缓创建过程。因此除非是生成镜像所必需的文件,不然不要放到上下文路径下。如果使用非上下文路径下的Dockerfile,可以
通过-f​选项来指定其路径。

要指定生成镜像的标签信息,可以通过-t​选项。该选项可以重复使用多次为镜像一次添加多个名称。

命令选项

「选项」「说明」
--add-host list「添加自定义的主机名到IP的映射」
--build-arg list「添加构建时的变量」
--cache-from strings「使用指定镜像作为缓存源」
--cgroup-parent string「指定的上层 cgroup」
--compress「使用 gzip 来压缩构建上下文发送」
--cpu-period int「分配的 CFS 调度器时长」
--cpu-quota int「CFS 调度器总份额」
-c, --cpu-shares int「CPU 权重」
--cpuset-cpus string「多CPU 允许使用的 CPU」
--cpuset-mems string「多 CPU 允许使用的内存」
--disable-content-trust「不进行镜像校验,禁止为真」
-f, --file string「Dockerfile 名称」
--force-rm「总是删除中间过程的容器」
--iidfile string「将镜像 ID 写入到文件」
--isolation string「容器隔离技术」
--label list「设置镜像的标签」
-m, --memory bytes「限制内存使用的最大值」
--memory-swap bytes「限制内存和缓存的总数值」
--network string「指定 RUN 命令的网络模式」
--no-cache「创建镜像时不使用缓存」
--platform string「指定平台类型」
--pull「总是尝试获取更新的基础镜像」
-q, --quiet「不打印构建过程中的日志信息」
--rm「删除成功后自动删除中间件容器」
--security-opt strings「指定安全相关的选项」
--shm-size bytes/dev/shm​的大小
--squash「将新创建的层压缩到单一层中」
--stream「将构建过程输出为流」
-t, --tag list「指定镜像的标签列表」
--target string「指定构建的目标阶段」
--ulimit ulimit「指定 ulimit 的配置」

选择父镜像

大部分情况下,生成新的镜像都需要通过FROM​指令来指定父镜像。父镜像是生成镜像的基础,会直接影响到所生成镜像的大小和功能。用户可以选择两种镜像作为父镜像,一种是所谓的基础镜像(baseimage),另外一种是普通的镜像(往往由第三方创建,基于基础镜像)。基础镜像比较特殊,:其Dockerfile中往往不存在FROM指令,或者基于scratch镜像(FROM scratch),这意味着其在整个镜像树中处于根的位置。

使用.dockerignore​ 文件

可以通过.dockerignore ​文件(每一行添加一条匹配模式)来让 Docker 忽略匹配路 径或文件,在创建镜像时候不将无关数据发送到服务端。

多步骤创建

自17.05版本开始,Docker支持多步骤镜像创建(Multi--stage build)特性,可以精简最终生成的镜像大小。

对于需要编译的应用(如C、Go或Java语言等)来说,通常情况下至少需要准备两个环境的Docker镜像:

  • 编译环境镜像:包括完整的编译引擎、依赖库等,往往比较庞大。作用是编译应用为二进制文件;
  • 运行环境镜像:利用编译好的二进制文件,运行应用,由于不需要编译环境,体积比较小。

使用多步骤创建,可以在保证最终生成的运行环境镜像保持精简的情况下,使用单一的Dockerfile,降低维护复杂度。

最佳实践

精简镜像用途:尽量让每个镜像的用途都比较集中单一,避免构造大而复杂、多功能 的镜像

选用合适的基础镜像:容器的核心是应用。选择过大的父镜像(如Ubuntu系统镜像)会造成最终生成应用镜像的臃肿,推荐选用瘦身过的应用镜像(如node:slim),或者较为小巧的系统镜像(如alpine、busybox或debian);

提供注释和维护者信息: Dockerfile 也是一种代码,需要考虑方便后续的扩展和他人 的使用;

正确使用版本号:使用明确的版本号信息,如 1.0, 2.0, 而非依赖于默认的 la es 七。 通过版本号可以避免环境不一致导致的问题;

减少镜像层数:如果希望所生成镜像的层数尽量少,则要尽量合并 RUN ADD COPY 指令。通常情况下,多个 RUN 指令可以合并为一条 RUN 指令;

恰当使用多步骤创建(17.05+版本支持):通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应用所需要的最小化环境。当然,用户也可以通过分别构造编译镜像和运行镜像来达到类似的结果,但这种方式需要维护多个Dockerfile

使用.dockerignore​ 文件:使用它可以标记在执行 docker build​ 时忽略的路径和 文件,避免发送不必要的数据内容,从而加快整个镜像创建过程。

及时删除临时文件和缓存文件:特别是在执行 apt-get​指令后,/var/cache/ apt​下面会缓存了一些安装包;

提高生成速度:如合理使用 cache, 减少内容目录下的文件,或使用.dockerignore ​文件指定等;

调整合理的指令顺序:在开启 cache 的情况下,内容不变的指令尽量放在前面,这样 可以尽量复用;

减少外部源的干扰:如果确实要从外部引入数据,需要指定持久的地址,并带版本信 息等,让他人可以复用而不出错。