一、什么是 Dockerfile

Dockerfile 是一个文本文件,用来定义 Docker 镜像的构建步骤。文件里的每一条指令,都会成为镜像构建过程中的一个动作,例如选择基础镜像、安装依赖、复制文件、设置环境变量以及定义容器启动命令。

它的核心价值在于:

  • 把镜像制作过程代码化
  • 让构建过程可复现、可审计
  • 让团队能够用统一方式交付容器镜像

二、Dockerfile 应该怎么写

从工程实践看,编写 Dockerfile 可以按照一条很稳定的思路展开:

  • 先选择合适的基础镜像
  • 再准备运行环境和依赖
  • 然后复制应用文件
  • 最后定义容器启动方式

如果把这个流程进一步拆开,通常可以归纳为:

  1. 找到一个合适的基础镜像
  2. 按需切换系统源或安装依赖
  3. 创建运行用户并设置权限
  4. 拷贝应用和配置文件
  5. 指定启动命令

这条链路非常适合绝大多数业务镜像的起步阶段。

三、最常见的 Dockerfile 指令有哪些

Dockerfile 常用指令主要包括:

  • FROM:指定基础镜像
  • LABEL:写入元数据
  • RUN:执行构建时命令
  • EXPOSE:声明暴露端口
  • CMD:定义默认启动命令
  • ENTRYPOINT:定义主启动命令
  • ENV:设置环境变量
  • ADD:复制文件,支持压缩包自动解压
  • COPY:复制文件或目录
  • WORKDIR:设置工作目录
  • USER:设置启动用户
  • ARG:定义构建参数

其中 MAINTAINER 虽然历史上常见,但现在已经不推荐继续使用,实际生产中更建议使用 LABEL 替代。

四、从几个典型例子理解核心指令

4.1 FROM:所有镜像构建的起点

任何 Dockerfile 几乎都从 FROM 开始,因为你需要先确定镜像建立在哪个基础环境之上。

FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/centos:7

基础镜像选择会直接影响:

  • 镜像体积
  • 软件兼容性
  • 安全补丁来源
  • 后续构建复杂度

4.2 RUN:在构建阶段执行命令

例如创建一个运行用户:

FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/centos:7
RUN useradd dot

配合 docker build -t centos:user . 构建后,就可以在容器中验证用户是否存在。

4.3 ENV:设置环境变量

FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/centos:7
RUN useradd dot
ENV envir=test version=1.0
CMD echo "envir:$envir version:$version"

这类写法适合给容器注入默认环境变量,或为后续启动命令提供参数。

4.4 LABEL:写入镜像元数据

FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/centos:7
LABEL maintainer="dot" version="demo"
LABEL multiple="true"

相比已废弃的 MAINTAINERLABEL 更灵活,也更适合现代镜像治理。

五、ADDCOPY 到底怎么选

这是 Dockerfile 初学者最容易混淆的一组指令。

5.1 ADD 更适合什么场景

当你需要把压缩包复制到镜像里,并自动解压时,ADD 会更方便:

FROM nginx
ADD ./index.tar.gz /usr/share/nginx/html/
WORKDIR /usr/share/nginx/html

5.2 COPY 更适合什么场景

COPY 更纯粹,通常更适合普通文件和目录复制:

FROM nginx
WORKDIR /usr/share/nginx/html
COPY webroot/ .

这里有个很重要的细节:COPY webroot/ . 只会把 webroot 目录下的内容复制进去,不会把 webroot 这个目录本身带过去。

如果你想保留目录名,需要这样写:

COPY webroot/ /tmp/webroot

六、如何控制文件权限和运行用户

6.1 复制文件时直接设权限

Dockerfile 支持在复制时直接设置所有者和权限:

COPY --chown=user:group somefile.txt /path/to/destination/
COPY --chmod=644 someotherfile.txt /path/to/destination/
COPY --chown=user:group --chmod=755 executable.sh /path/to/destination/

这比先复制、再 RUN chmod 的方式更整洁,也能减少不必要的镜像层。

6.2 用 USER 切换启动用户

FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/centos:7
RUN useradd -m tomcat -u 1001
USER 1001

这类写法有助于避免容器长期以 root 身份运行,更符合生产环境安全要求。

七、为什么要看 docker history

构建完镜像后,docker history 是一个非常值得常用的命令:

docker history centos:user

它能帮助你看清:

  • 每一层是怎么产生的
  • 哪一步引入了大量体积
  • 哪些指令没有必要单独形成镜像层

如果你想做好镜像优化,docker history 几乎是必备工具。

八、写 Dockerfile 时最值得记住的几个原则

把 Day004 这一部分内容浓缩后,可以得到下面几个实用原则:

  • 基础镜像先选对,再谈后面的优化
  • 能用 LABEL 就尽量不要再用 MAINTAINER
  • 普通复制优先考虑 COPY
  • 需要自动解压时再考虑 ADD
  • 尽量在复制阶段直接处理权限和属主
  • 非必要不要长期使用 root 用户运行容器

先把这些基础打牢,后面再学习 CMDENTRYPOINTARG、多架构构建和镜像优化时,就能更快建立完整的镜像制作思维。