第8章 使用Dockerfile创建镜像
Dockerfile
是一个文本格式的配置文件,用户可以使用 Dockerfile
来快速创建自定义的镜像。
基本结构
Dockerfile
由一行行命令语句组成,并且支持以#开头
的注释行。
一般而言, Dockerfile
主体内容分为四部分:基础镜像信息、维护者信息、镜像操作指 令和容器启动时执行指令。
简单示例:
less
# escape=\ (backslash)
# 转义字符 \ (backslash)
# This dockerfile uses the ubuntu:xeniel image
#此 dockerfile 使用 ubuntu:xeniel 映像
# VERSION 2 - EDITION 1
# Author:docker user
# Command format:Instruction [arguments / command]
# 命令格式:指令 [arguments command]
# Base image to use,this must be set as the first line
# 基础映像,必须作为第一行设置
FROM ubuntu:xeniel
# Maintainer: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.conf
# Commands when creating a new container
# 创建容器时运行的命令
CMD /usr/sbin/nginx
首行可以通过注释来指定解析器命令,后续通过注释说明镜像的相关信息。主体部分首先使用
FROM
指令指明所基于的镜像名称
,接下来一般是使用LABEL
指令说明维护者信息
。后面则是镜像操作指令,例如RUN
指令将对镜像执行跟随的命令。每运行一条RUN指令
,镜像添加新的一层
,并提交。最后是CMD指令,来指定运行容器时的操作命令。
例子
debian:jessie
基础镜像基础上安装 Nginx
环境
bash
#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
相关环境
bash
# 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
。
bashARG DOCKER_USERNAME=library FROM ${DOCKER_USERNAME}/alpine RUN set -x ; echo ${DOCKER_USERNAME}
使用上述 Dockerfile 会发现无法输出
${DOCKER_USERNAME}
变量的值,要想正常输出,你必须在FROM
之后再次指定ARG
,如下示例
bash# 只在 FROM 中生效 ARG DOCKER_USERNAME=library FROM ${DOCKER_USERNAME}/alpine # 要想在 FROM 之后使用,必须再次指定 ARG DOCKER_USERNAME=library RUN set -x ; echo ${DOCKER_USERNAME}
如下示例,变量将会在每个
FROM
指令中生效
bash# 这个变量在每个 FROM 中都生效 ARG DOCKER_USERNAME=library FROM ${DOCKER_USERNAME}/alpine RUN set -x ; echo 1 FROM ${DOCKER_USERNAME}/alpine RUN set -x ; echo 2
如下示例,对于在各个阶段中使用的变量都必须在每个阶段分别指定
bashARG 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
环境变量可以看到
ini
# 只在 FROM 中生效
ARG DOCKER_USERNAME=library
FROM ubuntu
# 要想在 FROM 之后使用,必须再次指定
ARG DOCKER_USERNAME=bbb
ENV releaser=$DOCKER_USERNAME
RUN echo ${releaser}
FROM
指定所创建镜像的基础镜像,格式是 FROM IMAGE[:TAG][@DIGEST]
ruby
# 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
指令(每个镜像一次)。
为了保证镜像精简,可以选用体积较小的镜像如 Alpine
Debian
作为基础镜像
LABEL
LABEL
指令用来给镜像以键值对的形式添加一些元数据(metadata)。这些信息可以用来辅助过滤出特定镜像。格式为LABEL <key>=<value> <key>=<value> <key>=<value> ...
还可以用一些标签来申明镜像的作者、文档地址等
ini
#声明版本
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
查看镜像的详细信息
具体可以参考 github.com/opencontain...
org.opencontainers.image 前缀 |
org.label-schema 前缀 |
兼容性说明 |
---|---|---|
created |
build-date |
相容 |
url |
url |
相容 |
source |
vcs-url |
相容 |
version |
version |
相容 |
revision |
vcs-ref |
相容 |
vendor |
vendor |
相容 |
title |
name |
相容 |
description |
description |
相容 |
documentation |
usage |
如果文档通过 URL 定位,则值是兼容的 |
authors |
标签架构中没有等效项 | |
licenses |
标签架构中没有等效项 | |
ref.name |
标签架构中没有等效项 | |
schema-version |
OCI 镜像规范中没有等效项 | |
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
目录
bash
WORKDIR /app
RUN mkdir demo && cd demo
RUN echo "hello" > world.txt
上述需求可以进行如下优化,推荐使用第二种写法
bash
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 的情况下,内容不变的指令尽量放在前面,这样 可以尽量复用;
减少外部源的干扰:如果确实要从外部引入数据,需要指定持久的地址,并带版本信 息等,让他人可以复用而不出错。