跳转至

Dockerfile 单阶段构建

概述

Docker V17.05 之前,Dockerfile 中只能有一个 FROM 指令,称为单阶段构建。

单阶段构建是指整个镜像构建过程在一个 FROM 指令中完成,所有操作(安装依赖、编译代码、配置环境等)都在同一个镜像层中进行。

实战演示

使用 alpine:3.22.1 做练习

1. 创建 Dockerfile 目录

Bash
mkdir /opt/mydockerfile/redis -p
cd /opt/mydockerfile/redis

2. 准备 Dockerfile 文件之前

可以基于基础镜像,挨个执行一下需求:

Bash
# 临时启动一个容器,将需求执行一遍,再写 Dockerfile
docker run -it --rm alpine:3.22.1

这样做的好处:

  • 验证命令是否正确
  • 了解依赖关系
  • 减少构建失败次数

3. 准备 Dockerfile 文件

Bash
vim Dockerfile
Docker
# 使用 Alpine Linux 3.22.1 作为基础镜像
FROM alpine:3.22.1

# 添加标签元数据
LABEL auth="chaic" version="1.0"

# 配置 Alpine 软件源(使用阿里云镜像)
RUN echo https://mirrors.aliyun.com/alpine/v3.22/main > /etc/apk/repositories && \
    echo https://mirrors.aliyun.com/alpine/v3.22/community >> /etc/apk/repositories && \
    apk update && apk add vim

# 设置环境变量
ENV base_dir="/data"

# 创建工作目录并下载图片
WORKDIR ${base_dir}/image
ADD https://i-blog.csdnimg.cn/direct/20ff41c745534abcb8eaa241295099dc.jpeg .

# 创建工作目录并添加 Redis 源码包
WORKDIR ${base_dir}/app
ADD redis-4.0.14.tar.gz .
COPY redis-4.0.14.tar.gz .

# 创建工作目录并复制配置文件
WORKDIR ${base_dir}/file
COPY 6379.conf .

# 切换到数据目录
WORKDIR /data

# 声明数据卷
VOLUME /data

# 设置入口点(注释掉的 CMD 作为默认参数)
#CMD ls ${base_dir}
ENTRYPOINT ["ls"]
CMD ["/data"]

# 声明暴露端口
EXPOSE 8088

Dockerfile 逐行解析

FROM alpine:3.22.1

指定基础镜像为 Alpine Linux 3.22.1 版本。

Alpine Linux 是一个轻量级的 Linux 发行版,适合构建小型镜像。

LABEL auth="chaic" version="1.0"

添加镜像的元数据标签:

  • auth:作者信息
  • version:版本号

RUN 配置软件源

Docker
1
2
3
RUN echo https://mirrors.aliyun.com/alpine/v3.22/main > /etc/apk/repositories && \
    echo https://mirrors.aliyun.com/alpine/v3.22/community >> /etc/apk/repositories && \
    apk update && apk add vim
  • 替换 Alpine 的软件源为阿里云镜像
  • 更新软件包索引
  • 安装 vim 编辑器

使用 \ 反斜杠将多个命令合并为一行,减少镜像层数。

ENV base_dir="/data"

设置环境变量 base_dir/data

环境变量可以在后续指令中引用,如 ${base_dir}

WORKDIR 指令

Docker
WORKDIR ${base_dir}/image

创建工作目录 /data/image,并切换到该目录。

后续的 ADDCOPY 等指令将在该目录下执行。

ADD 指令

Docker
1
2
3
ADD https://i-blog.csdnimg.cn/direct/20ff41c745534abcb8eaa241295099dc.jpeg .
ADD redis-4.0.14.tar.gz .
COPY redis-4.0.14.tar.gz .
  • 从远程 URL 下载图片文件
  • 添加 Redis 源码压缩包(ADD 会自动解压)
  • 复制 Redis 源码压缩包(COPY 不解压)

COPY 6379.conf .

复制本地配置文件到容器内。

VOLUME /data

声明数据卷 /data

数据卷用于持久化数据,容器删除后数据不会丢失。

ENTRYPOINT 和 CMD

Docker
ENTRYPOINT ["ls"]
CMD ["/data"]
  • ENTRYPOINT 设置入口点命令为 ls
  • CMD 提供默认参数 /data
  • 容器启动时实际执行:ls /data

使用 ENTRYPOINT + CMD 的组合方式,使得容器启动时可以覆盖参数但保持命令不变。

例如:

Bash
docker run myalpine  # 执行 ls /data
docker run myalpine /tmp  # 执行 ls /tmp

EXPOSE 8088

声明容器监听 8088 端口。

这只是文档说明,实际端口映射需要在 docker run 时使用 -p 参数。

4. 打包镜像

Bash
docker build -t myalpine .

5. 测试

Bash
docker run -it --rm myalpine

预期输出:列出 /data 目录的内容。

单阶段构建的特点

优点

  1. 简单直观
  • 所有操作在一个文件中完成
  • 易于理解和维护
  1. 构建速度快
  • 不需要多阶段的复制和清理操作
  • 适合小型项目

缺点

  1. 镜像体积大
  • 构建工具(编译器、依赖包)会保留在最终镜像中
  • 例如:如果需要编译 Go 应用,gcc 会留在镜像中
  1. 安全性较低
  • 构建过程中的临时文件可能包含敏感信息
  • 源代码可能被包含在最终镜像中
  1. 不够灵活
  • 无法单独构建某个阶段
  • 无法复用中间产物

适用场景

单阶段构建适合以下情况:

  1. 简单应用
  • 直接打包运行时环境
  • 不需要编译过程
  1. 原型开发
  • 快速迭代测试
  • 对镜像体积不敏感
  1. 小型项目
  • 依赖简单
  • 构建工具体积小

优化建议

即使使用单阶段构建,也可以采取一些优化措施:

1. 合并 RUN 指令

Docker
1
2
3
4
5
6
7
8
# 不推荐(多个层)
RUN apk update
RUN apk add vim
RUN apk add curl

# 推荐(一个层)
RUN apk update && \
    apk add vim curl

2. 使用 .dockerignore

Text Only
1
2
3
*.md
.git
node_modules

避免不必要的文件被复制到构建上下文中。

3. 清理缓存

Docker
1
2
3
RUN apk update && \
    apk add vim && \
    rm -rf /var/cache/apk/*

4. 选用更小的基础镜像

Docker
1
2
3
4
5
# 推荐
FROM alpine:3.22.1

# 或者
FROM debian:bookworm-slim

与多阶段构建的对比

特性 单阶段构建 多阶段构建
复杂度 简单 复杂
镜像体积 较大 较小
安全性 较低 较高
构建时间 较短 较长
适用场景 简单应用 复杂应用

总结

单阶段构建是 Docker 镜像构建的基础方式,适合简单的应用场景。随着项目复杂度增加和对镜像体积、安全性要求的提高,建议采用多阶段构建来优化镜像。

本示例通过一个完整的 Dockerfile 演示了单阶段构建的各个环节,包括: - 基础镜像选择 - 环境配置 - 文件复制和下载 - 入口点和命令设置 - 端口和数据卷声明

理解单阶段构建有助于掌握 Docker 镜像构建的基本原理。