应用进程与 Nginx 协同
生产环境最稳的模式通常是:Nginx 对公网,应用只对本机开放。
协同模型
graph LR
A["公网 443/80"] --> B[Nginx 反向代理]
B --> C["127.0.0.1:3000 应用"]
C --> D[systemd 托管进程]
B --> E["/var/log/nginx/ 访问日志"]
D --> F["journald 应用日志"]
完整 Nginx proxy_pass 配置块
# /etc/nginx/sites-available/myapp.conf
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# 上传文件大小限制(默认 1M,常见原因是 413 错误)
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# 传递真实客户端信息(应用日志/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;
# WebSocket 支持(普通 HTTP 应用可省略)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置
proxy_connect_timeout 10s; # 连接后端超时
proxy_read_timeout 60s; # 等待后端响应超时
proxy_send_timeout 60s; # 向后端发送数据超时
}
# 静态文件直接由 Nginx 提供(绕过应用,性能更好)
location /static/ {
alias /opt/myapp/current/public/;
expires 7d;
add_header Cache-Control "public, immutable";
}
# 健康检查端点(不记录访问日志,减少噪音)
location /health {
proxy_pass http://127.0.0.1:3000;
access_log off;
}
}
分层排查流程(502/504 场景)
graph TD
A[用户报告 502/504] --> B[curl -I https://app.example.com]
B --> C{Nginx 有响应?}
C -->|无响应| D[检查 Nginx 是否运行: systemctl status nginx]
C -->|502/504| E[确认应用进程状态]
E --> F[systemctl status myapp]
F --> G{myapp 运行中?}
G -->|否| H[journalctl -u myapp -n 50 查崩溃原因]
G -->|是| I[确认端口监听]
I --> J[ss -tulpn | grep 3000]
J --> K{端口监听?}
K -->|否| L[应用没有绑定正确端口,查启动日志]
K -->|是| M[curl -sf http://127.0.0.1:3000/health]
M --> N{本机直连通?}
N -->|否| O[应用逻辑错误,查应用日志]
N -->|是| P[Nginx 配置问题,nginx -t 检查语法]
快速诊断命令(60 秒版):
# 1. Nginx 状态
systemctl status nginx
# 2. 应用状态
systemctl status myapp
# 3. 应用端口监听
ss -tulpn | grep 3000
# 4. 本机直连应用
curl -sf http://127.0.0.1:3000/health && echo OK || echo FAIL
# 5. 通过 Nginx 访问
curl -sf https://app.example.com/health && echo OK || echo FAIL
# 6. 查看 Nginx 错误日志(最近 20 行)
sudo tail -20 /var/log/nginx/error.log
# 7. 查看应用日志
journalctl -u myapp -n 30 --no-pager
常见错误速查表
| 错误 | 含义 | 最先排查 |
|---|---|---|
502 Bad Gateway | Nginx 连接不到后端 | systemctl status myapp,应用是否崩溃 |
504 Gateway Timeout | 后端连接超时(proxy_read_timeout) | 应用响应慢,查数据库/计算瓶颈 |
413 Request Entity Too Large | 上传文件超过 client_max_body_size | 增大该值或在应用层拒绝大文件 |
upstream connect() failed | 后端端口未监听 | ss -tulpn | grep 3000 |
connection refused | 应用未启动或绑定了错误地址 | curl http://127.0.0.1:3000 本机直连测试 |
no live upstreams | 所有 upstream 节点均不健康 | upstream 健康检查配置问题 |
应用只监听本机(安全最佳实践)
# ✅ 正确:只绑定 localhost,外部无法直接访问应用端口
# Node.js
app.listen(3000, '127.0.0.1')
# Python (Gunicorn)
gunicorn --bind 127.0.0.1:3000 app:application
# 验证:应用端口不应对外暴露
ss -tulpn | grep 3000
# 应该看到:127.0.0.1:3000(而非 0.0.0.0:3000)
# 确认防火墙不放行应用端口(3000 不需要对外)
sudo ufw status
# 应该只有 22, 80, 443 对外开放
常见误区
| 误区 | 正确做法 |
|---|---|
| 改完应用没验证本机端口,直接查 Nginx | 先 curl http://127.0.0.1:3000/health 确认本机直连通 |
应用绑定 0.0.0.0:3000,防火墙没挡 | 应用只绑定 127.0.0.1,由 Nginx 对外提供服务 |
proxy_read_timeout 用默认值 60s | 长任务(报表、导出)需要调大,否则出现 504 |
没有传 X-Real-IP header | 应用看到的 IP 始终是 127.0.0.1,IP 限流和日志失效 |
本节执行清单
- [ ] 确认应用只监听
127.0.0.1(不是0.0.0.0) - [ ] 配置完整
proxy_pass块(含 4 个proxy_set_header) - [ ] 测试本机直连(
curl http://127.0.0.1:3000/health)正常再测 Nginx - [ ] 用 502 排查流程图熟悉分层诊断顺序
下一章:应用部署实战——正式把代码发布上去。