一、文档说明
本文档基于当前 SPC 工业平台完整 Docker 微服务架构编写,适用于项目统一入口 Nginx 反向代理网关。
覆盖内容:Nginx 容器架构说明、目录结构、完整配置、启停重载、配置更新、故障排查、子路径代理、WebSocket 转发、API 网关转发、生产运维规范。
适配架构:Docker Compose + 双网络(public-net 外网访问、internal-net 服务内网)
二、Nginx 网关架构概述
1. 核心定位
Nginx 作为项目统一流量入口,承载所有前端页面、后端 API、WebSocket 长连接的反向代理,统一对外暴露 80/443 端口,实现动静分离、路由分发、路径重写、跨域代理。
2. 网络模式说明
-
public-net(桥接外网):Nginx、前端服务、网关服务暴露外网可访问端口
-
internal-net(私有内网):所有后端微服务内网互通,不对外暴露,保障服务安全
3. 代理业务清单
-
主前端 SPC.Web:根路径 / 与子路径 /spc-web/ 双访问适配
-
3D 可视化前端 SPC.3D:子路径 /spc-3d/ 访问
-
WebSocket 实时长连接:/ws/ 路由转发至 API 网关
-
全局 API 接口:/api/ 统一转发至网关服务 55000 端口
三、Nginx 部署目录结构
部署根目录:与 docker-compose.yml 同级
./proxy
├── nginx
│ └── conf.d/ # 站点配置目录(所有代理规则存放处)
│ └── default.conf # 核心代理配置文件
└── ssl/ # HTTPS 证书存放目录
ocker中网关必须有如下内容:
bash
// WS路由:精确匹配 /ws/xxx 高优先级
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "ws",
"DownstreamHostAndPorts": [
{
"Host": "spcoverviewservice",
"Port": 9019
}
],
"UpstreamPathTemplate": "/ws/{everything}",
"UpstreamHttpMethod": [ "GET" ],
"Priority": 10
},
// WS兜底:纯/ws 根路径
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "ws",
"DownstreamHostAndPorts": [
{
"Host": "spcoverviewservice",
"Port": 9019
}
],
"UpstreamPathTemplate": "/ws/",
"UpstreamHttpMethod": [ "GET" ],
"Priority": 5
}
四、Docker Compose Nginx 服务配置
以下为项目生产可用 Nginx 完整服务配置,集成日志、时区、挂载、依赖、网络策略。
bash
version: "3.8"
networks:
public-net:
driver: bridge
internal-net:
external: true
services:
# 统一入口Nginx网关
proxy:
image: 10.31.20.10:8081/build-tools/nginx:1.25
container_name: spc-proxy
restart: always
ports:
- "56000:80"
- "443:443"
volumes:
- ./proxy/nginx/conf.d:/etc/nginx/conf.d
- ./proxy/ssl:/etc/nginx/ssl
environment:
- TZ=Asia/Shanghai
networks:
- public-net
depends_on:
- spc-web
- spc-3d
- spcwebgateway
# 主前端应用
spc-web:
image: 10.31.20.10:8081/sensesmill-docker/spc.web:beta
container_name: spc-web
restart: always
expose:
- "80"
volumes:
- ./web/spc.web/app-config/app-config.prod.json:/usr/share/nginx/html/assets/file/app-config/app-config.prod.json
environment:
- TZ=Asia/Shanghai
networks:
- public-net
# 3D可视化前端
spc-3d:
image: 10.31.20.10:8081/sensesmill-docker/spc.3d:beta
container_name: spc-3d
restart: always
expose:
- "80"
volumes:
- ./web/spc.3d/app-config/config.json:/usr/share/nginx/html/config/config.json
- ./web/spc.3d/app-config/dataConfig.json:/usr/share/nginx/html/config/dataConfig.json
environment:
- TZ=Asia/Shanghai
networks:
- public-net
# API网关服务
spcwebgateway:
image: 10.31.20.10:8081/sensesmill-docker/spcwebgateway:beta
container_name: spcwebgateway
restart: always
ports:
- "55000:80"
- "55443:443"
volumes:
- ./spcwebgateway/Logs:/app/Logs
- ./spcwebgateway/config/appsettings.json:/app/appsettings.json
- ./spcwebgateway/config/ocelot.json:/app/ocelot.json
environment:
- TZ=Asia/Shanghai
- ASPNETCORE_URLS=http://+:80
logging:
driver: json-file
options:
max-size: 10m
max-file: "31"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/Health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- public-net
- internal-net
# 认证授权中心
spcplantdataservice-authserver:
image: 10.31.20.10:8081/sensesmill-docker/spcplantdataservice-authserver:beta
container_name: spcplantdataservice-authserver
restart: always
ports:
- "55001:80"
volumes:
- ./spcplantdataservice-authserver/Logs:/app/Logs
- ./spcplantdataservice-authserver/config/appsettings.json:/app/appsettings.json
environment:
- TZ=Asia/Shanghai
- ASPNETCORE_URLS=http://+:80
logging:
driver: json-file
options:
max-size: 10m
max-file: "31"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/Health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- public-net
- internal-net
# 工厂基础数据服务
spcplantdataservice:
image: 10.31.20.10:8081/sensesmill-docker/spcplantdataservice:beta
container_name: spcplantdataservice
restart: always
ports:
- "55002:80"
volumes:
- ./spcplantdataservice/Logs:/app/Logs
- ./spcplantdataservice/config/appsettings.json:/app/appsettings.json
- ./spcplantdataservice/BlobStoreFiles:/app/BlobStoreFiles
- ./spcplantdataservice/file:/app/file
environment:
- TZ=Asia/Shanghai
- ASPNETCORE_URLS=http://+:80
logging:
driver: json-file
options:
max-size: 10m
max-file: "31"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/Health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- public-net
- internal-net
配置说明
-
镜像:私有仓库 Nginx1.25 稳定版
-
端口映射:外网 56000 映射容器80,443 用于HTTPS
-
配置挂载:本地 conf.d 目录挂载容器配置目录,支持热更新
-
证书挂载:统一存放 SSL 证书文件
-
依赖启动:优先等待前端、网关服务启动后再启动 Nginx
-
网络隔离:仅接入外网网络,不直接接入内网服务网络,提升安全性
五、Nginx 核心代理配置(default.conf)
适配项目子路径访问、静态资源重写、WebSocket、API转发完整配置,可直接覆盖使用。
server {
listen 80;
server_name _;
# SPC.Web - 根路径(保持原样)
location / {
proxy_pass http://spc-web:80;
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;
}
# SPC.Web - 子路径访问(新增)
location /spc-web/ {
proxy_pass http://spc-web:80/;
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;
# 替换 HTML 中的路径前缀(如果 spc-web 使用根路径引用资源)
sub_filter '<base href="/">' '<base href="/spc-web/">';
sub_filter 'src="/' 'src="/spc-web/';
sub_filter 'href="/' 'href="/spc-web/';
sub_filter_once off;
}
# SPC.3D - 子路径访问
location /spc-3d/ {
proxy_pass http://spc-3d:80/;
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;
sub_filter '<base href="/">' '<base href="/spc-3d/">';
sub_filter 'src="/' 'src="/spc-3d/';
sub_filter 'href="/' 'href="/spc-3d/';
sub_filter_once off;
}
# WebSocket
location /ws/ {
proxy_pass http://spcwebgateway:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
# API
location /api/ {
proxy_pass http://spcwebgateway:80;
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 120s;
proxy_read_timeout 120s;
}
}
或者直接挂载nginx.conf
bash
# ------------------------
# Docker容器内Nginx 80端口网关主机配置
# 作用:统一入口反向代理内部各业务容器(spc-web/spc-3d/spcwebgateway)
# 容器网络内可直接通过容器名作为域名通信,无需宿主机IP
# ------------------------
server {
# 容器内监听80端口,对外由docker端口映射暴露访问
listen 80;
# 匹配所有访问域名,不限制前端访问域名
server_name _;
# ======================================
# 1. SPC.Web前端容器 - 根路径访问
# 外部访问地址:http://宿主机IP/
# 转发目标:同Docker网络内 spc-web 容器80端口
# proxy_pass 末尾无斜杠:完整保留客户端原始请求路径转发给前端容器
# ======================================
location / {
proxy_pass http://spc-web:80;
# 透传客户端原始访问域名,前端/后端可获取真实Host
proxy_set_header Host $host;
# 传递客户端真实公网IP(宿主机转发场景日志溯源用)
proxy_set_header X-Real-IP $remote_addr;
# 多级代理链路完整IP记录
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 透传当前访问协议 http/https,适配鉴权、跳转逻辑
proxy_set_header X-Forwarded-Proto $scheme;
}
# ======================================
# 2. SPC.Web前端容器 - 子路径 /spc-web/ 访问
# 外部访问地址:http://宿主机IP/spc-web/
# 场景:前端打包仅适配根路径 "/",无法重新打包,用sub_filter动态替换HTML内资源路径
# Docker容器代理必须双关闭压缩:gzip off + 清空Accept-Encoding,防止上游容器返回压缩HTML导致替换失效
# proxy_pass末尾带/:自动截断URL前缀/spc-web/,转发给spc-web容器根路径
# ======================================
location /spc-web/ {
proxy_pass http://spc-web:80/;
# 透传客户端请求基础信息头
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;
# 【Docker代理关键配置】局部关闭当前location的gzip压缩
gzip off;
# 清空Accept-Encoding请求头,告知上游spc-web容器不要返回gzip压缩包,保证HTML明文输出
proxy_set_header Accept-Encoding "";
# 替换页面基础路由基准标签,所有资源/路由以 /spc-web/ 为根
sub_filter '<base href="/">' '<base href="/spc-web/">';
# 替换JS/CSS/图片静态资源绝对路径,避免404
sub_filter 'src="/' 'src="/spc-web/';
# 替换页面内a标签跳转链接,防止路由丢失子路径前缀
sub_filter 'href="/' 'href="/spc-web/';
# sub_filter_once off:页面内所有匹配文本全部替换,不止替换第一个
sub_filter_once off;
}
# ======================================
# 3. SPC.3D可视化前端容器 - 子路径 /spc-3d/ 访问
# 外部访问地址:http://宿主机IP/spc-3d/
# 逻辑同/spc-web/,适配子目录部署,关闭压缩保障HTML字符串替换生效
# 转发目标:Docker内网 spc-3d 容器80端口
# ======================================
location /spc-3d/ {
proxy_pass http://spc-3d:80/;
# 透传客户端请求头
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;
# 【Docker代理关键配置】关闭压缩,获取明文HTML用于路径替换
gzip off;
proxy_set_header Accept-Encoding "";
# 替换3D页面全部根路径引用,适配/spc-3d/子目录访问
sub_filter '<base href="/">' '<base href="/spc-3d/">';
sub_filter 'src="/' 'src="/spc-3d/';
sub_filter 'href="/' 'href="/spc-3d/';
sub_filter_once off;
}
# ======================================
# 4. WebSocket长连接代理 /ws/
# 转发目标:网关容器 spcwebgateway:80
# 业务场景:实时数据推送、设备监控、在线消息、3D实时联动
# Docker容器间WebSocket必须开启HTTP/1.1、协议升级、延长读写超时
# ======================================
location /ws/ {
proxy_pass http://spcwebgateway:80;
# WebSocket协议升级强制依赖HTTP/1.1,HTTP1.0不支持
proxy_http_version 1.1;
# 标记协议升级:HTTP 切换 WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# 长连接空闲超时1小时,避免容器间ws连接被nginx主动断开
proxy_read_timeout 3600s;
}
# ======================================
# 5. 后端API接口统一代理 /api/
# 所有业务接口统一转发至网关容器 spcwebgateway:80
# 延长超时时间,适配大数据导出、报表慢查询、复杂生产计算接口
# ======================================
location /api/ {
proxy_pass http://spcwebgateway:80;
# 透传客户端真实IP、访问协议,后端鉴权/审计日志使用
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 120s;
# 接口响应读取超时,适配耗时业务接口
proxy_read_timeout 120s;
}
}
六、核心功能详解
1. 子路径适配原理
前端项目默认根路径打包,直接子路径访问会出现静态资源404。通过sub_filter 动态替换 html 内的 base、src、href 路径,实现无需改前端代码,Nginx 层自动适配子路径。
2. WebSocket 长连接支持
开启 HTTP1.1、Upgrade 升级头、超长读取超时(1小时),适配设备实时数据、报警推送、3D 实时联动场景,杜绝长连接断开问题。
3. API 统一转发
所有前端 /api/ 请求统一转发至后端网关服务 55000 端口,由 Ocelot 网关统一路由到各个微服务(设备、能耗、报警、生产、质量、时序数据服务)。
七、日常运维操作命令
所有命令在docker-compose.yml 同级目录执行
1. 启动 Nginx
docker compose up -d proxy
2. 停止 Nginx
docker compose down proxy
3. 重启 Nginx
docker compose restart proxy
4. 配置热重载(重要,不重启容器生效)
修改 nginx 配置后,必须执行重载,避免重启服务断流
# 检查配置语法
docker exec -it spc-proxy nginx -t
# 热重载配置
docker exec -it spc-proxy nginx -s reload
5. 查看 Nginx 日志
# 实时日志
docker logs -f spc-proxy
6. 进入容器内部调试
docker exec -it spc-proxy bash
八、常见故障排查方案
1. 子路径访问页面空白、静态资源404
原因:sub_filter 规则未生效或配置未重载
解决:检查配置文件、执行 nginx -t && nginx -s reload
2. WebSocket 连接失败、频繁断开
原因:未开启升级头、超时时间过短
解决:确认 location /ws/ 配置完整,超时时间不低于 3600s
3. API 接口 502/504 超时
原因:代理超时时间不足、网关服务未启动
解决:调整 proxy 超时参数,检查 spcwebgateway 健康状态
4. Nginx 启动失败
排查步骤:
-
检查配置语法:
docker exec spc-proxy nginx -t -
检查端口占用:80/443/56000
-
检查挂载目录权限
九、生产运维规范
-
所有 Nginx 配置修改必须先检查语法再重载,禁止直接重启容器
-
前端新增子页面、新服务路由,统一在 conf.d 中新增 location 规则
-
SSL 证书过期前提前替换,存放至 ./proxy/ssl 目录
-
严禁直接修改容器内配置,所有配置通过本地挂载文件管理
-
对外端口统一由 Nginx 暴露,后端微服务禁止直接对外开放端口
十、附录:项目完整代理路由对照表
|-----------|------------------|---------------|
| 访问路径 | 代理目标服务 | 用途 |
| / | spc-web:80 | 主系统首页 |
| /spc-web/ | spc-web:80 | 主系统子路径访问 |
| /spc-3d/ | spc-3d:80 | 3D可视化页面 |
| /ws/ | spcwebgateway:80 | 实时WebSocket推送 |
| /api/ | spcwebgateway:80 | 所有后端微服务接口 |