Dockerfile是一个文本文件,包含了一条条指令,每条指令对应构建一层镜像,Docker基于它来构建一个完整镜像。本文介绍Dockerfile的常用指令及相应的最佳实践建议。

1. 理解构建上下文(build context)

Docker镜像通过docker build指令构建,该指令执行时当前的工作目录就是docker构建的上下文,即build context,上下文中的文件及目录都会作为构建上下文内容发送给Docker Daemon。

docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context

 

如上 –no-cache 表示镜像构建时不使用缓存,-f 指定Dockerfile文件位置, context 指定build context目录。

 

将一些非必要的文件包含到build context中,会导致build context过大,从而导致镜像过大,会增加镜像构建、推送及拉取的时间,以及容器运行时的大小。

 

执行docker build时会显示build context的大小,

Sending build context to Docker daemon  187.8MB

 

最佳实践建议

  1. 使用.dockerignore来排除不需要加入到build context中的文件,类似于.gitignore

  2. 不要安装不必要的包,所有包含的东西都是镜像必须的,非必须的不要包含。

  3. 解耦应用,如果应用有分层,解耦应用到多个容器,便于横向扩展,如web应用程序栈包含web服务应用,数据库,缓存等。

  4. 最少化镜像层数:只有RUN、COPY、ADD指令会创建镜像层,其它指令创建临时的中间镜像,不会增大镜像构建的大小

  5. 如果可能,尽可能使用多阶段构建,只复制你需要的组件到最终镜像,这使得你可以在中间构建阶段包含工具与debug信息,同时又不会增大最终镜像的大小。

  6. 排序多行参数:将参数按字母排序,有利于避免包重复,及后续的维护与提高易读性

 

2. FROM

作用
FROM指定基础镜像,每一个定制镜像,必须以一个现有镜像为基础。因此一个Dockerfile中FROM是必须的指令,并且必须是第一条。使用格式,

FROM <image>:<tag>
# 注释以#开头。基础镜像的tag可不指定,默认使用latest
# 示例:FROM mysql:5.7

 

最佳实践建议

  1. 如果不想以任何镜像为基础,则可以使用FROM scratch

  2. 尽量使用官方镜像作为基础镜像

  3. 推荐使用Alpine镜像,因为它足够轻量级(小于5MB),但麻雀虽小五脏俱全,基本具有Linux的基础功能

     

3. RUN

作用
用来执行命令行命令,是最常用的指令之一。使用格式,

# shell格式,跟直接在命令行输入命令一行
RUN <命令>
# 示例:RUN mkdir -p /usr/src/redis

# exec格式,类似于函数调用
RUN ["可执行文件", "参数1", "参数2"]

RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建指令中指定–no-cache参数,如:docker build --no-cache

 

最佳实践建议

  1. 将比较长的复杂的指令通过 \ 分为多行,让Dockerfile文件可读性、可理解性、可维护性更高,将多个指令通过 && 连接,减少镜像的层数

  2. 确保每一层只添加必需的东西,任何无关的东西都应该清理掉,如所有下载、展开的文件,apt 缓存文件等,以尽可能减少镜像各层的大小

  3. RUN apt-get update 与 RUN apt-get install 组合成一条RUN指令(将apt-get update单独作为一条指令会因为缓存问题导致后续的apt-get install 指令失败)

 

比如先按如下Dockerfile创建了一个镜像

FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl

一段时间后,再按以下Dockerfile创建另一个镜像