190 lines
5.7 KiB
Markdown
190 lines
5.7 KiB
Markdown
# 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. 关键优化点
|
||
|
||
#### ✅ 使用虚拟包清理构建依赖
|
||
```dockerfile
|
||
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. 本地构建
|
||
|
||
```bash
|
||
# 使用缓存构建(推荐)
|
||
docker build -t flnxi-material .
|
||
|
||
# 不使用缓存构建(完全重新构建)
|
||
docker build --no-cache -t flnxi-material .
|
||
```
|
||
|
||
### 2. 使用 Docker Compose
|
||
|
||
```bash
|
||
# 使用缓存构建(推荐)
|
||
docker compose build app
|
||
|
||
# 不使用缓存构建
|
||
docker compose build --no-cache app
|
||
|
||
# 构建并启动
|
||
docker compose up -d --build app
|
||
```
|
||
|
||
### 3. 使用优化部署脚本
|
||
|
||
```powershell
|
||
# 使用缓存部署(推荐,速度快)
|
||
.\scripts\deploy\deploy-optimized.ps1
|
||
|
||
# 不使用缓存部署(完全重新构建)
|
||
.\scripts\deploy\deploy-optimized.ps1 -NoCache
|
||
```
|
||
|
||
## 部署脚本优化
|
||
|
||
### 新脚本特性
|
||
|
||
1. **智能缓存使用**:
|
||
- 默认使用 Docker 构建缓存
|
||
- 只在必要时使用 `--no-cache`
|
||
|
||
2. **更好的错误处理**:
|
||
- 检查 Docker 和 Docker Compose 是否安装
|
||
- 等待容器就绪
|
||
- 显示详细的部署日志
|
||
|
||
3. **优雅停止**:
|
||
- 使用 `--timeout` 参数优雅停止旧容器
|
||
- 避免数据丢失
|
||
|
||
4. **状态检查**:
|
||
- 检查容器是否正常启动
|
||
- 显示容器状态和日志
|
||
|
||
### 脚本参数
|
||
|
||
- `-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: 可以检查最终镜像的大小和内容:
|
||
```bash
|
||
# 查看镜像大小
|
||
docker images flnxi-material
|
||
|
||
# 查看镜像内容
|
||
docker run --rm flnxi-material ls -la /app
|
||
|
||
# 检查是否包含编译工具
|
||
docker run --rm flnxi-material which g++
|
||
# 应该返回空(不包含 g++)
|
||
```
|
||
|
||
### Q4: 如果构建失败怎么办?
|
||
|
||
A: 检查以下几点:
|
||
1. 确保所有依赖文件(`package.json`, `package-lock.json`)已上传
|
||
2. 检查 Dockerfile 中的路径是否正确
|
||
3. 查看构建日志中的错误信息
|
||
4. 如果问题持续,可以尝试使用 `--no-cache` 完全重新构建
|
||
|
||
## 进一步优化建议
|
||
|
||
1. **使用 SSH 密钥**:避免在脚本中硬编码密码
|
||
2. **使用 CI/CD**:自动化构建和部署流程
|
||
3. **镜像多架构支持**:支持 ARM 和 x86 架构
|
||
4. **健康检查**:添加容器健康检查
|
||
5. **日志聚合**:使用日志聚合工具(如 ELK)
|
||
|
||
## 参考
|
||
|
||
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
|
||
- [Next.js Docker 部署指南](https://nextjs.org/docs/deployment#docker-image)
|
||
- [Alpine Linux 包管理](https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management)
|
||
|
||
|