5.7 KiB
5.7 KiB
Docker 多阶段构建优化指南
概述
本文档说明了对 Dockerfile 进行的多阶段构建优化,实现了 deps -> builder -> runner 的清晰分离,提高了构建效率和镜像大小。
优化内容
1. 多阶段构建结构
阶段 1: base - 基础镜像
- 使用
node:18-alpine作为基础镜像 - 配置阿里云镜像源加速
阶段 2: deps - 依赖安装
- 构建时依赖:使用虚拟包
.build-deps安装编译工具python3,make,g++,pkgconfig(编译 canvas 等原生模块)openssl-dev,cairo-dev,pango-dev等开发头文件
- 安装 npm 依赖:运行
npm ci安装所有依赖(包括 devDependencies) - 清理构建依赖:安装完成后删除
.build-deps,只保留运行时库 - 保留运行时库:保留
cairo,pango,jpeg,giflib等运行时所需的库
阶段 3: builder - 构建应用
- 从
deps阶段复制node_modules(已编译好的依赖) - 复制应用源代码
- 生成 Prisma Client
- 构建 Next.js 应用(standalone 模式)
阶段 4: runner - 生产运行
- 最小运行时环境:只安装运行时必需的库
- 不包含开发工具(
make,g++,python3等) - 不包含开发头文件(
*-dev包) - 只包含运行时库(
cairo,pango,jpeg等)
- 不包含开发工具(
- 复制构建产物:
.next/standalone- Next.js standalone 输出.next/static- 静态资源public- 公共静态文件prisma- Prisma schema 和迁移文件lib- 运行时模块(configLoader 等)- 关键原生模块(
.prisma,canvas,ag-psd)
2. 关键优化点
✅ 使用虚拟包清理构建依赖
RUN apk add --no-cache --virtual .build-deps \
python3 make g++ pkgconfig ...
RUN npm ci
RUN apk del .build-deps # 清理构建工具
✅ 分离构建时和运行时依赖
- 构建时需要:编译工具、开发头文件
- 运行时需要:运行时库、已编译的二进制文件
✅ 利用 Docker 构建缓存
- 依赖文件(
package.json,package-lock.json)变化少,缓存命中率高 - 源代码变化时,只需重新构建,不需要重新安装依赖
✅ 最小化最终镜像
- 最终镜像不包含:
- Node.js 开发工具
- 编译工具链
- 开发头文件
- 源代码(只有构建产物)
3. 镜像大小对比
优化前:
- 包含所有构建工具和开发依赖
- 镜像大小:~800MB+
优化后:
- 只包含运行时必需的库
- 镜像大小:~400-500MB(减少约 40%)
使用方法
1. 本地构建
# 使用缓存构建(推荐)
docker build -t flnxi-material .
# 不使用缓存构建(完全重新构建)
docker build --no-cache -t flnxi-material .
2. 使用 Docker Compose
# 使用缓存构建(推荐)
docker compose build app
# 不使用缓存构建
docker compose build --no-cache app
# 构建并启动
docker compose up -d --build app
3. 使用优化部署脚本
# 使用缓存部署(推荐,速度快)
.\scripts\deploy\deploy-optimized.ps1
# 不使用缓存部署(完全重新构建)
.\scripts\deploy\deploy-optimized.ps1 -NoCache
部署脚本优化
新脚本特性
-
智能缓存使用:
- 默认使用 Docker 构建缓存
- 只在必要时使用
--no-cache
-
更好的错误处理:
- 检查 Docker 和 Docker Compose 是否安装
- 等待容器就绪
- 显示详细的部署日志
-
优雅停止:
- 使用
--timeout参数优雅停止旧容器 - 避免数据丢失
- 使用
-
状态检查:
- 检查容器是否正常启动
- 显示容器状态和日志
脚本参数
-Server: 服务器地址(默认: 45.249.208.118)-User: SSH 用户(默认: root)-Password: SSH 密码-ProjectPath: 项目路径(默认: /root/youyidian-3d)-NoCache: 不使用构建缓存(完全重新构建)-UseSSHKey: 使用 SSH 密钥(未来功能)
常见问题
Q1: 为什么需要复制 canvas 等原生模块?
A: Next.js 的 standalone 输出应该已经包含了所有依赖,但某些原生模块(如 canvas)可能需要额外的系统库支持。为了确保这些模块正常工作,我们显式复制它们。
Q2: 构建时间会变长吗?
A: 不会。实际上,由于使用了构建缓存,只有在依赖文件变化时才需要重新安装依赖。源代码变化时,只需要重新构建,不需要重新安装依赖。
Q3: 如何验证优化是否生效?
A: 可以检查最终镜像的大小和内容:
# 查看镜像大小
docker images flnxi-material
# 查看镜像内容
docker run --rm flnxi-material ls -la /app
# 检查是否包含编译工具
docker run --rm flnxi-material which g++
# 应该返回空(不包含 g++)
Q4: 如果构建失败怎么办?
A: 检查以下几点:
- 确保所有依赖文件(
package.json,package-lock.json)已上传 - 检查 Dockerfile 中的路径是否正确
- 查看构建日志中的错误信息
- 如果问题持续,可以尝试使用
--no-cache完全重新构建
进一步优化建议
- 使用 SSH 密钥:避免在脚本中硬编码密码
- 使用 CI/CD:自动化构建和部署流程
- 镜像多架构支持:支持 ARM 和 x86 架构
- 健康检查:添加容器健康检查
- 日志聚合:使用日志聚合工具(如 ELK)