Docker自动化构建实战:从手工到多阶段构建的完美进化

Docker自动化构建实战:从手工到多阶段构建的完美进化

前言:为什么要自动化构建?

在容器化时代,手动构建镜像存在三个核心问题:

  1. 不可重复:今天能成功,明天可能失败
  2. 无法追溯:三个月后记不清当时怎么配的
  3. 镜像臃肿:带着编译工具链,动辄几百MB

本文将带你从零开始,体验手工构建 → Dockerfile → 多阶段构建的完整进化之路,最终掌握企业级镜像构建的最佳实践。


一、手工构建Tengine镜像:理解镜像本质

在自动化之前,我们先手动走一遍流程,深刻理解镜像的组成。

1.1 运行基础容器

bash 复制代码
# 启动Ubuntu容器
[root@hadoop108 ~]# docker run -itd --name ubt_tengine_2.3.3 ubuntu:20.04 /bin/bash

# 进入容器
[root@hadoop108 ~]# docker exec -it ubt_tengine_2.3.3 /bin/bash

1.2 配置基础环境

bash 复制代码
# 替换apt源为阿里云(国内加速)
root@16552f5daf21:/# sed -ri 's#archive.ubuntu.com|security.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list

# 更新缓存并安装基础工具
root@16552f5daf21:/# apt update && apt install -y wget vim curl

1.3 编译安装Tengine

bash 复制代码
# 创建规范目录
root@16552f5daf21:/# mkdir -p /opt/module /opt/software

# 下载源码
root@16552f5daf21:~# wget -P /opt/software/ http://tengine.taobao.org/download/tengine-2.3.3.tar.gz

# 安装编译依赖
root@16552f5daf21:~# apt install -y libssl-dev make gcc pcre2-utils libpcre3-dev zlib1g-dev

# 解压并编译
root@16552f5daf21:/opt/software# tar -xzvf tengine-2.3.3.tar.gz
cd tengine-2.3.3/

# 配置(开启所需模块)
./configure --prefix=/opt/module/tengine-2.3.3/ \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_mp4_module \
--with-stream \
--with-stream_ssl_module \
--with-stream_realip_module \
--add-module=modules/ngx_http_upstream_check_module/ \
--add-module=modules/ngx_http_upstream_session_sticky_module

# 编译安装
make -j `nproc` && make install

1.4 运行时配置优化

bash 复制代码
# 创建nginx用户
root@16552f5daf21:~# groupadd nginx && useradd -g nginx nginx

# 创建软链接(便于使用)
root@16552f5daf21:~# ln -s /opt/module/tengine-2.3.3 /opt/module/tengine
root@16552f5daf21:~# ln -s /opt/module/tengine/sbin/nginx /sbin/nginx

# 日志重定向(Docker最佳实践)
root@16552f5daf21:~# ln -s /dev/stdout /opt/module/tengine/logs/access.log
root@16552f5daf21:~# ln -s /dev/stderr /opt/module/tengine/logs/error.log

# 测试页面
root@16552f5daf21:~# echo 'docker tengine' > /opt/module/tengine/html/index.html

# 启动测试
root@16552f5daf21:~# nginx && curl localhost
docker tengine

1.5 生成镜像

bash 复制代码
# 清理无用文件
root@16552f5daf21:~# rm -rf /opt/software/* /var/cache/*

# 提交为镜像
[root@hadoop108 ~]# docker commit ubt_tengine_2.3.3 tengine:2.3.3

# 查看镜像大小
[root@hadoop108 ~]# docker images
REPOSITORY       TAG           IMAGE ID       CREATED         SIZE
tengine          2.3.3         25f29f6c517a   9 seconds ago   389MB

💡 手工构建的痛点

  • 操作繁琐,容易遗漏
  • 镜像体积大(389MB)
  • 无法版本控制
  • 不可重复构建

二、Dockerfile自动化构建:迈出第一步

2.1 项目结构

bash 复制代码
[root@hadoop108 ~]# mkdir -p /opt/module/tengine
[root@hadoop108 ~]# cd /opt/module/tengine

# 准备文件
[root@hadoop108 tengine]# ll
总用量 2792
-rw-r--r-- 1 root root    1447 Dockerfile
-rw-r--r-- 1 root root      32 index.html
-r-------- 1 root root 2848144 tengine-2.3.3.tar.gz

2.2 编写Dockerfile

dockerfile 复制代码
# /opt/module/tengine/Dockerfile
FROM ubuntu:20.04

LABEL author="礼拜天没时间" \
      url="https://blog.csdn.net/weixin_73059914"

# =====================
# 环境变量
# =====================
ENV TENGINE_VERSION=2.3.3
ENV TENGINE_NAME=tengine-${TENGINE_VERSION}
ENV NGINX_USER=nginx
ENV INSTALL_PREFIX=/opt/module

# Tengine编译配置选项
ENV TENGINE_CONFIGURE_OPTS="./configure \
    --prefix=${INSTALL_PREFIX}/${TENGINE_NAME} \
    --user=${NGINX_USER} \
    --group=${NGINX_USER} \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_stub_status_module \
    --with-http_mp4_module \
    --with-stream \
    --with-stream_ssl_module \
    --with-stream_realip_module \
    --add-module=modules/ngx_http_upstream_check_module/ \
    --add-module=modules/ngx_http_upstream_session_sticky_module"

# =====================
# 1. 替换apt源为阿里云
# =====================
RUN sed -ri 's#archive.ubuntu.com|security.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list \
    && apt update

# =====================
# 2. 添加源码包
# =====================
ADD ${TENGINE_NAME}.tar.gz /tmp/

# =====================
# 3. 编译安装
# =====================
RUN apt install -y libssl-dev make gcc pcre2-utils libpcre3-dev zlib1g-dev \
    && cd /tmp/${TENGINE_NAME} \
    && ${TENGINE_CONFIGURE_OPTS} \
    && make -j $(nproc) \
    && make install

# =====================
# 4. 运行时配置
# =====================
RUN groupadd ${NGINX_USER} \
    && useradd -g ${NGINX_USER} ${NGINX_USER} \
    && ln -s ${INSTALL_PREFIX}/${TENGINE_NAME} ${INSTALL_PREFIX}/tengine \
    && ln -s ${INSTALL_PREFIX}/tengine/sbin/nginx /sbin/nginx \
    && ln -sf /dev/stdout ${INSTALL_PREFIX}/tengine/logs/access.log \
    && ln -sf /dev/stderr ${INSTALL_PREFIX}/tengine/logs/error.log \
    && rm -rf /tmp/* /var/cache/*

# =====================
# 5. 添加网页文件
# =====================
ADD index.html ${INSTALL_PREFIX}/tengine/html/

# =====================
# 6. 暴露端口
# =====================
EXPOSE 80 443

# =====================
# 7. 启动命令
# =====================
CMD ["nginx", "-g", "daemon off;"]

2.3 构建并运行

bash 复制代码
# 构建镜像
[root@hadoop108 tengine]# docker build -t "tengine-dockerfile:2.3.3" .

# 查看镜像
[root@hadoop108 tengine]# docker images
REPOSITORY           TAG           IMAGE ID       CREATED         SIZE
tengine-dockerfile   2.3.3         276c35d21239   11 seconds ago  360MB

# 运行容器
[root@hadoop108 tengine]# docker run --name tengine -p 80:80 -d tengine-dockerfile:2.3.3

# 测试访问
curl http://localhost

✅ Dockerfile带来的改进

  • 可重复构建
  • 版本控制友好
  • 操作文档化
  • 镜像体积略有减小(360MB)

❌ 依然存在的问题

  • 镜像仍包含gcc、make等编译工具
  • 安全风险(编译工具可能被利用)
  • 体积还是偏大

三、多阶段构建:终极进化

3.1 什么是多阶段构建?

核心思想:将"编译环境"和"运行环境"彻底分离,只把"运行必需的结果"放进最终镜像。
阶段2:Final
阶段1:Builder
COPY --from
源码
编译
编译产物
基础镜像
拷贝编译产物
最终镜像

3.2 多阶段Dockerfile

dockerfile 复制代码
# /opt/module/tengine-multi/Dockerfile
# ==============================
# 阶段1:编译环境
# ==============================
FROM ubuntu:20.04 AS builder

LABEL author="礼拜天没时间" \
      url="https://blog.csdn.net/weixin_73059914"

# 环境变量
ENV TENGINE_VERSION=2.3.3
ENV TENGINE_NAME=tengine-${TENGINE_VERSION}
ENV NGINX_USER=nginx
ENV INSTALL_PREFIX=/opt/module

# Tengine编译选项
ENV TENGINE_CONFIGURE_OPTS="./configure \
    --prefix=${INSTALL_PREFIX}/${TENGINE_NAME} \
    --user=${NGINX_USER} \
    --group=${NGINX_USER} \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_stub_status_module \
    --with-http_mp4_module \
    --with-stream \
    --with-stream_ssl_module \
    --with-stream_realip_module \
    --add-module=modules/ngx_http_upstream_check_module/ \
    --add-module=modules/ngx_http_upstream_session_sticky_module"

# 替换apt源
RUN sed -ri 's#archive.ubuntu.com|security.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list \
    && apt update

# 添加源码
ADD ${TENGINE_NAME}.tar.gz /tmp/

# 编译安装
RUN apt install -y libssl-dev make gcc pcre2-utils libpcre3-dev zlib1g-dev \
    && cd /tmp/${TENGINE_NAME} \
    && ${TENGINE_CONFIGURE_OPTS} \
    && make -j $(nproc) \
    && make install

# ==============================
# 阶段2:运行环境
# ==============================
FROM ubuntu:20.04

LABEL author="礼拜天没时间" \
      url="https://blog.csdn.net/weixin_73059914"

# 环境变量
ENV TENGINE_VERSION=2.3.3
ENV TENGINE_NAME=tengine-${TENGINE_VERSION}
ENV NGINX_USER=nginx
ENV INSTALL_PREFIX=/opt/module

# 1. 只拷贝编译产物
COPY --from=builder ${INSTALL_PREFIX}/ ${INSTALL_PREFIX}/

# 2. 安装运行依赖(只需要运行时库)
RUN sed -ri 's#archive.ubuntu.com|security.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list \
    && apt update \
    && apt install -y libssl-dev pcre2-utils libpcre3-dev zlib1g-dev

# 3. 运行时配置
RUN groupadd ${NGINX_USER} \
    && useradd -g ${NGINX_USER} ${NGINX_USER} \
    && ln -s ${INSTALL_PREFIX}/${TENGINE_NAME} ${INSTALL_PREFIX}/tengine \
    && ln -s ${INSTALL_PREFIX}/tengine/sbin/nginx /sbin/nginx \
    && ln -sf /dev/stdout ${INSTALL_PREFIX}/tengine/logs/access.log \
    && ln -sf /dev/stderr ${INSTALL_PREFIX}/tengine/logs/error.log \
    && rm -rf /tmp/* /var/cache/*

# 4. 添加网页文件
ADD index.html ${INSTALL_PREFIX}/tengine/html/

# 5. 暴露端口
EXPOSE 80 443

# 6. 启动命令
CMD ["nginx", "-g", "daemon off;"]

3.3 构建对比

bash 复制代码
# 构建多阶段镜像
[root@hadoop108 tengine-multi]# docker build -t "tengine-multi:2.3.3" .

# 查看镜像大小
[root@hadoop108 tengine-multi]# docker images
REPOSITORY                 TAG           IMAGE ID       CREATED         SIZE
tengine-multi              2.3.3         e5ecfb93e5b9   58 seconds ago  196MB
tengine-dockerfile         2.3.3         276c35d21239   10 minutes ago  360MB
tengine                    2.3.3         25f29f6c517a   1 hour ago      389MB

3.4 三种方式对比

构建方式 镜像大小 构建速度 安全性 可维护性 适用场景
手工构建 389MB 学习理解
Dockerfile 360MB 简单项目
多阶段构建 196MB 极好 生产环境

多阶段构建的核心优势

  • 体积减少50%:196MB vs 389MB
  • 安全性提升:没有gcc/make等编译工具
  • 启动更快:更小的镜像拉取快、启动快
  • 依赖隔离:运行时只需要必要的库

四、镜像层次架构:企业级最佳实践

4.1 三层架构理论

在大型企业中,镜像构建遵循严格的层次结构:
系统层(System)
运行时层(Runtime)
应用层(Application)
shop项目
blog项目
hospital项目
aggbook项目
JDK8
JDK11
JDK17
Python3.8
Node.js 16
CentOS 7.9
Ubuntu 20.04
CentOS 7.9-ssh

4.2 目录结构实战

bash 复制代码
[root@hadoop108 module]# tree df/ -L 3
df/
├── project                 # 应用层:具体业务项目
│   ├── aggbook
│   │   ├── build.sh
│   │   └── Dockerfile
│   ├── blog
│   │   ├── build.sh
│   │   └── Dockerfile
│   └── hospital
│       ├── build.sh
│       └── Dockerfile
├── runtime                 # 运行时层:各种语言环境
│   ├── centos-7.9-jdk8
│   │   ├── build.sh
│   │   └── Dockerfile
│   ├── centos-7.9-jdk11
│   │   ├── build.sh
│   │   └── Dockerfile
│   ├── centos-7.9-python-3.8
│   │   ├── build.sh
│   │   └── Dockerfile
│   └── centos-7.9-nodejs-16
│       ├── build.sh
│       └── Dockerfile
└── system                  # 系统层:基础操作系统
    ├── centos-7.9
    │   ├── build.sh
    │   └── Dockerfile
    ├── centos-7.9-ssh
    │   ├── build.sh
    │   └── Dockerfile
    └── ubuntu-20.04
        ├── build.sh
        └── Dockerfile

4.3 层次化Dockerfile示例

系统层:CentOS 7.9基础镜像

dockerfile 复制代码
# df/system/centos-7.9/Dockerfile
FROM centos:7.9.2009

RUN yum install -y epel-release \
    && yum clean all

CMD ["/bin/bash"]

运行时层:JDK8镜像

dockerfile 复制代码
# df/runtime/centos-7.9-jdk8/Dockerfile
FROM centos-7.9:latest

ENV JAVA_HOME=/usr/local/jdk1.8.0_202
ENV PATH=$PATH:$JAVA_HOME/bin

ADD jdk-8u202-linux-x64.tar.gz /usr/local/

CMD ["/bin/bash"]

应用层:Spring Boot项目

dockerfile 复制代码
# df/project/shop/Dockerfile
FROM centos-7.9-jdk8:latest

WORKDIR /app
ADD shop.jar /app/
ADD start.sh /app/

EXPOSE 8080
CMD ["/app/start.sh"]

4.4 层次架构的收益

层级 复用性 变更频率 构建次数 典型大小
系统层 全公司共用 极低(季度/年) 1次 200MB
运行时层 部门共用 低(月/季度) 10+次 300-500MB
应用层 项目独用 高(每天) 100+次 50-200MB

核心收益

  1. 存储节省:10个Java项目只需要1份系统层+1份JDK层
  2. 构建加速:改代码只需重建应用层,利用缓存秒级完成
  3. 标准化:统一的基础环境,减少"在我机器能跑"的问题
  4. 安全可控:基础镜像由平台组统一维护,打满安全补丁

五、生产环境最佳实践总结

5.1 镜像构建演进路线

手工构建
Dockerfile
多阶段构建
层次化架构
CI/CD集成

5.2 企业级构建清单

  • 基础镜像:选择官方镜像或自建基础镜像
  • .dockerignore:排除无用文件
  • 多阶段构建:分离编译和运行环境
  • 非root用户:运行容器不使用root
  • 标签规范:版本号+commit+构建时间
  • 镜像扫描:集成trivy/clair扫描漏洞
  • 签名验证:保证镜像完整性

5.3 最终对比数据

指标 手工构建 Dockerfile 多阶段构建 层次化架构
镜像大小 389MB 360MB 196MB 150MB
构建时间 15min 8min 8min 3min
安全风险 极低
可维护性 极好
存储复用 部分 完全

结语

从手工构建到多阶段构建,我们见证了Docker镜像构建的完整进化史:

  • 手工构建:理解镜像本质的必经之路
  • Dockerfile:自动化的第一步
  • 多阶段构建:生产环境的标准答案
  • 层次化架构:企业级大规模实践的终极方案

记住这个原则:编译环境要胖,运行环境要瘦。多阶段构建正是这一思想的最佳实践。

现在,是时候重构你的Dockerfile了!


思考题:如果你的项目是用apt/yum安装的软件(如MySQL、Redis),多阶段构建还适用吗?为什么?

欢迎在评论区分享你的见解!

相关推荐
Cxiaomu2 小时前
【无标题】
自动化
Trouvaille ~3 小时前
【Linux】数据链路层与以太网详解:从 MAC 地址到 ARP 的完整指南
linux·运维·服务器·网络·以太网·数据链路层·arp
xiaoliuliu123453 小时前
Xftp-7.0.0109p文件传输安装步骤详解(附FTP/SFTP连接与文件传输教程)
运维·服务器
小鸡食米3 小时前
LVS(Linux Virtual Server)
运维·服务器·网络
罗技1234 小时前
Docker启动Coco AI Server后,如何访问内置Easysearch?
人工智能·docker·容器
DeeplyMind5 小时前
第14章 挂载宿主机目录(Bind Mount)(最常用,重要)
运维·docker·云原生·容器·eureka
DeeplyMind5 小时前
第17章 Docker网络实战与高级管理
网络·docker·容器
FJW0208145 小时前
《Nginx 进阶实战:配置详解、站点发布与常用功能大全》(2)
运维·nginx
DeeplyMind6 小时前
第19章 Docker Compose进阶
运维·docker·容器