{T}

Docker镜像构建优化实战

Docker镜像构建优化实战

学习目标:掌握 Node.js 应用(Nuxt3/NestJS)的 Docker 容器化部署、多阶段构建优化、环境变量配置、Docker Compose 实战。


一、容器化部署策略对比

1.1 两种容器化方式分析

code
前端/Node.js 应用容器化策略:
│
├── 策略一:容器作为运行环境 + Volume 映射 
│   ├── 实现方式
│   │   ├── 使用基础镜像(如 Nginx/Node)
│   │   ├── 宿主机代码映射到容器
│   │   └── 容器提供运行环境
│   │
│   ├── 适用场景
│   │   ├── 宿主机环境复杂
│   │   ├── 已有其他服务运行
│   │   └── 需要灵活修改配置
│   │
│   └── 缺点
│       ├── 依赖宿主机文件
│       ├── 环境不稳定
│       └── 迁移时需同步文件
│
└── 策略二:代码打包进镜像 (推荐)
    ├── 实现方式
    │   ├── 多阶段构建
    │   ├── 代码打包到镜像内
    │   └── 自包含的运行环境
    │
    ├── 优点
    │   ├── 环境完全独立
    │   ├── 镜像删除后可重建
    │   ├── 一键部署
    │   └── 版本控制清晰
    │
    └── 适用场景
        ├── 生产环境部署
        ├── CI/CD 流水线
        └── 微服务架构

1.2 Nuxt3 与 NestJS 容器化差异

code
框架容器化差异对比:
│
├── Nuxt3 应用
│   ├── 中间文件:.nuxt 目录
│   ├── 构建产物:.output 目录
│   ├── 依赖管理:bundleDependencies
│   └── 注意事项:需执行 nuxt prepare 生成类型文件
│
└── NestJS 应用
    ├── 中间文件:prisma 生成的 client
    ├── 构建产物:dist 目录
    ├── 依赖管理:需执行 prisma generate
    └── 注意事项:数据库连接配置

二、Dockerfile 配置实战

2.1 .dockerignore 文件配置

作用:排除不必要的文件,减小镜像体积,避免兼容性问题。

plaintext
# .dockerignore
.nuxt
.output
.github
.git
.DS_Store
node_modules
dist

配置说明

文件/目录忽略原因
.nuxt中间缓存文件,容器内重新构建
.output构建产物,容器内生成
node_modules依赖包,容器内安装(避免跨平台兼容问题)
.DS_StoremacOS 系统文件,不影响应用运行

2.2 多阶段构建 Dockerfile

Nuxt3 应用 Dockerfile 完整示例

dockerfile
# ==================== 构建阶段 ====================
FROM node:18-alpine AS builder

# 设置工作目录
WORKDIR /app

# 设置 npm 淘宝源(国内环境加速)
RUN npm config set registry https://registry.npmmirror.com

# 复制依赖配置文件
COPY package*.json ./

# 安装所有依赖(包括开发依赖)
RUN npm install

# 复制项目源码
COPY . .

# 执行构建
RUN npm run build

# ==================== 生产阶段 ====================
FROM node:18-alpine AS production

# 设置工作目录
WORKDIR /app

# 从构建阶段复制构建产物
COPY --from=builder /app/.output ./.output

# 暴露端口(容器内部端口)
EXPOSE 3000

# 设置环境变量
ENV NODE_ENV=production

# 启动应用
CMD ["node", ".output/server/index.mjs"]

多阶段构建优势

code
多阶段构建流程:
│
├── 构建阶段(builder)
│   ├── 基础镜像:node:18-alpine
│   ├── 包含:完整开发环境
│   ├── 执行:npm install + npm run build
│   └── 产出:.output 目录
│
└── 生产阶段(production)
    ├── 基础镜像:node:18-alpine
    ├── 只包含:运行必需文件
    ├── 体积:大幅减小
    └── 启动:直接运行构建产物

2.3 关键技术点解析

1. bundleDependencies 机制

问题:为什么不需要 npm install --production

答案:Nuxt3 构建时会自动打包依赖。

json
// .output/server/package.json(自动生成)
{
  "bundleDependencies": true,
  "dependencies": {
    // 所有运行时依赖已打包到 node_modules
  }
}

验证方式

bash
# 查看 .output/server/node_modules
ls .output/server/node_modules
# 会看到所有依赖已打包

2. 包管理工具选择

code
包管理工具对比(容器构建场景):
│
├── pnpm
│   ├── 优点:本地开发快、磁盘占用少
│   ├── 缺点:使用软链接,容器内可能路径问题
│   └── 容器场景:不推荐(CI/CD 一次性构建)
│
├── npm
│   ├── 优点:兼容性好、无软链接问题
│   ├── 缺点:安装较慢
│   └── 容器场景:推荐(稳定性优先)
│
└── yarn
    ├── 优点:速度快、确定性安装
    ├── 缺点:额外学习成本
    └── 容器场景:推荐

三、镜像构建与运行

3.1 镜像构建命令

bash
# 构建镜像(带版本号)
docker build -t nuxt-app:1.0 .

# 查看构建的镜像
docker images | grep nuxt-app

# 预期输出
# nuxt-app   1.0   abc123def456   2 minutes ago   250MB

3.2 容器运行与调试

基础运行命令

bash
# 运行容器(测试)
docker run \
  --name test \
  -p 8001:3000 \
  nuxt-app:1.0

# 参数说明:
# --name test      容器名称
# -p 8001:3000     端口映射(宿主机:容器)
# nuxt-app:1.0     镜像名称:版本号

查看容器状态

bash
# 查看运行中的容器
docker ps

# 查看容器日志
docker logs -f test

# 预期输出
# Listening on http://[::]:3000

访问应用

bash
# 浏览器访问
http://localhost:8001

3.3 环境变量配置

方式一:命令行传递

bash
# 停止并删除旧容器
docker stop test
docker rm test

# 使用环境变量运行
docker run \
  --name test \
  -p 8002:3000 \
  -e BASE_URL=http://192.168.3.77:3000 \
  nuxt-app:1.0

# 参数说明:
# -e BASE_URL=xxx  设置环境变量

方式二:Docker Compose 配置(推荐)

docker-compose.yml

yaml
version: '3.8'

services:
  nuxt-app:
    image: nuxt-app:1.0
    container_name: nuxt-app-prod
    restart: always
    ports:
      - "8003:3000"
    environment:
      - BASE_URL=http://192.168.3.77:3000
      # 可以添加更多环境变量
      # - API_KEY=xxx
      # - DB_HOST=xxx

运行命令

bash
# 启动服务(后台运行)
docker compose up -d

# 停止服务
docker compose down

# 查看运行状态
docker compose ps

方式三:使用 .env 文件

.env.production

bash
BASE_URL=http://192.168.3.77:3000
API_KEY=your_api_key
DB_HOST=localhost

docker-compose.yml

yaml
version: '3.8'

services:
  nuxt-app:
    image: nuxt-app:1.0
    container_name: nuxt-app-prod
    restart: always
    ports:
      - "8005:3000"
    env_file:
      - .env.production  # 引用环境变量文件

优势

code
使用 .env 文件的优势:
│
├── 1. 统一管理
│   ├── 所有环境变量集中在一个文件
│   └── 避免配置分散
│
├── 2. 安全性
│   ├── 敏感信息不入代码库
│   └── 可添加到 .gitignore
│
├── 3. 可维护性
│   ├── 修改方便
│   └── 多环境配置(.env.dev/.env.prod)
│
└── 4. 复用性
    ├── 同一配置文件可用于多个服务
    └── 避免重复配置

四、常见问题与解决方案

4.1 问题排查流程

code
容器问题排查流程:
│
├── 1. 查看容器状态
│   └── docker ps -a(查看是否运行)
│
├── 2. 查看容器日志
│   └── docker logs -f <container_name>
│
├── 3. 进入容器调试
│   └── docker exec -it <container_name> sh
│
├── 4. 检查端口映射
│   └── netstat -tlnp | grep <port>
│
└── 5. 重新构建镜像
    └── docker build --no-cache -t nuxt-app:1.0 .

4.2 常见问题汇总

问题现象原因分析解决方案
构建失败:找不到 package.json第二阶段未复制依赖文件使用 bundleDependencies,无需安装
页面空白,无数据环境变量未传递添加 -e 参数或配置 env_file
端口冲突宿主机端口被占用更换端口或停止占用服务
镜像体积过大包含不必要文件配置 .dockerignore,使用多阶段构建
依赖安装慢使用国外源配置淘宝源:npm config set registry
.nuxt 文件缺失未执行 prepare在构建阶段添加 npm run prepare

4.3 端口冲突处理

bash
# 查看端口占用
lsof -i :8001

# 停止占用端口的容器
docker stop test
docker rm test

# 重新运行(使用新端口)
docker compose up -d

五、Docker Compose 高级配置

5.1 完整配置示例

yaml
version: '3.8'

services:
  nuxt-app:
    image: nuxt-app:1.0
    container_name: nuxt-app-prod
    restart: always
    ports:
      - "8005:3000"
    env_file:
      - .env.production
    networks:
      - app-network
    depends_on:
      - api-server
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3

  api-server:
    image: nestjs-app:1.0
    container_name: nestjs-api
    restart: always
    ports:
      - "3000:3000"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

5.2 常用命令速查

bash
# 启动所有服务
docker compose up -d

# 停止所有服务
docker compose down

# 重启特定服务
docker compose restart nuxt-app

# 查看服务日志
docker compose logs -f nuxt-app

# 进入容器
docker compose exec nuxt-app sh

# 重新构建并启动
docker compose up -d --build

六、镜像优化技巧

6.1 镜像体积优化

code
镜像优化策略:
│
├── 1. 使用 alpine 基础镜像 
│   ├── node:18        → 900MB+
│   ├── node:18-alpine → 170MB+
│   └── 体积减少:80%+
│
├── 2. 多阶段构建 
│   ├── 构建阶段:包含构建工具
│   ├── 生产阶段:只包含运行文件
│   └── 体积减少:60%+
│
├── 3. 优化层数 
│   ├── 合并 RUN 命令
│   ├── 清理缓存文件
│   └── 示例:RUN npm install && npm cache clean --force
│
└── 4. .dockerignore 
    ├── 排除 node_modules
    ├── 排除构建产物
    └── 排除系统文件

6.2 构建速度优化

dockerfile
# 优化前(每次都重新安装依赖)
COPY . .
RUN npm install

# 优化后(利用 Docker 缓存)
COPY package*.json ./
RUN npm install
COPY . .

原理:Docker 按层构建,package.json 未变时复用缓存层。


七、NestJS 应用容器化补充

7.1 NestJS Dockerfile 示例

dockerfile
# ==================== 构建阶段 ====================
FROM node:18-alpine AS builder

WORKDIR /app

# 安装 Prisma CLI(全局)
RUN npm install -g prisma

# 复制依赖文件
COPY package*.json ./
COPY prisma ./prisma/

# 安装依赖
RUN npm install

# 生成 Prisma Client
RUN prisma generate

# 复制源码并构建
COPY . .
RUN npm run build

# ==================== 生产阶段 ====================
FROM node:18-alpine AS production

WORKDIR /app

# 复制构建产物和依赖
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
COPY --from=builder /app/prisma ./prisma

EXPOSE 3000

ENV NODE_ENV=production

CMD ["node", "dist/main.js"]

7.2 NestJS 注意事项

code
NestJS 容器化关键点:
│
├── 1. Prisma 相关
│   ├── 必须执行 prisma generate
│   ├── 复制 prisma 目录到生产镜像
│   └── 数据库迁移在运行时执行
│
├── 2. 依赖管理
│   ├── 需要安装生产依赖
│   ├── 保留 node_modules
│   └── 不像 Nuxt3 有 bundleDependencies
│
└── 3. 环境变量
    ├── DATABASE_URL(数据库连接)
    ├── JWT_SECRET(密钥)
    └── 其他业务配置

八、AI 辅助开发实践

8.1 使用 AI 工具编写 Dockerfile

提问示例

code
用户:帮我写一个 Dockerfile,用于部署 Nuxt3 应用

AI:(生成基础 Dockerfile)

用户:镜像体积太大,需要优化

AI:(提供多阶段构建方案)

用户:某个命令是什么意思?

AI:(详细解释命令作用)

8.2 AI 工具的价值

code
AI 辅助开发流程:
│
├── 传统方式
│   └── 搜索 → 筛选 → 尝试 → 调试
│
└── AI 辅助
    ├── 提问 → 获取方案 → 调整优化
    ├── 解释命令含义
    ├── 提供最佳实践
    └── 快速迭代优化

注意事项

  • AI 生成的配置需要验证
  • 理解每行命令的含义
  • 根据实际情况调整

九、实战案例:完整部署流程

9.1 需求描述

将一个 Nuxt3 应用容器化部署,要求:

  1. 镜像体积小于 300MB
  2. 支持环境变量配置
  3. 使用 Docker Compose 管理
  4. 生产环境就绪

9.2 完整实现

步骤 1:创建 .dockerignore

plaintext
.nuxt
.output
node_modules
.github
.git
.DS_Store
*.log

步骤 2:创建 Dockerfile

dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
RUN npm config set registry https://registry.npmmirror.com
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 生产阶段
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/.output ./.output
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", ".output/server/index.mjs"]

步骤 3:创建环境变量文件

bash
# .env.production
BASE_URL=http://api.example.com

步骤 4:创建 docker-compose.yml

yaml
version: '3.8'
services:
  nuxt-app:
    build: .
    image: nuxt-app:latest
    container_name: nuxt-app-prod
    restart: always
    ports:
      - "8005:3000"
    env_file:
      - .env.production

步骤 5:构建和运行

bash
# 构建镜像
docker compose build

# 启动服务
docker compose up -d

# 查看日志
docker compose logs -f

# 访问应用
open http://localhost:8005

十、学习要点总结

核心要点

  1. 多阶段构建是必须掌握的技能

    • 减小镜像体积 80%+
    • 构建环境与运行环境分离
    • 生产镜像更安全
  2. 环境变量配置的三种方式

    • 命令行 -e 参数(适合测试)
    • Docker Compose environment(适合简单配置)
    • env_file 引用 .env 文件(推荐生产使用)
  3. 理解不同框架的容器化差异

    • Nuxt3:bundleDependencies 自动打包依赖
    • NestJS:需手动复制 node_modules + prisma generate
    • Next.js:类似的 .next 目录处理
  4. 善用 AI 工具提升效率

    • 快速生成 Dockerfile 模板
    • 解释命令含义
    • 优化镜像配置
  5. 调试能力比记忆命令更重要

    • 掌握 docker logs 排查问题
    • 理解错误信息
    • 知道如何查找解决方案

十一、延伸学习资源

官方文档

进阶主题

code
推荐学习路径:
│
├── 1. Docker 网络管理
│   ├── bridge 网络
│   ├── overlay 网络
│   └── 服务间通信
│
├── 2. Docker 数据管理
│   ├── Volume 持久化
│   ├── Bind Mount
│   └── 数据备份恢复
│
├── 3. CI/CD 集成
│   ├── GitHub Actions
│   ├── GitLab CI
│   └── 自动化构建部署
│
└── 4. Kubernetes 入门
    ├── Pod/Service/Deployment
    ├── 容器编排
    └── 生产级部署

实践建议

  1. 动手实践:按照笔记完整实现一遍
  2. 错误记录:记录遇到的错误和解决方案
  3. 性能对比:测试不同优化策略的效果
  4. 扩展应用:尝试容器化其他项目(Next.js、NestJS)

附录:常用命令速查表

Docker 命令

bash
# 镜像管理
docker build -t name:tag .      # 构建镜像
docker images                   # 查看镜像列表
docker rmi <image_id>           # 删除镜像
docker tag old new              # 重命名镜像

# 容器管理
docker run -d -p 8001:3000 img  # 运行容器
docker ps                       # 查看运行容器
docker stop <container>         # 停止容器
docker rm <container>           # 删除容器
docker logs -f <container>      # 查看日志
docker exec -it <container> sh  # 进入容器

# 清理命令
docker system prune             # 清理未使用资源
docker volume prune             # 清理未使用卷

Docker Compose 命令

bash
docker compose up -d            # 启动服务
docker compose down             # 停止服务
docker compose ps               # 查看状态
docker compose logs -f          # 查看日志
docker compose restart          # 重启服务
docker compose build            # 重新构建

下一步学习:NestJS 应用容器化实战、Docker Compose 多服务编排、CI/CD 自动化部署流水线。