Docker 场景下 Nginx 部署 Vue 项目的实践
·3 分钟
在 Docker 中使用 Nginx 部署 Vue 前端,并同时代理 API 与图片等静态资源的完整实践,总结端口冲突、502 问题等
nginx
docker
vue
proxy
背景 #
今天在公司有点时间,想要试试前端部署的一个完整流程,当然这只是一个简单的 docker+nginx 部署流程。
- 前端:Vue build 后的 dist 静态文件
- 后端接口:运行在
172.xx.xx.xx:66xx - 图片资源路径:
/uploads/** - 对外访问端口:不占用现有服务,选择一个不常用端口
运行环境说明 #
宿主机
└── Docker
└── nginx 容器
├── 提供 Vue 静态页面
├── 反向代理 API
└── 反向代理图片资源
部署环境 #
一、基础准备(Docker 环境) #
1. 确认 Docker 是否安装 #
docker -v
输出类似:
Docker version 27.x.x
说明 Docker 已可用。
2. 确认 Docker Compose(v2) #
docker compose version
若能正常输出版本号,即可继续。
二、创建项目目录结构 #
mkdir -p /data/xiaoss/qywx
cd /data/xiaoss/qywx
最终目录规划如下:
qywx/
├── docker-compose.yml
├── html/
│ └── qywx_fe/ # Vue build 后的文件
├── conf/
│ └── nginx.conf
└── logs/
三、创建 docker-compose.yml(核心) #
1. 新建文件 #
nano docker-compose.yml
2. 编写内容 #
version: "3.9"
services:
nginx:
image: nginx:latest
container_name: nginx_qywx
ports:
- "9110:80"
volumes:
- ./html:/usr/share/nginx/html
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx
restart: unless-stopped
说明:
image: nginx:latest→ 自动从 Docker Hub 拉取官方 nginx 镜像9110:80→ 宿主机 9110 → 容器 80volumes→ 挂载前端、配置、日志目录
四、拉取 Nginx 并启动容器 #
1. 启动(首次会自动拉镜像) #
docker compose up -d
等价于:
docker pull nginx:latest- 创建容器
- 后台运行
2. 查看运行状态 #
docker ps
应看到:
nginx_qywx nginx:latest Up 0.0.0.0:9110->80/tcp
五、准备 Vue 前端静态文件 #
将 Vue 打包产物放入:
/data/xiaoss/qywx/html/qywx_fe
例如:
qywx_fe/
├── index.html
├── js/
├── css/
└── assets/
六、Nginx 配置(关键部分) #
1. 创建 nginx.conf #
mkdir -p conf
nano conf/nginx.conf
2. 最终配置内容 #
worker_processes 1;
events { worker_connections 1024; }
http {
include mime.types;
default_type application/octet-stream;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization,Origin, X-Requested-With, Content-Type, Accept";
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name _;
# Vue 前端
root /usr/share/nginx/html/qywx_fe;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 图片资源代理
location /uploads/ {
proxy_pass http://172.xx.xx.xx:66xx;
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;
proxy_read_timeout 60s;
}
# API 代理
location /api {
proxy_pass http://172.xx.xx.xx:66xx;
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;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
}
七、重启容器使配置生效 #
docker compose down
docker compose up -d
问题与排查过程 #
1. 端口冲突问题 #
最开始使用的时候选的默认 80 端口,但是服务器中已经有使用的了:
ports:
- "80:80"
直接报错:bind: address already in use
排查后发现:
- 宿主机已有系统 nginx 占用 80
- 多个 docker-proxy 已占用 8000+ 端口
后续选择更换端口,随便找了一个没用过的端口号:
ports:
- "9110:80"
2. 当我一切准备完毕后,发现项目中需要匹配域名端口的 /uploads/** 图片无法访问 #
在前端代码中使用的是:
/uploads/excellentDisplay/good2.jpg
打包并部署后发现:
- 页面正常
- 图片访问失败或返回 HTML
经过 AI 的原因分析是因为:
Vue 使用了 history 模式,nginx 中有:
location / {
try_files $uri $uri/ /index.html;
}
该配置会:
- 吞掉所有未被单独匹配的路径
/uploads/**被当成前端路由处理- fallback 到
index.html
关键解决方案 #
1. 必须为 /uploads 单独配置 location #
这是整个问题中 最关键的一点。
location /uploads/ {
proxy_pass http://172.16.99.32:6690;
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;
proxy_read_timeout 60s;
}
2. 完整 Nginx 配置(最终版) #
worker_processes 1;
events { worker_connections 1024; }
http {
include mime.types;
default_type application/octet-stream;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization,Origin, X-Requested-With, Content-Type, Accept";
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name _;
# Vue 前端
root /usr/share/nginx/html/qywx_fe;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 图片资源代理
location /uploads/ {
proxy_pass http://172.16.99.32:6690;
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;
proxy_read_timeout 60s;
}
# API 代理
location /api {
proxy_pass http://172.16.99.32:6690;
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;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
}
Docker Compose 配置(最终版) #
version: "3.9"
services:
nginx:
image: nginx:latest
container_name: nginx_qywx
ports:
- "9110:80"
volumes:
- ./html:/usr/share/nginx/html
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx
restart: unless-stopped
日志处理 #
在最后我发现 nginx 是没有日志输出,不好判断问题的情况,于是我加上 nginx 的日志输出。
为了便于排查 502、代理错误等问题:
- 将容器内日志目录映射到宿主机
- 直接查看真实
access.log/error.log
- ./logs:/var/log/nginx
最后经过一系列操作完成部署流程。
关键经验总结 #
- Docker 场景下需要判断端口的占用情况,尤其是在服务器中可能存在多个 docker 的情况下。
- Vue history 模式一定要注意
try_files的副作用 location的顺序非常重要- 日志一定要持久化,否则排错非常痛苦
- 页面正常 ≠ 资源与接口正常,有可能接口资源出现网络 502 错误导致不正常