DeployHub 部署平台故障排查全记录:从 Nginx 代理缺失到完整修复
一个真实的前后端分离项目部署踩坑记录,涉及 Nginx 配置冲突、后端超时机制、框架项目限制等完整解决方案。
项目背景
DeployHub 是一个一键部署 GitHub 项目的平台,用户输入仓库地址即可自动拉取、构建并部署静态网站。项目采用前后端分离架构:
- 前端:纯静态 HTML/CSS/JS,部署在 Nginx 目录
- 后端:Node.js API 服务,运行在
localhost:3001 - 域名:
qingyin.monster/deploy/ - 代理:Nginx 负责将
/api/请求转发到后端服务
问题现象
用户反馈:前端页面能正常加载,但点击"开始部署"后,请求直接卡住或返回 404。
排查过程
Step 1:定位核心问题
通过浏览器开发者工具和网络请求分析,发现:
- 访问
https://qingyin.monster/deploy/时,静态文件正常 - 点击部署后,
/api/deploy请求 没有代理到后端,直接 404
根本原因:certbot 自动生成的 qingyin-api Nginx 配置文件里,缺少 location /api/ 代理到后端 3001 端口的配置。
bash
default 文件里有 /api/ 代理 ← 但 certbot 没用到这个
qingyin-api 文件(实际生效的)里没有 /api/ 代理 ← ❌ 问题在这里
Step 2:发现隐藏炸弹 ------ 配置冲突
执行 nginx -t 时发现大量警告:
vbscript
conflicting server name "qingyin.monster" on 0.0.0.0:80, ignored
conflicting server name "www.qingyin.monster" on 0.0.0.0:80, ignored
conflicting server name "qingyin.monster" on 0.0.0.0:443, ignored
这意味着:有多个配置文件在监听同一个域名和端口,Nginx 只加载了第一个,其他的被忽略了。刚修改的 qingyin-api 可能根本没被加载!
Step 3:修复 Nginx 代理配置
3.1 备份当前配置
bash
sudo cp /etc/nginx/sites-enabled/qingyin-api /etc/nginx/sites-enabled/qingyin-api.bak.$(date +%s)
3.2 精确修复 proxy_pass
通过 grep -n "proxy_pass" 确认配置位置在第 33 行,内容是 proxy_pass http://localhost:3001;(注意:没有 /api/ 后缀)。
使用 sed 精确替换:
bash
# 用 sed 精确替换第 33 行
sudo sed -i '33s|proxy_pass http://localhost:3001;|proxy_pass http://localhost:3001/api/;|' /etc/nginx/sites-enabled/qingyin-api
# 确认修改成功
grep -n "proxy_pass" /etc/nginx/sites-enabled/qingyin-api
# 测试配置
sudo nginx -t
# 重载 Nginx
sudo systemctl reload nginx
Step 4:后端安全加固
修复代理后,发现后端还有几个潜在风险:
4.1 框架项目限制
服务器资源有限,React/Vue/Next.js 等框架项目构建会卡住整个后端。需要增加技术栈检测和拒绝机制:
javascript
// 部署前检测技术栈
const detectTechStack = (repoPath) => {
const packageJsonPath = path.join(repoPath, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
// 检测框架依赖
if (deps.react || deps.next || deps.vue || deps['@vue/cli-service']) {
return { type: 'framework', name: deps.next ? 'Next.js' : deps.react ? 'React' : 'Vue' };
}
}
return { type: 'static' };
};
// 拒绝框架项目
if (techStack.type === 'framework') {
return res.status(400).json({
type: 'error',
message: `不支持 ${techStack.name} 项目(服务器资源限制,仅支持静态 HTML/CSS/JS 项目)`
});
}
4.2 并发部署限制
防止多个部署同时执行导致服务器卡死:
javascript
// 部署锁机制
let isDeploying = false;
app.post('/api/deploy', async (req, res) => {
// 检查是否已有部署在进行中
if (isDeploying) {
return res.status(429).json({
type: 'error',
message: '当前已有部署在进行中,请稍后再试'
});
}
isDeploying = true;
try {
// 执行部署逻辑...
} finally {
isDeploying = false;
}
});
4.3 部署超时机制
防止构建过程无限期卡住:
javascript
const DEPLOY_TIMEOUT = 30000; // 30秒超时
const deployWithTimeout = (repoUrl, timeout) => {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('部署超时(30秒),已自动取消'));
}, timeout);
deploy(repoUrl)
.then(result => {
clearTimeout(timer);
resolve(result);
})
.catch(err => {
clearTimeout(timer);
reject(err);
});
});
};
验证结果
测试 1:静态项目部署 ✅
bash
curl -X POST https://qingyin.monster/api/deploy \
-H "Content-Type: application/json" \
-d '{"repo":"https://github.com/ruanyf/es6tutorial"}'
结果:{"type":"success","url":"https://qingyin.monster/deploy/es6tutorial/"}
测试 2:React 项目拒绝 ✅
bash
curl -X POST https://qingyin.monster/api/deploy \
-H "Content-Type: application/json" \
-d '{"repo":"https://github.com/facebook/react"}'
结果:{"type":"error","message":"不支持 React 项目(服务器资源限制)"}
测试 3:并发限制 ✅
同时发起两个部署请求,第二个返回:{"error":"当前已有部署在进行中,请稍后再试"}
关键教训
- Nginx 配置冲突的隐蔽性
多个配置文件监听同一域名时,Nginx 只会加载第一个(按文件名排序),其他配置被静默忽略。修改配置后一定要用 nginx -t 检查冲突警告。
- 后端必须做资源保护
小服务器部署平台必须限制:
- 技术栈:只支持纯静态项目,拒绝框架构建
- 并发数:单线程部署,防止资源耗尽
- 超时:30秒自动取消,防止进程挂死
- 代理路径的细节
proxy_pass http://localhost:3001; 和 proxy_pass http://localhost:3001/api/; 有本质区别:
- 前者:Nginx 把
/api/deploy原样转发到后端,后端收到的路径是/api/deploy - 后者:Nginx 把
/api/deploy替换为/api/deploy(注意/api/会替换掉匹配部分)
根据后端路由设计选择正确的代理路径。
最终架构图
bash
用户浏览器
↓ HTTPS
Nginx (qingyin.monster)
├── /deploy/* → 静态文件 (/var/www/html/deploy/)
└── /api/* → proxy_pass → Node.js (localhost:3001)
↓
部署控制器
↓
┌─────────┴─────────┐
↓ ↓
技术栈检测 并发锁检查
↓ ↓
框架项目?是 → 拒绝 正在部署?是 → 拒绝
↓ 否 ↓ 否
git clone + 静态文件复制
↓
返回部署 URL
后续优化方向
- Redis 队列:用 Redis 做部署任务队列,支持异步处理和状态查询
- GitHub OAuth:支持私有仓库部署
- 部署历史:持久化存储部署记录,支持回滚
- Webhook 自动部署:监听 GitHub push 事件自动触发部署
如果你也在做类似的小服务器部署平台,希望这篇踩坑记录能帮到你。
#DeployHub #Nginx #Node.js #部署平台 #故障排查 #全栈开发