前端 Docker 开发与生产部署指南
本文档以
yudao-ui-admin-uniapp项目(Vue3 + uniapp + vite + pnpm)为例,系统讲解前端项目在 Docker 环境下的开发与生产部署。所有概念均从零讲起,无需 Docker 基础。
目录
- [一、Docker 核心概念](#一、Docker 核心概念 "#%E4%B8%80docker-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5")
- [二、Docker Desktop 侧边栏菜单详解](#二、Docker Desktop 侧边栏菜单详解 "#%E4%BA%8Cdocker-desktop-%E4%BE%A7%E8%BE%B9%E6%A0%8F%E8%8F%9C%E5%8D%95%E8%AF%A6%E8%A7%A3")
- 三、镜像、容器、数据卷的空间占用与关系
- [四、.nvmrc 机制与本地方案](#四、.nvmrc 机制与本地方案 "#%E5%9B%9Bnvmrc-%E6%9C%BA%E5%88%B6%E4%B8%8E%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%A1%88")
- [五、前端 Docker 开发流程(yudao-ui-admin-uniapp)](#五、前端 Docker 开发流程(yudao-ui-admin-uniapp) "#%E4%BA%94%E5%89%8D%E7%AB%AF-docker-%E5%BC%80%E5%8F%91%E6%B5%81%E7%A8%8Byudao-ui-admin-uniapp")
- 六、端口映射与网络访问原理
- 七、数据卷的共用与隔离原则
- [八、Node 服务通过环境变量配置存储路径](#八、Node 服务通过环境变量配置存储路径 "#%E5%85%ABnode-%E6%9C%8D%E5%8A%A1%E9%80%9A%E8%BF%87%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E9%85%8D%E7%BD%AE%E5%AD%98%E5%82%A8%E8%B7%AF%E5%BE%84")
- [九、Compose 编排详解](#九、Compose 编排详解 "#%E4%B9%9Dcompose-%E7%BC%96%E6%8E%92%E8%AF%A6%E8%A7%A3")
- [十、一个 nginx 容器托管多个前端](#十、一个 nginx 容器托管多个前端 "#%E5%8D%81%E4%B8%80%E4%B8%AA-nginx-%E5%AE%B9%E5%99%A8%E6%89%98%E7%AE%A1%E5%A4%9A%E4%B8%AA%E5%89%8D%E7%AB%AF")
- [十一、生产环境 Docker 配置](#十一、生产环境 Docker 配置 "#%E5%8D%81%E4%B8%80%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83-docker-%E9%85%8D%E7%BD%AE")
- [十二、Docker 最佳实践与避坑指南](#十二、Docker 最佳实践与避坑指南 "#%E5%8D%81%E4%BA%8Cdocker-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E4%B8%8E%E9%81%BF%E5%9D%91%E6%8C%87%E5%8D%97")
- 十三、日常使用:图形界面操作与日志查看
- [十四、Docker 磁盘存储位置与空间管理](#十四、Docker 磁盘存储位置与空间管理 "#%E5%8D%81%E5%9B%9Bdocker-%E7%A3%81%E7%9B%98%E5%AD%98%E5%82%A8%E4%BD%8D%E7%BD%AE%E4%B8%8E%E7%A9%BA%E9%97%B4%E7%AE%A1%E7%90%86")
- 十五、快捷启动命令
- [十六、uniapp 多平台 Docker 开发与构建](#十六、uniapp 多平台 Docker 开发与构建 "#%E5%8D%81%E5%85%ADuniapp-%E5%A4%9A%E5%B9%B3%E5%8F%B0-docker-%E5%BC%80%E5%8F%91%E4%B8%8E%E6%9E%84%E5%BB%BA")
一、Docker 核心概念
Docker 是一种"轻量级虚拟化"技术,它把应用和依赖打包成一个标准化单元(容器),让应用能在任何环境里一致地运行。
1.1 三个核心对象
| 概念 | 通俗理解 | 类比 | 特性 |
|---|---|---|---|
| 镜像(Image) | 应用的"安装包",包含运行所需的所有文件 | 系统安装光盘 ISO | 只读、可分发、可重建 |
| 容器(Container) | 镜像跑起来的"活实例" | 装好正在运行的系统 | 可读写、可启停、可删除 |
| 数据卷(Volume) | Docker 管理的持久化存储 | 插在电脑上的 U 盘 | 独立于容器生命周期,删容器不删数据 |
1.2 三者关系
arduino
Dockerfile ──build──> Image(镜像:只读模板)
│
│ docker run / docker compose up
▼
Container(容器:活的进程)
│
│ 挂载(mount)
▼
Volume(数据卷:持久化数据)
1.3 与虚拟机的区别
| 维度 | 虚拟机 | Docker 容器 |
|---|---|---|
| 隔离级别 | 硬件级(每个 VM 有完整 OS) | 进程级(共享宿主机内核) |
| 启动速度 | 分钟级 | 秒级 |
| 资源占用 | GB 级 | MB 级 |
| 镜像大小 | 几 GB | 几十 MB ~ 几百 MB |
二、Docker Desktop 侧边栏菜单详解
Docker Desktop 是 Docker 在 Windows/Mac 上的图形界面。左侧菜单逐条说明:
| 菜单 | 是什么 | 通俗理解 |
|---|---|---|
| Containers | 容器列表 | 正在跑(或停着)的进程实例。你的 uniapp dev server 就在这里面 |
| Images | 镜像列表 | 容器的"安装包"。如 node:20-alpine、yudao-ui-admin-uniapp-uniapp-dev |
| Volumes | 数据卷列表 | Docker 管理的持久化存储。如 uniapp_node_modules 命名卷 |
| Builds | 构建历史 | 每次 docker build / docker compose up --build 的一条记录 |
| Docker Scout | 安全扫描 | 检查镜像里有没有漏洞依赖(高级功能,日常不常用) |
| Extensions | 扩展插件 | 类似 VSCode 插件,给 Docker Desktop 加功能 |
2.1 Images 页面的状态标识
| 状态 | 含义 | 操作建议 |
|---|---|---|
| In use | 正被某个容器引用 | 不能直接删,先停容器 |
| Unused | 没有容器使用 | 可以删,释放空间 |
2.2 Builds 历史
每次执行 docker build 或 docker compose up --build,Docker Desktop 会记一条构建历史。只在 build 时新增,普通 up 不会新增。失败的构建也会记录,可在 Builds 页面点进去看错误日志。
三、镜像、容器、数据卷的空间占用与关系
三者都占用磁盘空间,只是大小和生命周期不同。
3.1 用"装电脑"类比
镜像(Image) = 系统安装光盘(ISO) → 占空间最大
容器(Container)= 装好的系统,正在运行 → 在镜像之上加一层薄的可写层,占用小
数据卷(Volume) = U 盘,插电脑上存数据 → 大小看你存了多少东西
3.2 实际占用示例(yudao-ui-admin-uniapp 项目)
| 东西 | 类比 | 占用空间 | 删掉会怎样 |
|---|---|---|---|
yudao-ui-admin-uniapp-uniapp-dev 镜像 |
uniapp 安装盘 | ~700 MB | 容器跑不了,需要重新 build |
uniapp-dev 容器(运行中) |
装好的 uniapp 系统 | 几 MB(可写层) | 服务停了,数据卷还在 |
uniapp_node_modules 数据卷 |
存 node_modules 的 U 盘 | 几百 MB | 依赖没了,下次启动要重装 |
3.3 为什么"删容器不会删镜像"
ini
你电脑装了个游戏(镜像 ISO = 50GB)
运行起来玩(容器 = 游戏进程 + 存档)
存档放在 U 盘里(数据卷)
关机(删容器)→ 游戏还在(镜像还在)
拔 U 盘(删数据卷)→ 存档没了,但游戏还能重新运行
删安装盘(删镜像)→ 游戏彻底没了,需要重新下载
3.4 空间清理建议(按收益排序)
- 删 Unused 镜像:Images 页面 → 删 Unused 状态的镜像
- 删停止的容器:Containers 页面 → 删 Exited 状态的容器
- 删废弃的数据卷:Volumes 页面 → 删没被任何容器引用的卷
- 一键全清:Docker Desktop → Troubleshoot → Clean / Purge data(谨慎)
Docker 会自动回收未使用的镜像层(garbage collection),平时不用刻意清理,磁盘紧张时再手动清。
四、.nvmrc 机制与本地方案
4.1 什么是 .nvmrc
.nvmrc 是一个文本文件,内容只有一行,指定项目所需的 Node.js 版本:
20
也可以写精确版本(20.18.0)或 LTS 代号(lts/iron)。
4.2 工作机制
.nvmrc 本身不做事,它依赖 Node 版本管理器工具来读取:
| 工具 | 安装 | 自动读取 .nvmrc |
|---|---|---|
| nvm | `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash` |
| fnm | `curl -fsSL https://fnm.vercel.app/install | bash` |
| volta | `curl https://get.volta.sh | bash` |
4.3 典型本地开发流程
bash
# 1. 在项目根目录创建 .nvmrc
echo "20" > .nvmrc
# 2. 进入项目目录,切换 Node 版本
cd /path/to/project
nvm use # 读取 .nvmrc,切到 Node 20
# 3. 安装依赖、启动
pnpm install
pnpm dev
4.4 .nvmrc 与 Docker 的关系
两者无关,解决的是不同问题:
| 方案 | 解决的问题 | 机制 |
|---|---|---|
.nvmrc |
本机切 Node 版本 | 依赖 nvm/fnm 工具读文件 |
| Docker | 整个运行环境隔离 | FROM node:20-alpine 直接锁定版本 |
Docker 里 Node 版本由 Dockerfile 的 FROM 指令直接锁定,比 .nvmrc 更硬。本项目 package.json 里 engines.node >= 20,所以 Docker 用 node:20-alpine。
五、前端 Docker 开发流程(yudao-ui-admin-uniapp)
5.1 为什么前端要用 Docker 开发
| 痛点 | Docker 解决方案 |
|---|---|
| 团队成员 Node 版本不一致 | 镜像锁定版本,人人一样 |
| Windows 装某些 npm 包失败(如 node-sass) | 容器内是 Linux,原生兼容 |
| 新人入职装环境要半天 | docker compose up 一条命令搞定 |
| 切换项目时依赖冲突 | 每个项目独立容器,互不干扰 |
5.2 项目关键事实(影响 Docker 方案)
| 项 | 现状 | 对 Docker 的影响 |
|---|---|---|
| Node 版本 | engines.node >= 20,packageManager: pnpm@10.10.0 |
用 node:20-alpine + corepack |
| 平台二进制 | @esbuild/darwin-arm64、@esbuild/darwin-x64、@rollup/rollup-darwin-x64 写死 |
不能 把宿主机 node_modules 挂进容器,必须容器内 pnpm i |
| dev server | host: '0.0.0.0', port: 9000, hmr: true |
已绑 0.0.0.0,端口映射即用 |
| 后端代理 | VITE_APP_PROXY_ENABLE=true,代理前缀 /yudao-server/admin-api |
需补 VITE_SERVER_TARGET 指向后端 |
| predev 钩子 | predev 会跑 create-base-files.js 生成 pages.json |
容器内 pnpm dev:h5 会自动触发 |
5.3 三个核心文件
5.3.1 Dockerfile.dev
dockerfile
FROM node:20-alpine
# git 被 husky 间接需要;corepack 启用 pnpm@10
RUN apk add --no-cache git && corepack enable
WORKDIR /app
# 只拷依赖描述文件,利用 docker 层缓存。源码通过 volume 挂载,不 COPY。
COPY package.json pnpm-lock.yaml .npmrc ./
# --ignore-scripts: 跳过 prepare 钩子(init-husky / init-baseFiles),
# 因为 scripts/ 目录此时还没拷进来。base 文件会在运行时由 predev 钩子生成。
RUN pnpm install --frozen-lockfile --ignore-scripts
EXPOSE 9000
# 默认 H5 开发模式,predev 钩子会自动生成 pages.json 等基础文件
CMD ["pnpm", "dev:h5"]
5.3.2 docker-compose.dev.yml
yaml
services:
uniapp-dev:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "9000:9000"
volumes:
# 源码挂进容器:IDE 改代码 → 容器内 vite HMR
- .:/app
# 隔离 Linux node_modules,避免被 Windows 宿主机二进制污染
- uniapp_node_modules:/app/node_modules
environment:
# 后端 yudao-server 地址:
# - 后端在宿主机 IDE 跑 → host.docker.internal
# - 后端在局域网某机器跑 → 直接写 IP,如 192.168.1.182
- VITE_SERVER_TARGET=http://host.docker.internal:48080
# 容器里没有微信开发者工具,跳过 openDevTools 插件
- SKIP_OPEN_DEVTOOLS=true
extra_hosts:
- "host.docker.internal:host-gateway"
stdin_open: true
tty: true
volumes:
uniapp_node_modules:
5.3.3 .dockerignore
arduino
node_modules
dist
.git
docs
.image
.husky
.vscode
.github
*.log
5.4 启动与使用
bash
cd yudao-ui-admin-uniapp
docker compose -f docker-compose.dev.yml up
浏览器打开 http://localhost:9000/admin-ui-uniapp/。
5.5 工作流程
bash
┌────────────────────────────────────────────────────────────┐
│ 宿主机(Windows) │
│ ┌─────────────────────┐ │
│ │ VSCode/IDE │ ← 你在这里改代码 │
│ │ yudao-ui-admin- │ │
│ │ uniapp/src/... │ │
│ └──────────┬──────────┘ │
│ │ 源码目录通过 volume 挂载 │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Docker 容器 │ │
│ │ - pnpm dev:h5 │ ← vite dev server 在这里跑 │
│ │ - vite (port 9000) │ │
│ │ - node_modules │ ← 命名卷,Linux 二进制 │
│ └──────────┬──────────┘ │
│ │ 端口映射 9000:9000 │
│ ▼ │
│ 浏览器访问 http://localhost:9000 │
└────────────────────────────────────────────────────────────┘
改代码 → 容器内 vite 监听文件变化 → HMR 推送更新到浏览器。整个过程对开发者透明,和本地开发体验一致。
5.6 常见问题
Q1:pnpm install 时报 MODULE_NOT_FOUND: create-base-files.js
原因 :pnpm install 自动跑 prepare 脚本,但构建时只拷了 package.json,scripts/ 目录还没进去。
解决 :加 --ignore-scripts 跳过 prepare 钩子。base 文件会在运行时由 predev 钩子生成(那时源码已挂载)。
Q2:拉镜像报 dialing registry-1.docker.io:443 超时
原因:国内访问 Docker Hub 不稳定。
解决:配 Docker 镜像加速。Docker Desktop → Settings → Docker Engine:
json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
或换基础镜像源为阿里云:FROM registry.cn-hangzhou.aliyuncs.com/library/node:20-alpine。
Q3:HMR 不生效
可能原因:vite HMR websocket 连不上。
解决 :在 docker-compose.dev.yml 的 environment 加:
yaml
- VITE_HMR_HOST=localhost
- VITE_HMR_PORT=9000
并在 vite.config.ts 的 server 配置加 hmr.host 和 hmr.clientPort。
六、端口映射与网络访问原理
6.1 为什么需要端口映射
容器有自己的网络(默认 bridge),宿主机无法直接访问容器端口。必须把宿主机端口映射到容器端口。
yaml
浏览器访问 localhost:9000
│
▼
宿主机 9000 端口
│ ports: "9000:9000"
▼
容器内 9000 端口(vite dev server 监听)
6.2 映射格式
yaml
ports:
- "9000:9000" # 宿主机端口:容器端口
- "8080:80" # 宿主机 8080 → 容器 80
- "127.0.0.1:9000:9000" # 只允许本机访问
6.3 vite 为什么需要 host: '0.0.0.0'
vite 默认只监听 localhost,容器内 localhost 只能容器自己访问。改为 0.0.0.0 表示监听所有网卡,端口映射才能把流量转进来。
本项目 vite.config.ts 已配 host: '0.0.0.0',无需改动。
6.4 容器访问宿主机服务(host.docker.internal)
容器内要访问宿主机跑的服务(如后端 IDE 跑的 yudao-server),用特殊域名 host.docker.internal:
yaml
environment:
- VITE_SERVER_TARGET=http://host.docker.internal:48080
extra_hosts:
- "host.docker.internal:host-gateway"
- Windows/Mac :Docker Desktop 自带
host.docker.internal - Linux :需要
extra_hosts配置才能用
6.5 容器间互访(服务名 DNS)
同一个 compose 网络里的容器,可以用服务名互相访问,Docker 自动建 DNS:
yaml
services:
mysql:
# ...
yudao-server:
environment:
# 用服务名 mysql,不是 IP
- SPRING_REDIS_HOST=redis
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/qs_crm
七、数据卷的共用与隔离原则
7.1 node_modules 数据卷不能多个项目共用
即使几个前端项目依赖看起来一样,也不要共享一个 node_modules 数据卷。原因:
| 问题 | 解释 |
|---|---|
| 并发写冲突 | 多个容器同时往同一个数据卷写,可能互相覆盖或锁死 |
| 隐形差异 | 表面依赖相同,实际 package-lock.yaml 锁定的精确版本、子依赖树结构可能不同 |
| 项目隔离 | A 项目升级了一个 patch 版本,B 项目不知情就崩了 |
正确做法:每个项目一个独立的数据卷,命名区分:
yaml
# 项目 A
volumes:
project_a_node_modules:
# 项目 B
volumes:
project_b_node_modules:
数据卷在磁盘上只存"差异层",底层公共镜像层是共享的,所以不浪费空间。
7.2 业务数据卷按需加
项目需要存文件(如用户上传),单独加一个数据卷:
yaml
volumes:
- uniapp_node_modules:/app/node_modules # 依赖隔离
- uniapp_uploads:/app/public/uploads # 上传文件持久化
7.3 命名卷 vs bind mount
| 类型 | 语法 | 特点 | 适用场景 |
|---|---|---|---|
| 命名卷(Named Volume) | uniapp_node_modules:/app/node_modules |
Docker 管理,路径不透明 | 依赖缓存、数据库数据 |
| bind mount | ./uploads:/app/public/uploads |
映射宿主机目录,可直接查看 | 上传文件、日志、配置文件 |
关键原则:
node_modules必须用命名卷(跨平台二进制问题,宿主机和容器 OS 不同)- 业务数据两种都行,看是否需要宿主机直接访问
八、Node 服务通过环境变量配置存储路径
8.1 标准做法
Node.js 通过 process.env 读环境变量,Docker 通过 environment 注入,数据卷挂载到对应路径。三方解耦。
Node 代码
javascript
const fs = require('fs');
const path = require('path');
// 从环境变量读保存路径,没配置就默认 /app/uploads
const uploadDir = process.env.UPLOAD_DIR || '/app/uploads';
// 确保目录存在
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// 保存文件
fs.writeFileSync(path.join(uploadDir, 'test.txt'), 'hello');
Docker Compose
yaml
services:
my-node-app:
build: .
environment:
- UPLOAD_DIR=/app/data/uploads
volumes:
- node_modules:/app/node_modules
- upload_data:/app/data/uploads # 数据卷路径与环境变量一致
volumes:
node_modules:
upload_data:
8.2 优势
| 优势 | 说明 |
|---|---|
| 环境隔离 | 开发/测试/生产用不同路径,代码不动 |
| 路径可配 | 不改代码,改 compose 即可 |
| 持久化 | 数据卷独立于容器,删容器数据还在 |
九、Compose 编排详解
9.1 什么是 Compose
一句话:把"几个容器怎么一起跑"写在一个 yaml 里,一条命令全起全停。
9.2 不用 Compose vs 用 Compose
css
不用 Compose(手动):
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=xxx ...一堆参数
docker run -d --name redis --requirepass xxx ...一堆参数
docker run -d --name server --link mysql --link redis ...一堆参数
docker run -d --name nginx -p 80:80 ...一堆参数
→ 容器间 IP 怎么互通?启动顺序谁先?挂了怎么重启?全靠你手动管
用 Compose(自动):
docker compose up -d
→ 一条命令,按依赖顺序起,自动建网络,自动重启,自动清理
9.3 Compose 文件结构
yaml
services: # 定义所有容器
服务名1:
image: ... # 用现成镜像
build: ... # 或从 Dockerfile 构建
ports: ... # 端口映射
volumes: ... # 数据卷挂载
environment: ... # 环境变量
depends_on: ... # 依赖关系(启动顺序)
restart: ... # 重启策略
volumes: # 定义数据卷
卷名:
networks: # 定义网络(可选,默认会建 bridge)
网络名:
9.4 关键字段说明
| 字段 | 作用 | 示例 |
|---|---|---|
image |
用现成镜像 | image: mysql:8.0 |
build |
从 Dockerfile 构建 | build: { context: ./server, dockerfile: Dockerfile } |
ports |
端口映射 | "80:80" = 宿主机 80 → 容器 80 |
volumes |
挂载 | mysql_data:/var/lib/mysql = 命名卷挂到容器路径 |
environment |
环境变量 | MYSQL_ROOT_PASSWORD=123456 |
depends_on |
依赖 | 等 mysql 起来再起 server |
restart |
重启策略 | always = 挂了自动重启 |
healthcheck |
健康检查 | 定期检查, unhealthy 会触发重启 |
container_name |
容器名 | qs-mysql(不指定则用 目录名-服务名-1) |
9.5 常用命令
bash
docker compose up -d # 后台启动所有服务
docker compose up -d --build # 强制重新构建镜像
docker compose ps # 查看服务状态
docker compose logs -f 服务名 # 看日志
docker compose stop # 停止(不删容器)
docker compose down # 停止并删除容器/网络(保留数据卷)
docker compose down -v # 停止并删除容器+数据卷(谨慎!)
十、一个 nginx 容器托管多个前端
10.1 场景
有 3 个前端项目:
yudao-ui-admin-uniapp(uniapp H5)yudao-ui-admin-vue3(PC 后台)yudao-mall-h5(商城 H5)
不想每个都起一个容器,想用一个 nginx 容器统一托管。
10.2 原理
nginx 根据 URL 路径分流到不同静态目录:
bash
浏览器请求 nginx 容器内
─────────────────────────────────────────────────────
/admin-uniapp/... ───► /usr/share/nginx/html/admin-uniapp/index.html
/admin-vue3/... ───► /usr/share/nginx/html/admin-vue3/index.html
/mall-h5/... ───► /usr/share/nginx/html/mall-h5/index.html
/yudao-server/... ───► proxy_pass http://yudao-server:48080
10.3 物理结构
css
┌─────────────── nginx 容器 ───────────────┐
│ /usr/share/nginx/html/ │
│ ├── admin-uniapp/ ← uniapp H5 产物 │
│ ├── admin-vue3/ ← vue3 后台产物 │
│ └── mall-h5/ ← 商城 H5 产物 │
│ │
│ /yudao-server/ → 反代到后端容器 │
└──────────────────────────────────────────┘
10.4 Compose 配置示例
yaml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./frontend-a/dist:/usr/share/nginx/html/a
- ./frontend-b/dist:/usr/share/nginx/html/b
- ./frontend-c/dist:/usr/share/nginx/html/c
depends_on:
- yudao-server
10.5 nginx 配置示例
nginx
server {
listen 80;
# 前端 A
location /admin-uniapp/ {
root /usr/share/nginx/html;
try_files $uri $uri/ /admin-uniapp/index.html;
}
# 前端 B
location /admin-vue3/ {
root /usr/share/nginx/html;
try_files $uri $uri/ /admin-vue3/index.html;
}
# 后端 API 反向代理
location /yudao-server/ {
proxy_pass http://yudao-server:48080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
10.6 对比:每个前端一个容器
| 方案 | 优点 | 缺点 |
|---|---|---|
| 一个 nginx 托管多前端 | 省资源、配置集中 | 改一个前端要重建 nginx 镜像/重挂卷 |
| 每个前端一个容器 | 隔离性好、独立升级 | 资源占用多、需要外面再套一层反代 |
十一、生产环境 Docker 配置
11.1 开发 vs 生产 的差异
| 维度 | 开发 | 生产 |
|---|---|---|
| 前端服务 | vite dev server(带 HMR) | nginx 托管静态文件 |
| 后端服务 | IDE 里跑 | 容器里跑 jar |
| 数据库 | 连宿主机/局域网现有 | 容器里跑(数据卷持久化) |
| 源码挂载 | 是(HMR 需要) | 否(镜像里打包产物) |
| 自动重启 | 不需要 | restart: always |
| 日志 | 控制台看 | 挂卷到宿主机或日志中心 |
11.2 生产 Dockerfile(前端,多阶段构建)
dockerfile
# ===== 阶段 1:构建 H5 产物 =====
FROM node:20-alpine AS builder
RUN corepack enable
WORKDIR /app
COPY package.json pnpm-lock.yaml .npmrc ./
RUN pnpm install --frozen-lockfile --ignore-scripts
COPY . .
# 触发 prebuild:h5 钩子生成 pages.json,然后构建
RUN pnpm build:h5
# ===== 阶段 2:nginx 运行 =====
FROM nginx:alpine
# 把构建产物复制到 nginx 的 /admin-ui-uniapp/ 子路径
COPY --from=builder /app/dist/build/h5 /usr/share/nginx/html/admin-ui-uniapp
# nginx 配置模板(用 envsubst 注入后端地址)
COPY nginx.conf /etc/nginx/templates/default.conf.template
EXPOSE 80
11.3 nginx 配置模板(生产)
nginx
server {
listen 80;
server_name _;
# gzip 压缩
gzip on;
gzip_types text/plain text/css application/javascript application/json;
# 前端静态文件(子路径部署)
location /admin-ui-uniapp/ {
alias /usr/share/nginx/html/admin-ui-uniapp/;
try_files $uri $uri/ /admin-ui-uniapp/index.html;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
# 后端 API 反向代理(不做 rewrite,后端 prefix 已为 /yudao-server/admin-api)
location /yudao-server/ {
proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
envsubst 机制 :nginx 官方镜像支持
/etc/nginx/templates/*.template文件,启动时自动用环境变量替换${VAR}。所以${BACKEND_HOST}在容器启动时通过-e BACKEND_HOST=xxx注入,无需重新构建镜像即可切换后端地址。
11.4 生产 docker-compose.yml(完整编排)
yaml
services:
# ──────────────── 数据库 ────────────────
mysql:
image: mysql:8.0
container_name: qs-mysql
restart: always
environment:
MYSQL_DATABASE: qs_crm
MYSQL_ROOT_PASSWORD: rongyi123$qwer
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init-sql:/docker-entrypoint-initdb.d # 首启自动导入 SQL
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# ──────────────── 缓存 ────────────────
redis:
image: redis:7-alpine
container_name: qs-redis
restart: always
command: redis-server --requirepass rongyi.com --appendonly yes
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "rongyi.com", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ──────────────── 后端 ────────────────
yudao-server:
build:
context: ./yudao-boot-mini/yudao-server
dockerfile: Dockerfile
container_name: qs-server
restart: always
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
environment:
JAVA_OPTS: "-Xms512m -Xmx512m -Djava.security.egd=file:/dev/./urandom"
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL: jdbc:mysql://mysql:3306/qs_crm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_USERNAME: root
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD: rongyi123$qwer
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: "6379"
SPRING_REDIS_DATABASE: "3"
SPRING_REDIS_PASSWORD: rongyi.com
TZ: Asia/Shanghai
ports:
- "48080:48080"
# ──────────────── 前端(nginx + H5 静态文件)────────────────
uniapp-h5:
build:
context: ./yudao-ui-admin-uniapp
dockerfile: Dockerfile
container_name: qs-uniapp
restart: always
depends_on:
- yudao-server
environment:
BACKEND_HOST: yudao-server # 用服务名访问后端
BACKEND_PORT: "48080"
ports:
- "80:80"
volumes:
mysql_data:
redis_data:
11.5 生产部署流程
bash
# 1. 后端打包 jar
cd yudao-boot-mini
mvn clean package -DskipTests
# 2. 数据库初始化 SQL 放到 init-sql/ 目录
mkdir -p init-sql
cp sql/mysql/ruoyi-vue-pro.sql init-sql/
cp sql/mysql/quartz.sql init-sql/
# 3. 一键启动
cd ..
docker compose up -d
# 4. 查看状态
docker compose ps
# 5. 看日志
docker compose logs -f yudao-server
11.6 为什么一容器一服务(不把前端后端塞一个容器)
不推荐塞一个容器里跑多个服务,原因:
| 问题 | 后果 |
|---|---|
| 一个挂了全挂 | 前端崩了,后端也跟着重启 |
| 资源不隔离 | 一个内存泄漏拖垮全家 |
| 升级困难 | 改一个组件要重建整个镜像 |
| 日志混乱 | 所有服务日志混在一起 |
| 横向扩展不行 | 想给后端加副本?做不到 |
推荐架构:一容器一服务,用 compose 编排。
十二、Docker 最佳实践与避坑指南
12.1 Dockerfile 最佳实践
| 实践 | 原因 |
|---|---|
| 用 alpine 基础镜像 | 体积小(~50MB vs ~900MB) |
| 多阶段构建 | 构建工具不进最终镜像 |
.dockerignore 排除无关文件 |
减小构建上下文 |
| 依赖描述文件单独 COPY + install | 利用层缓存,改代码不重装依赖 |
| 不用 root 用户运行 | 安全 |
| 合并 RUN 指令 | 减少镜像层数 |
12.2 Compose 最佳实践
| 实践 | 原因 |
|---|---|
| 用命名卷持久化数据 | 删容器不丢数据 |
depends_on + healthcheck |
确保依赖服务就绪 |
restart: always 生产必备 |
挂了自动重启 |
| 服务名互通而非 IP | Docker 自动 DNS,IP 会变 |
敏感信息用 .env 文件 |
不把密码写死在 compose |
12.3 前端 Docker 常见坑
| 坑 | 解决 |
|---|---|
| Windows 宿主机 node_modules 挂进容器报错 | 用命名卷隔离,不让宿主机覆盖 |
| vite HMR 不工作 | 检查 host: '0.0.0.0' 和端口映射 |
prepare 钩子构建失败 |
加 --ignore-scripts |
| 镜像拉不下来 | 配国内镜像加速 |
| 容器内访问 localhost 指向容器自己 | 用 host.docker.internal 访问宿主机 |
12.4 资源清理
bash
# 删所有停止的容器
docker container prune
# 删所有 Unused 镜像
docker image prune -a
# 删所有未引用的数据卷
docker volume prune
# 一键清理(谨慎)
docker system prune -a
附录:本项目文件清单
| 文件 | 用途 | 阶段 |
|---|---|---|
yudao-ui-admin-uniapp/Dockerfile.dev |
开发用,跑 vite dev server | 开发 |
yudao-ui-admin-uniapp/docker-compose.dev.yml |
开发编排 | 开发 |
yudao-ui-admin-uniapp/Dockerfile.build |
构建用,含 Android SDK | 构建/开发 |
yudao-ui-admin-uniapp/docker-compose.build.yml |
构建编排 | 构建 |
yudao-ui-admin-uniapp/docker-compose.mp-dev.yml |
小程序/App 开发编排 | 开发 |
yudao-ui-admin-uniapp/.dockerignore |
排除无关文件 | 通用 |
yudao-ui-admin-uniapp/Dockerfile |
生产用,多阶段构建 + nginx | 生产 |
yudao-ui-admin-uniapp/nginx.conf |
nginx 配置模板 | 生产 |
docker-compose.yml |
生产编排(mysql+redis+server+uniapp) | 生产 |
十三、日常使用:图形界面操作与日志查看
Docker Desktop 的图形界面足以覆盖日常 90% 的操作,命令行只在初始化、重建镜像时用。
13.1 一次创建,终身点击
容器首次创建后,就会一直在 Docker Desktop 的 Containers 列表里。日常启停只需点击按钮,不用敲命令。
第一次:初始化容器(只需做一次)
bash
cd yudao-ui-admin-uniapp
docker compose -f docker-compose.dev.yml up -d
# 或: pnpm dev:docker
这步创建容器 uniapp-dev。之后它会常驻在 Containers 列表里。
日常使用:点击启停
打开 Docker Desktop → 左侧 Containers → 找到 uniapp-dev:
| 操作 | 方法 |
|---|---|
| 启动 | 点击容器右侧 ▶️ Start |
| 暂停 | 点击容器右侧 ⏸️ Stop |
| 重启 | 点击容器右侧 🔄 Restart |
底层等价于 docker start <name> / docker stop <name>。
13.2 up vs start 的关键区别
| 命令 | 作用 | 适用场景 |
|---|---|---|
docker compose up |
创建并启动容器 | 第一次初始化、或 down 删除后重建 |
docker compose down |
停止并删除容器 | 彻底清理 |
docker start <name> |
启动已存在的容器 | 日常开机后启动服务 |
docker stop <name> |
停止运行中的容器 | 临时暂停 |
日常用 Docker Desktop 点击 Start / Stop 即可 ,只有以下情况需要重新 up(重建):
| 场景 | 命令 |
|---|---|
修改了 Dockerfile.dev |
pnpm dev:docker:rebuild |
修改了 docker-compose.dev.yml 的端口/挂载等配置 |
pnpm dev:docker |
down 删除了容器想重开 |
pnpm dev:docker |
仅仅是改代码 → 完全不用重启容器! vite HMR 会自动热更新。
13.3 图形界面查看日志
打开容器 → 切换到 Logs 标签页,即可看到实时日志。与命令行 docker logs -f 完全等价,显示的是同一份 stdout/stderr 输出。
| Docker Desktop 操作 | 等价命令行 |
|---|---|
| 打开容器 → Logs 标签页 | docker logs <容器名> |
| 切换 "Follow log"(▶️ 图标) | docker logs -f <容器名> |
| 滚动查看历史 | docker logs --tail <行数> |
Logs 页面技巧
| 操作 | 方法 |
|---|---|
| 实时跟踪 | 点右上角 ▶️ Follow log 图标(变成 ⏸️ 时代表暂停跟踪) |
| 清空当前显示 | 点 🗑️ Clear 图标(只是清屏,不影响后台日志) |
| 跳到最新 | 点 Jump to bottom 图标 |
| 筛选关键字 | 顶部搜索框输入,只显示匹配行 |
| 复制日志 | 选中文字 → Ctrl+C |
日常用 Docker Desktop 的 Logs 页看就好,不用开终端跑 docker compose logs -f。
十四、Docker 磁盘存储位置与空间管理
14.1 默认存储位置
Docker Desktop 在 Windows 上把所有镜像、容器、数据卷存在一个 WSL2 虚拟磁盘文件里:
kotlin
C:\Users\<你的用户名>\AppData\Local\Docker\wsl\data\ext4.vhdx
所有镜像、容器、数据卷都打包在这一个 .vhdx 文件里,文件会随着使用增长,但不会自动收缩。
14.2 查看当前占用
方式一 :Docker Desktop → 左下角 Troubleshoot → Clean / Purge data 旁边能看到当前磁盘使用量。
方式二:命令行
bash
wsl -d docker-desktop -e df -h
方式三:查看 Docker 整体占用
bash
docker system df
输出示例:
sql
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 3 2 1.2GB 350MB
Containers 2 1 50MB 20MB
Local Volumes 3 2 800MB 100MB
14.3 更改存储位置
当镜像太多、C 盘吃紧时,可以把 Docker 数据迁到其他盘。
步骤:
-
停止 Docker Desktop:右下角托盘右键 → Quit Docker Desktop
-
复制虚拟磁盘文件到新位置:
makefile源: C:\Users\<用户名>\AppData\Local\Docker\wsl\data\ext4.vhdx 目标: D:\Docker\ext4.vhdx -
修改 Docker Desktop 设置:
- 启动 Docker Desktop
- Settings → Resources → Disk image location
- Browse 选择新路径(如
D:\Docker\) - Apply & Restart
重要 :直接改路径不迁移数据的话,Docker 会重新创建空磁盘,之前所有镜像容器都没了。必须先复制
.vhdx文件到新位置。
14.4 限制磁盘大小
Settings → Resources → Disk image size → 拖动调整最大容量(默认通常 64GB)。
WSL2 的
.vhdx是动态扩展的,64GB 表示上限,实际占用看里面数据多少。
14.5 快速腾空间
改位置之前先清理无用数据,通常能省下几个 GB:
bash
# 删所有停止的容器
docker container prune
# 删所有 Unused 镜像
docker image prune -a
# 删所有未引用的数据卷
docker volume prune
# 一键清理(删所有未使用的东西)
docker system prune -a
或图形界面:Docker Desktop → Images → 勾掉 Unused 的删除;Volumes → 删没被引用的卷。
14.6 压缩 vhdx 文件(进阶)
即使删了镜像,.vhdx 文件也不会自动缩小。要回收磁盘空间:
powershell
# 1. 关闭 Docker Desktop
# 2. 关闭 WSL
wsl --shutdown
# 3. 压缩磁盘
diskpart
# 在 diskpart 提示符里:
select vdisk file="D:\Docker\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit
十五、快捷启动命令
为减少记忆负担,把常用 Docker 命令封装到 package.json 的 scripts 里,与项目现有 pnpm dev、pnpm build:mp 等风格一致。
15.1 命令列表
| 命令 | 作用 |
|---|---|
pnpm dev:docker |
启动 Docker 开发容器 |
pnpm dev:docker:stop |
停止开发容器 |
pnpm dev:docker:rebuild |
改了依赖/Dockerfile 后重建镜像并启动 |
pnpm dev:docker:logs |
查看容器日志 |
15.2 使用流程
bash
# 第一次启动(创建容器 + 构建镜像 + 启动)
cd yudao-ui-admin-uniapp
pnpm dev:docker
# 日常使用:直接用 Docker Desktop 点 Start / Stop(见第十三章)
# 改了 package.json 依赖后
pnpm dev:docker:rebuild
# 临时停掉容器
pnpm dev:docker:stop
# 查看日志(与 Docker Desktop Logs 页等价)
pnpm dev:docker:logs
15.3 等价对照表
| 快捷命令 | 等价的 docker 命令 |
|---|---|
pnpm dev:docker |
docker compose -f docker-compose.dev.yml up |
pnpm dev:docker:stop |
docker compose -f docker-compose.dev.yml down |
pnpm dev:docker:rebuild |
docker compose -f docker-compose.dev.yml up --build |
pnpm dev:docker:logs |
docker compose -f docker-compose.dev.yml logs -f |
十六、uniapp 多平台 Docker 开发与构建
uniapp 支持 H5、小程序、App(Android/iOS)多端,所有开发和构建工作都可以放进 Docker。
16.1 两种开发模式
uniapp 的开发模式分两种,这是理解 Docker 开发的关键:
| 类型 | H5 | 小程序 / App |
|---|---|---|
| 本质 | dev server(vite 实时服务) | 编译产物 + watch(文件变了自动重编译) |
| 产物 | 无产物(内存中运行) | 有产物(dist/dev/ 目录) |
| 预览方式 | 浏览器直接访问 URL | 宿主机工具打开产物目录 |
| 热更新 | vite HMR(无需刷新) | 重新编译 + 刷新工具 |
16.2 H5 开发 vs 小程序/App 开发
H5 开发流程
less
Docker 容器内: pnpm dev:h5 (vite dev server, 端口 9000)
↓
宿主机端口映射 9000:9000
↓
浏览器访问 http://localhost:9000
↓
改代码 → vite HMR → 浏览器自动刷新
特点:容器内跑 dev server,浏览器直接连,无产物。
小程序开发流程
bash
Docker 容器内: pnpm dev:mp-weixin (watch 模式持续编译)
↓ 产物映射
宿主机 dist/dev/mp-weixin/ ← 编译产物在这里
↓
宿主机微信开发者工具 → 导入这个目录
↓
改代码 → 容器内自动重编译 → 开发者工具自动刷新
特点:容器内跑 watch 编译,产物映射到宿主机,用宿主机的开发者工具预览。
App 开发流程(Android)
bash
Docker 容器内: pnpm dev:app-android (watch 模式持续编译)
↓ 产物映射
宿主机 dist/dev/app/ ← 编译产物在这里
↓
宿主机 HBuilderX → 打开项目 → 运行到真机/模拟器
特点:同小程序,产物映射到宿主机,用宿主机工具预览。
16.3 各平台支持情况
| 平台 | Docker 开发 | Docker 构建 | 预览方式 |
|---|---|---|---|
| H5 | ✅ | ✅ | 浏览器访问 URL |
| 微信小程序 | ✅ | ✅ | 微信开发者工具(宿主机) |
| 支付宝等小程序 | ✅ | ✅ | 对应开发者工具(宿主机) |
| Android App | ✅ | ✅ | HBuilderX / Android Studio(宿主机) |
| iOS App | ❌ | ❌ | 必须 macOS |
16.4 开发快捷命令
H5 开发(已实现)
bash
pnpm dev:docker # 启动 H5 dev server
pnpm dev:docker:stop # 停止
小程序/App 开发
bash
pnpm dev:docker:mp-weixin # 启动微信小程序 watch 编译
pnpm dev:docker:mp-alipay # 启动支付宝小程序 watch 编译
pnpm dev:docker:app-android # 启动 Android App watch 编译
pnpm dev:docker:mp-stop # 停止所有
pnpm dev:docker:mp-logs # 查看日志
16.5 小程序/App 开发使用步骤
步骤 1:启动 watch 编译
bash
cd yudao-ui-admin-uniapp
pnpm dev:docker:mp-weixin
步骤 2:用宿主机工具预览
- 微信小程序 :打开微信开发者工具 → 导入项目 → 目录选择
dist/dev/mp-weixin - 支付宝小程序 :打开支付宝小程序开发者工具 → 导入项目 → 目录选择
dist/dev/mp-alipay - Android App:打开 HBuilderX → 运行 → 运行到手机或模拟器
步骤 3:改代码自动刷新
在 VSCode 改代码保存后:
- Docker 容器内自动检测文件变化
- 自动重新编译
- 产物更新到
dist/dev/目录 - 开发者工具自动刷新(需在开发者工具中开启"自动编译")
16.6 构建快捷命令(生产构建)
bash
pnpm build:docker:h5 # 构建 H5 → dist/docker/h5/
pnpm build:docker:mp-weixin # 构建微信小程序 → dist/docker/mp-weixin/
pnpm build:docker:app-android # 构建 Android App → dist/docker/app/
pnpm build:docker:shell # 进入容器 shell 手动构建
16.7 开发 vs 构建 对比
| 维度 | 开发 (dev:docker:xxx) |
构建 (build:docker:xxx) |
|---|---|---|
| 执行频率 | 启动一次,持续运行 | 需要构建时才跑 |
| 容器生命周期 | 常驻运行 | 一次性(构建完自动删) |
| 产物目录 | dist/dev/ |
dist/docker/ |
| 改代码后 | 自动重编译 + 工具自动刷新 | 需要重新跑命令 |
| 核心命令 | pnpm dev:mp-weixin(watch 模式) |
pnpm build:mp-weixin(单次构建) |
16.8 各平台开发差异总结
| 维度 | H5 | 小程序 | App |
|---|---|---|---|
| Docker 内跑什么 | vite dev server | uni watch 编译 | uni watch 编译 |
| 预览工具 | 浏览器 | 开发者工具(宿主机) | HBuilderX(宿主机) |
| 产物位置 | 无 | dist/dev/mp-xxx | dist/dev/app |
| 改代码刷新 | 浏览器自动 | 开发者工具自动 | HBuilderX 自动 |
| 端口映射 | 需要(9000) | 不需要 | 不需要 |
16.9 核心文件说明
Dockerfile.build(构建/开发共用镜像)
dockerfile
FROM node:20-alpine
# 基础工具 + Android SDK 依赖
RUN apk add --no-cache \
git \
curl \
unzip \
openjdk17 \
&& corepack enable
# 配置 Android SDK
ENV ANDROID_HOME=/opt/android-sdk
ENV ANDROID_SDK_ROOT=/opt/android-sdk
ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk
# 安装 Android SDK Command-line Tools
ARG ANDROID_CLI_VERSION=11076708
RUN mkdir -p $ANDROID_HOME/cmdline-tools && \
curl -L "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CLI_VERSION}_latest.zip" -o /tmp/android-cli.zip && \
unzip -q /tmp/android-cli.zip -d /tmp/ && \
mv /tmp/cmdline-tools $ANDROID_HOME/cmdline-tools/latest && \
rm -rf /tmp/*
# 接受 SDK 许可并安装必要组件
RUN yes | sdkmanager --licenses > /dev/null 2>&1 || true && \
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0" > /dev/null 2>&1 || true
WORKDIR /app
# 先拷贝依赖描述文件,利用层缓存
COPY package.json pnpm-lock.yaml .npmrc ./
RUN pnpm install --frozen-lockfile --ignore-scripts
# 默认命令:交互式 shell
CMD ["/bin/sh"]
关键设计:
- 基于
node:20-alpine,体积小 - 内置 Android SDK 34(Android App 开发/构建需要)
--ignore-scripts跳过 prepare 钩子(base 文件在 watch 启动时由 predev 生成)- 构建和开发共用同一个镜像,区别只在 compose 编排和启动命令
docker-compose.build.yml(生产构建编排)
yaml
services:
uniapp-build:
build:
context: .
dockerfile: Dockerfile.build
container_name: uniapp-build
volumes:
- .:/app
# 产物输出到宿主机 dist/docker/ 目录
- ./dist/docker/h5:/app/dist/build/h5
- ./dist/docker/mp-weixin:/app/dist/build/mp-weixin
- ./dist/docker/mp-alipay:/app/dist/build/mp-alipay
- ./dist/docker/app:/app/dist/build/app
- uniapp_build_node_modules:/app/node_modules
- android_sdk:/opt/android-sdk
environment:
- ANDROID_HOME=/opt/android-sdk
- SKIP_OPEN_DEVTOOLS=true
- WECHAT_DEVTOOLS_CLI_PATH=/bin/echo
command: /bin/sh
stdin_open: true
tty: true
volumes:
uniapp_build_node_modules:
android_sdk:
关键设计:
- 产物目录映射到
dist/docker/(生产构建产物) android_sdk命名卷持久化,避免重复下载 SDKuniapp_build_node_modules隔离 Linux 依赖- 配合
--rm使用,容器一次性
docker-compose.mp-dev.yml(小程序/App 开发编排)
yaml
services:
uniapp-mp-dev:
build:
context: .
dockerfile: Dockerfile.build
container_name: uniapp-mp-dev
volumes:
- .:/app
# 产物目录映射到宿主机 dist/dev/(开发产物)
- ./dist/dev/mp-weixin:/app/dist/dev/mp-weixin
- ./dist/dev/mp-alipay:/app/dist/dev/mp-alipay
- ./dist/dev/app:/app/dist/dev/app
- uniapp_mp_node_modules:/app/node_modules
- android_sdk:/opt/android-sdk
environment:
- SKIP_OPEN_DEVTOOLS=true
- WECHAT_DEVTOOLS_CLI_PATH=/bin/echo
- ANDROID_HOME=/opt/android-sdk
command: /bin/sh
stdin_open: true
tty: true
volumes:
uniapp_mp_node_modules:
android_sdk:
关键设计:
- 产物目录映射到
dist/dev/(开发产物,区别于构建的dist/docker/) - 常驻运行 (不配
--rm),容器跑 watch 编译 - 复用
Dockerfile.build镜像(已含 Android SDK) node_modules用独立命名卷,与 build 容器隔离
三套 compose 文件对比
| 文件 | 用途 | 产物目录 | 容器生命周期 | 端口 |
|---|---|---|---|---|
docker-compose.dev.yml |
H5 开发 | 无(dev server) | 常驻 | 9000:9000 |
docker-compose.mp-dev.yml |
小程序/App 开发 | dist/dev/ |
常驻 | 无 |
docker-compose.build.yml |
生产构建 | dist/docker/ |
一次性(--rm) |
无 |
三个 compose 共用两个镜像:
Dockerfile.dev(仅 H5 开发,轻量)和Dockerfile.build(含 Android SDK,H5/小程序/App 通用)。
全文完。有疑问随时问。