前端 Docker 开发与生产部署指南

前端 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-alpineyudao-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 builddocker 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 空间清理建议(按收益排序)

  1. 删 Unused 镜像:Images 页面 → 删 Unused 状态的镜像
  2. 删停止的容器:Containers 页面 → 删 Exited 状态的容器
  3. 删废弃的数据卷:Volumes 页面 → 删没被任何容器引用的卷
  4. 一键全清: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.jsonengines.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 >= 20packageManager: 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.jsonscripts/ 目录还没进去。

解决 :加 --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.tsserver 配置加 hmr.hosthmr.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 → 左下角 TroubleshootClean / 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 数据迁到其他盘。

步骤

  1. 停止 Docker Desktop:右下角托盘右键 → Quit Docker Desktop

  2. 复制虚拟磁盘文件到新位置:

    makefile 复制代码
    源: C:\Users\<用户名>\AppData\Local\Docker\wsl\data\ext4.vhdx
    目标: D:\Docker\ext4.vhdx
  3. 修改 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.jsonscripts 里,与项目现有 pnpm devpnpm 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 改代码保存后:

  1. Docker 容器内自动检测文件变化
  2. 自动重新编译
  3. 产物更新到 dist/dev/ 目录
  4. 开发者工具自动刷新(需在开发者工具中开启"自动编译")

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 命名卷持久化,避免重复下载 SDK
  • uniapp_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 通用)。


全文完。有疑问随时问。

相关推荐
扶疏52516 小时前
Prometheus+Prometheus Adapter+metrics-server+Ingress实现k8s水平自动扩缩容(HPA)
容器·kubernetes·prometheus
张忠琳16 小时前
【NPU】Ascend Docker Runtime v26.0.1 系统级架构分析
云原生·容器·kubernetes·npu·docker-runtime
江湖有缘17 小时前
保姆级教程:使用Docker一键部署Hoodik轻量级安全云盘
安全·docker·容器
湫默18 小时前
Kubernetes 基础集群部署
云原生·容器·kubernetes
xia54204644619 小时前
Docker 远程 API(Remote API)未授权访问漏洞入侵过程
运维·docker·容器
X1A0RAN20 小时前
容器化部署 Jenkins 教程
运维·docker·jenkins
程序员允诺20 小时前
Ubuntu 安装 Docker CE 和 Docker Compose 最新教程(2026)
ubuntu·docker·eureka
晚风吹长发1 天前
Docker基础——Docker Compose(容器编排)
linux·运维·服务器·docker·容器
tianyu2341 天前
Docker 数据目录迁移指南
linux·docker·容器·eureka·centos