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

第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​。

bash 复制代码
ARG 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

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

bash 复制代码
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​环境变量可以看到

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​指令(每个镜像一次)。

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

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 的情况下,内容不变的指令尽量放在前面,这样 可以尽量复用;

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

相关推荐
攸攸太上30 分钟前
Docker学习
java·网络·学习·docker·容器
Sylvan Ding37 分钟前
Docker+PyCharm远程调试&环境隔离解决方案
docker·容器·pycharm
_道隐_3 小时前
如何在Windows上安装Docker
windows·docker
孙强_05254 小时前
使用docker创建zabbix服务器
服务器·docker·zabbix
shelby_loo7 小时前
通过 Docker 部署 MySQL 服务器
服务器·mysql·docker
prcyang10 小时前
Docker Compose
运维·docker·容器
蜗牛^^O^11 小时前
Docker和K8S
java·docker·kubernetes
脚踏实地的大梦想家11 小时前
【Docker】安装全流程与配置完整镜像源(可安装 nginx)
运维·docker·容器
Zww089111 小时前
docker部署个人网页导航
运维·docker·容器
PeterJXL12 小时前
Docker-compose:管理多个容器
运维·docker·容器