🐳 前端项目 Docker 打包实操指南
------ 从零到跑通,在你的电脑上一步一步把 Vue 项目打包成 Docker 镜像
📋 本文目录
- 准备工作(确认环境)
- 第一步:本地构建前端
- 第二步:创建 Docker 三件套
- 第三步:构建 Docker 镜像
- 第四步:运行容器并验证
- 日常开发常用命令
- 常见问题 FAQ
1. 准备工作:确认环境就绪
1.1 Docker Desktop 正在运行?
看右下角任务栏有没有 Docker 鲸鱼图标。打开 PowerShell 运行:
shell
docker version --format "{{.Server.Version}}"
如果能输出版本号(如 24.0.7),说明 Docker daemon 正常。如果报错,先启动 Docker Desktop 等它变成绿色。
1.2 Node.js 和依赖
shell
node -v # 需要 Node 16+
npm -v # 任意版本
# 项目根目录下要有 node_modules/ 目录
Test-Path node_modules # PowerShell,返回 True 才对
1.3 确认项目结构
你的项目根目录应该长这样:
text
webName/
├── src/ ← 前端源码
├── package.json ← 依赖和构建脚本
├── vite.config.js ← Vite 配置
├── docker/
│ ├── Dockerfile ← 要创建的
│ ├── nginx.conf ← 要创建的
│ └── static_file/ ← 已存在,头像图标等
└── dist/ ← 构建产物(下一步生成)
├── index.html
├── static/
└── ...
2. 第一步:本地构建前端
在项目根目录打开 PowerShell,运行:
shell
cd D:\pro\webName
$env:NODE_OPTIONS="--max-old-space-size=8192" # Node 默认 2GB 不够,加内存
npm run build:prod
⚠️ 为什么要加 NODE_OPTIONS?
你的项目有 4500+ 模块,默认 Node 堆内存 2GB 不够用,会报
JavaScript heap out of memory。设置--max-old-space-size=8192给 8GB 就够了。这个设置只在当前 PowerShell 窗口生效,关掉窗口要重新设置。
构建成功后,dist/ 目录里会有 index.html、static/、font/ 等文件,大小约 30MB。
遇到构建错误?
如果报 Vue 模板语法错误(Invalid end tag)或导入路径错误,说明代码里有 bug,先修掉再继续。这次构建就是帮你发现这些问题的好机会。
3. 第二步:创建 Docker 三件套
在 docker/ 目录下创建 3 个文件。
3.1 Dockerfile
在 docker/Dockerfile 写入:
dockerfile
# 基础镜像:nginx:alpine(~5MB,超小)
FROM nginx:alpine
# 设置时区
ENV TZ=Asia/Shanghai
# 复制 Nginx 配置文件
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
# 复制构建产物到 Nginx 静态文件目录
COPY dist /usr/share/nginx/html
# 复制静态资源文件(头像、图标等)
COPY docker/static_file /usr/share/static_file
RUN chown -R nginx:nginx /usr/share/static_file
# 声明监听端口
EXPOSE 80
# 启动 Nginx(daemon off = 前台运行,Docker 要求)
CMD ["nginx", "-g", "daemon off;"]
3.2 nginx.conf
在 docker/nginx.conf 写入:
nginx
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# ============ SPA 路由 fallback(重要!)============
# Vue Router history 模式下,刷新子路由会 404
# 先找 $uri 文件 → 再找 $uri/ 目录 → 都没有就返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
# ============ API 反向代理 ============
# 等后端部署好再打开,把 8080 改成后端端口
# location /prod-api/ {
# proxy_pass http://host.docker.internal:8080/;
# proxy_set_header Host $host;
# }
# ============ 静态资源缓存 ============
location ~* \.(js|css|png|jpg|svg|woff2?)$ {
expires 7d;
add_header Cache-Control "public, immutable";
}
# ============ 禁止访问隐藏文件 ============
location ~ /\. { deny all; }
}
3.3 .dockerignore(项目根目录)
在项目根目录创建 .dockerignore,告诉 Docker 构建时忽略哪些文件:
text
node_modules
*.log
.git
.github
.vscode
.idea
.env.local
npm-debug.log*
docker/docs
docker/*.tar.gz
*.md
.trae
.kilo
.mimocode
💡 为什么要用单阶段而不是多阶段构建?
多阶段构建(在 Docker 容器内跑 npm install + vite build)理论上更"专业",但你的项目有 4500+ 模块,Hyper-V VM 内存不够会崩。所以先用"主机 build + Docker 只打包运行时"的方式,稳定、快、好调试。等服务器上部署时再考虑多阶段。
4. 第三步:构建 Docker 镜像
在项目根目录运行:
shell
docker build -t webName:v1.0 -f docker/Dockerfile .
参数拆解:
| 参数 | 含义 |
|---|---|
-t webName:v1.0 |
给镜像打标签,格式是 名字:版本 |
-f docker/Dockerfile |
指定 Dockerfile 路径(不写的话默认找当前目录的 Dockerfile) |
最后的 . |
构建上下文目录,Docker 会把这个目录下所有文件发给构建引擎 |
成功后会看到:
text
#10 naming to docker.io/library/webName:v1.0 done
#10 DONE 0.3s
验证镜像:
shell
docker images webName
# REPOSITORY TAG IMAGE ID CREATED SIZE
# webName v1.0 0c670d47a203 22 seconds ago 99.4MB
✅ 镜像 99.4MB 正常
nginx:alpine 基础 ~5MB + dist ~30MB + static_file ~几十MB,加起来 99MB 没问题。如果是多阶段构建最终镜像会更小(~35MB),但当前方式够用。
5. 第四步:运行容器并验证
shell
# 启动容器,映射端口 8080 → 80
docker run -d --name tds-web -p 8080:80 webName:v1.0
# ^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^
# | | | 镜像名:版本号
# | | 主机8080 → 容器80
# | 容器名
# 后台运行
# 验证容器在跑
docker ps --filter "name=tds-web"
# 浏览器访问
# http://localhost:8080
浏览器打开 http://localhost:8080,应该能看到登录页面。🎉
🎯 如果页面空白或 404
检查 Nginx 配置里的
try_files $uri $uri/ /index.html;是否正确。没有这行,Vue Router 的 history 模式刷新子路由会 404。
快速验证清单:
| 验证项 | 命令 / 操作 | 期望结果 |
|---|---|---|
| 容器在跑 | docker ps |
STATUS 列显示 Up |
| HTTP 可访问 | 浏览器打开 localhost:8080 | 看到登录页面 |
| 刷新子路由 | 点一个菜单再 F5 | 不 404 |
| 进容器内部 | docker exec -it tds-web sh |
能 ls /usr/share/nginx/html |
🎊 恭喜!你已经跑通了
刚才的完整流程回顾:
shell
# 1. 本地构建前端
npm run build:prod
# 2. 创建 Dockerfile + nginx.conf + .dockerignore(已做好)
# 3. 构建 Docker 镜像
docker build -t webName:v1.0 -f docker/Dockerfile .
# 4. 运行容器
docker run -d --name tds-web -p 8080:80 webName:v1.0
# 5. 浏览器访问 http://localhost:8080 ✅
6. 日常开发常用命令
代码改了,重新部署:
shell
# 停止并删除旧容器
docker rm -f tds-web
# 重新构建前端
npm run build:prod
# 重新构建镜像(tag 改版本号,或用 latest)
docker build -t webName:v1.1 -f docker/Dockerfile .
# 启动新容器
docker run -d --name tds-web -p 8080:80 webName:v1.1
查看日志:
shell
# 查看容器日志
docker logs tds-web
# 实时追踪最后 100 行(Ctrl+C 退出)
docker logs -f --tail 100 tds-web
进容器调试:
shell
docker exec -it tds-web sh
# 进去后可以:
ls /usr/share/nginx/html # 看文件在不在
cat /etc/nginx/conf.d/default.conf # 看 Nginx 配置
nginx -t # 测试 Nginx 配置语法
清理:
shell
# 停容器
docker stop tds-web
# 删容器
docker rm tds-web
# 删镜像
docker rmi webName:v1.0
# 一键清理所有没用的镜像和容器(谨慎!)
docker system prune -a
7. 常见问题 FAQ
Q1: docker build 报 COPY dist not found
A: dist 目录被 .dockerignore 排除了。检查 .dockerignore 里有没有写 dist,有的话删掉(单阶段构建需要它)。
Q2: docker build 报 pull access denied 或超时
A: Docker Hub 在国内很慢。在 Docker Desktop → Settings → Docker Engine 里加镜像加速器:
json
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
]
}
点 Apply & Restart。
Q3: npm run build:prod 报 heap out of memory
A: Node 默认 2GB 不够。PowerShell 先设:$env:NODE_OPTIONS="--max-old-space-size=8192",再跑构建。
Q4: 页面能打开但 API 请求跨域
A: 这是前端和后端不在同一个 origin。两种解法:
- 推荐:Nginx 反向代理。在 nginx.conf 里取消 API 代理注释,把后端地址改成你实际的后端服务。
- 临时:后端开 CORS(不推荐长期用)。
Q5: Docker Desktop 启动报 WSL 错误
A: 你的系统用的是 Hyper-V 后端(settings.json 里 wslEngineEnabled: false)。不要改回 WSL,保持 Hyper-V 就行。
Q6: 端口被占用
shell
# 查找谁占了 8080
Get-NetTCPConnection -LocalPort 8080 -ErrorAction SilentlyContinue |
Select-Object LocalPort, OwningProcess, @{N='Process';E={(Get-Process -Id $_.OwningProcess).Name}}
# 换个端口
docker run -d --name tds-web -p 9090:80 webName:v1.0