serv00部署gpt-load && Passenger进程保活

0.前提,部署到make run那一步,还未进行pm2保活

https://linux.do/t/topic/835935

1
2
3
4
5
6
7
8
9
10
11
12
git clone https://github.com/tbphp/gpt-load.git
cd gpt-load
go mod tidy
# 创建配置
cp .env.example .env
# 登陆serv00开放端口
make run
# 会打印出启动成功日志

pm2保活(可选)
go build -o gpt-load-server .
pm2 start ./gpt-load-server --name gpt-load

1.开始,参考下面讲的passenger保活进程,

https://github.com/hkfires/Keep-Serv00-Alive

2.app.js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
const express = require("express");
const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");
const app = express();

// ================== 配置区域 ==================
const port = process.env.PORT || 3000;
const USER = "username";
const TARGET_PORT = "30345";
const WORK_DIR = `/home/${USER}/gpt-load`;
const CMD = `./gpt-load-server`;
const PROCESS_NAME = "gpt-load-server";

// 日志文件路径 (在 public_nodejs 目录下)
const LOG_FILE = path.join(__dirname, 'keeper.log');

// 🛑 新增:暂停开关文件路径 (只要这个文件存在,脚本就不干活)
const STOP_FILE = path.join(__dirname, 'stop.txt');
// ============================================

// 自定义日志函数:同时输出到控制台和文件
function log(message) {
const time = new Date().toLocaleString();
const logMsg = `[${time}] ${message}\n`;

// 1. 尝试写入文件
try {
fs.appendFileSync(LOG_FILE, logMsg);
} catch (e) {
// 如果写文件失败,至少打印到 stderr
console.error(`Log write error: ${e.message}`);
}

// 2. 输出到控制台 (保留标准输出)
console.log(logMsg.trim());
}

function keepAlive() {
// ============================================================
// 🛑 新增:维护模式检测
// 如果目录下存在 stop.txt,直接跳过后续所有检查
if (fs.existsSync(STOP_FILE)) {
// 为了防止日志刷屏,这里可以选择不打印,或者每次跳过时打印
// 这里设置为每次周期都记录一下,方便确认状态
log("🛑 Maintenance Mode Active (stop.txt found). Skipping checks.");
return;
}
// ============================================================

// 1. 核心检测:查端口 (保持原有逻辑)
exec(`sockstat -4 -l -P tcp | grep ${TARGET_PORT}`, (err, stdout) => {
if (stdout && stdout.includes(TARGET_PORT)) {
log(`Port ${TARGET_PORT} is UP. Service is healthy.`);
} else {
log(`Port ${TARGET_PORT} is DOWN. Initiating restart sequence...`);

// 2. 清理旧进程
log(`Cleaning up any stuck '${PROCESS_NAME}' processes...`);
exec(`pkill -f "${PROCESS_NAME}"`, () => {

// 3. 启动新进程
log(`Starting new instance...`);
// 注意:gpt-load 自身的日志依然写到它自己的 gpt-load.log 里
exec(`cd ${WORK_DIR} && ${CMD} > gpt-load.log 2>&1 &`, (startErr) => {
if (startErr) log(`Start failed: ${startErr}`);
else log(`Start command sent successfully.`);
});
});
}
});
}

// 启动时立即检查
log("Keeper Service Started.");
keepAlive();

// 每 60 秒检查一次
setInterval(keepAlive, 60 * 1000);

// 修改:Web 访问时也能看到状态
app.get("/", (req, res) => {
if (fs.existsSync(STOP_FILE)) {
res.send("🛑 Service is in MAINTENANCE MODE (stop.txt detected).");
} else {
res.send(`✅ Keeper is watching port ${TARGET_PORT}`);
}
});

app.listen(port, () => log(`Keeper listening on port ${port}`));

部分网路节点存在非标准端口阻断的问题,导致访问502
新的app.js,转发端口,来实现纯域名访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
const express = require("express");
const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");
const { createProxyMiddleware } = require("http-proxy-middleware"); // 引入代理插件
const app = express();

// ================== 配置区域 ==================
const port = process.env.PORT || 3000;
const USER = "username";
const TARGET_PORT = "30345";
const WORK_DIR = `/home/${USER}/gpt-load`;
const CMD = `./gpt-load-server`;
const PROCESS_NAME = "gpt-load-server";

// 日志文件
const LOG_FILE = path.join(__dirname, 'keeper.log');
// 暂停开关
const STOP_FILE = path.join(__dirname, 'stop.txt');
// ============================================

function log(message) {
const time = new Date().toLocaleString();
const logMsg = `[${time}] ${message}\n`;
try { fs.appendFileSync(LOG_FILE, logMsg); } catch (e) {}
console.log(logMsg.trim());
}

function keepAlive() {
if (fs.existsSync(STOP_FILE)) {
log("🛑 Maintenance Mode Active (stop.txt found). Skipping checks.");
return;
}

exec(`sockstat -4 -l -P tcp | grep ${TARGET_PORT}`, (err, stdout) => {
if (stdout && stdout.includes(TARGET_PORT)) {
log(`Port ${TARGET_PORT} is UP. Service is healthy.`);
} else {
log(`Port ${TARGET_PORT} is DOWN. Initiating restart sequence...`);
log(`Cleaning up any stuck '${PROCESS_NAME}' processes...`);
exec(`pkill -f "${PROCESS_NAME}"`, () => {
log(`Starting new instance...`);
exec(`cd ${WORK_DIR} && ${CMD} > gpt-load.log 2>&1 &`, (startErr) => {
if (startErr) log(`Start failed: ${startErr}`);
else log(`Start command sent successfully.`);
});
});
}
});
}

// 启动保活逻辑
log("Keeper Service Started.");
keepAlive();
setInterval(keepAlive, 60 * 1000);

// ================== 路由配置 ==================

// 1. 特殊页面:查看保活状态 (访问 /keeper-status)
app.get("/keeper-status", (req, res) => {
if (fs.existsSync(STOP_FILE)) {
res.send("🛑 Service is in MAINTENANCE MODE (stop.txt detected).");
} else {
res.send(`✅ Keeper is watching port ${TARGET_PORT}. Service acts as Reverse Proxy.`);
}
});

// 2. 反向代理:把所有其他请求转发给 gpt-load (30345)
// 这样你直接访问域名,就等于访问了 30345,但不用担心代理节点屏蔽端口
app.use("/", createProxyMiddleware({
target: `http://127.0.0.1:${TARGET_PORT}`,
changeOrigin: true,
ws: true, // 支持 WebSocket (如果 gpt-load 需要)
onError: (err, req, res) => {
res.status(502).send(`
<h1>502 Bad Gateway</h1>
<p>Keeper is running, but gpt-load-server (Port ${TARGET_PORT}) seems down or starting.</p>
<p>Check <a href="/keeper-status">/keeper-status</a> for details.</p>
`);
}
}));

app.listen(port, () => log(`Keeper & Proxy listening on port ${port}`));

3.启动程序(要以绝对路径启动,不然……app.js没办法重启gpt-load-server)

启动gpt-load-server

1
2
cd /home/username/gpt-load
nohup ./gpt-load-server > gpt-load.log 2>&1 &

启动app.js(在serv00网站restart就行,会自动生成keeper.log)

```
cd /home/username/domains/username.serv00.net/public_nodejs
nohup node app.js > app.log 2>&1 &
```

4.验证

手动杀掉gpt-load-server

1
pkill -f gpt-load-server

在public_nodejs目录

1
2
[id@s9]:<~/domains/id.serv00.net/public_nodejs>$ pkill -f gpt-load-server
[id@s9]:<~/domains/id.serv00.net/public_nodejs>$ tail -f keeper.log

查看app.log keeper.log

1
tail -f keeper.log

显示下面则成功:
image|690x236

5.暂停保活(开启维护模式):

进入app.js所在目录创建 stop.txt 文件:

1
touch stop.txt

恢复保活(关闭维护模式):

删除 stop.txt 文件:

1
rm stop.txt

PS:最简单的还是pm2保活,官方也没看到说禁止,说的是最好用passenger,访问我用的域名+端口,可以自己试试passenger反代去掉端口