最后更新于:2026年07月

⚠️ 法律声明:本文仅从技术架构与运维角度探讨自建代理服务的工程实现,不构成任何商业建议。在中国大陆,未经许可经营 VPN 业务属于违法行为。请读者自行了解并遵守所在国家/地区的法律法规,本文内容仅供技术学习与研究参考。
当你已经掌握了 VPS 部署、协议配置、集群管理之后,下一步可能面临的工程挑战是:如何用一套统一的系统来管理多个节点、多个用户、流量计费和服务质量监控。这就是「代理服务运营」要解决的技术问题。
本文将从纯技术角度,探讨自建代理服务的架构设计、面板选型、用户管理系统、流量计费实现、监控告警体系,以及相关的工程思考。
🧭 技术架构总览
自建代理服务的技术挑战
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 自建代理服务的技术挑战 │
│ │
│ 单节点使用(个人): │
│ - 一台 VPS + xray/sing-box │
│ - 手动配置 │
│ - 无需用户管理 │
│ - 简单够用 │
│ │
│ 多节点运营(团队/社区): │
│ - 多台 VPS 分布在不同地域 │
│ - 需要统一管理面板 │
│ - 需要用户管理系统(注册/登录/订阅) │
│ - 需要流量计费(按量/按时) │
│ - 需要节点健康检查 │
│ - 需要自动化运维 │
│ - 需要财务与订单系统 │
│ - 需要客服与工单系统 │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 自建代理服务的技术挑战 │
│ │
│ 单节点使用(个人): │
│ - 一台 VPS + xray/sing-box │
│ - 手动配置 │
│ - 无需用户管理 │
│ - 简单够用 │
│ │
│ 多节点运营(团队/社区): │
│ - 多台 VPS 分布在不同地域 │
│ - 需要统一管理面板 │
│ - 需要用户管理系统(注册/登录/订阅) │
│ - 需要流量计费(按量/按时) │
│ - 需要节点健康检查 │
│ - 需要自动化运维 │
│ - 需要财务与订单系统 │
│ - 需要客服与工单系统 │
└──────────────────────────────────────────────────────────┘
系统架构图
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 自建代理服务系统架构 │
│ │
│ 用户端 │
│ ├── Web 面板(注册/登录/订阅/工单) │
│ ├── 客户端 App(Clash/v2rayN/Shadowrocket) │
│ └── 订阅链接自动更新 │
│ │
│ 前端层 │
│ ├── Nginx 反向代理(TLS 终止) │
│ ├── Web 面板(PHP/Laravel 或 Go) │
│ └── API 接口 │
│ │
│ 后端层 │
│ ├── 用户管理系统 │
│ ├── 订单与支付系统 │
│ ├── 流量计费系统 │
│ ├── 节点管理系统 │
│ └── 工单系统 │
│ │
│ 数据层 │
│ ├── MySQL(用户/订单数据) │
│ ├── Redis(缓存/会话) │
│ └── InfluxDB(流量统计) │
│ │
│ 节点层 │
│ ├── 节点 A(日本)- xray + 节点端程序 │
│ ├── 节点 B(美国)- xray + 节点端程序 │
│ └── 节点 C(新加坡)- xray + 节点端程序 │
│ │
│ 监控层 │
│ ├── Prometheus + Grafana │
│ ├── 告警系统(Telegram/邮件) │
│ └── 日志收集(ELK) │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 自建代理服务系统架构 │
│ │
│ 用户端 │
│ ├── Web 面板(注册/登录/订阅/工单) │
│ ├── 客户端 App(Clash/v2rayN/Shadowrocket) │
│ └── 订阅链接自动更新 │
│ │
│ 前端层 │
│ ├── Nginx 反向代理(TLS 终止) │
│ ├── Web 面板(PHP/Laravel 或 Go) │
│ └── API 接口 │
│ │
│ 后端层 │
│ ├── 用户管理系统 │
│ ├── 订单与支付系统 │
│ ├── 流量计费系统 │
│ ├── 节点管理系统 │
│ └── 工单系统 │
│ │
│ 数据层 │
│ ├── MySQL(用户/订单数据) │
│ ├── Redis(缓存/会话) │
│ └── InfluxDB(流量统计) │
│ │
│ 节点层 │
│ ├── 节点 A(日本)- xray + 节点端程序 │
│ ├── 节点 B(美国)- xray + 节点端程序 │
│ └── 节点 C(新加坡)- xray + 节点端程序 │
│ │
│ 监控层 │
│ ├── Prometheus + Grafana │
│ ├── 告警系统(Telegram/邮件) │
│ └── 日志收集(ELK) │
└──────────────────────────────────────────────────────────┘
📦 面板系统选型
主流开源面板对比
| 面板 | 语言 | 功能丰富度 | 维护活跃度 | 推荐指数 |
|---|---|---|---|---|
| SSPanel-UIM | PHP | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| V2board | PHP/Laravel | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Xboard | PHP/Laravel | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| WHMCS + 模块 | PHP | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
推荐:V2board(Laravel 框架,功能完善,社区活跃)
V2board 部署
环境要求:
PLAINTEXT
- PHP 8.1+
- MySQL 5.7+ / MariaDB 10.3+
- Redis 5.0+
- Nginx / Apache
- Composer- PHP 8.1+
- MySQL 5.7+ / MariaDB 10.3+
- Redis 5.0+
- Nginx / Apache
- Composer
安装步骤:
BASH
# 1. 安装 LNMP 环境
# 使用 1Panel 或宝塔面板简化部署
# 2. 克隆 V2board
cd /www/wwwroot
git clone https://github.com/v2board/v2board.git
cd v2board
# 3. 安装依赖
composer install --optimize-autoloader --no-dev
# 4. 配置环境
cp .env.example .env
php artisan key:generate
# 5. 编辑 .env
cat > .env << 'EOF'
APP_NAME=V2board
APP_ENV=local
APP_KEY=
APP_DEBUG=false
APP_URL=https://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=v2board
DB_USERNAME=v2board
DB_PASSWORD=your-password
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
EOF
# 6. 初始化数据库
php artisan v2board:install
# 7. 配置定时任务
crontab -e
# 添加:
* * * * * php /www/wwwroot/v2board/artisan schedule:run >> /dev/null 2>&1
# 8. 配置队列
php artisan queue:work --daemon
# 或使用 Supervisor
cat > /etc/supervisor/conf.d/v2board.conf << 'EOF'
[program:v2board-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /www/wwwroot/v2board/artisan queue:work --daemon
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/www/wwwroot/v2board/storage/logs/queue.log
EOF
supervisorctl update
supervisorctl start v2board-queue:*# 1. 安装 LNMP 环境
# 使用 1Panel 或宝塔面板简化部署
# 2. 克隆 V2board
cd /www/wwwroot
git clone https://github.com/v2board/v2board.git
cd v2board
# 3. 安装依赖
composer install --optimize-autoloader --no-dev
# 4. 配置环境
cp .env.example .env
php artisan key:generate
# 5. 编辑 .env
cat > .env << 'EOF'
APP_NAME=V2board
APP_ENV=local
APP_KEY=
APP_DEBUG=false
APP_URL=https://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=v2board
DB_USERNAME=v2board
DB_PASSWORD=your-password
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
EOF
# 6. 初始化数据库
php artisan v2board:install
# 7. 配置定时任务
crontab -e
# 添加:
* * * * * php /www/wwwroot/v2board/artisan schedule:run >> /dev/null 2>&1
# 8. 配置队列
php artisan queue:work --daemon
# 或使用 Supervisor
cat > /etc/supervisor/conf.d/v2board.conf << 'EOF'
[program:v2board-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /www/wwwroot/v2board/artisan queue:work --daemon
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/www/wwwroot/v2board/storage/logs/queue.log
EOF
supervisorctl update
supervisorctl start v2board-queue:*
Nginx 配置:
NGINX
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
root /www/wwwroot/v2board/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# 订阅链接
location /api/v1/client/subscribe {
try_files $uri $uri/ /index.php?$query_string;
# 禁止缓存
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
root /www/wwwroot/v2board/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# 订阅链接
location /api/v1/client/subscribe {
try_files $uri $uri/ /index.php?$query_string;
# 禁止缓存
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
👥 用户管理系统
用户数据模型
SQL
-- 用户表
CREATE TABLE v2_user (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
password_algo VARCHAR(10),
balance INT DEFAULT 0, -- 余额(分)
discount INT DEFAULT 0, -- 折扣
commission_type INT DEFAULT 0,
commission_rate INT DEFAULT 0,
commission_balance INT DEFAULT 0,
t INT DEFAULT 0, -- 传输协议
u BIGINT DEFAULT 0, -- 上行流量
d BIGINT DEFAULT 0, -- 下行流量
transfer_enable BIGINT DEFAULT 0, -- 流量上限
expired_at TIMESTAMP NULL,
banned INT DEFAULT 0,
plan_id INT DEFAULT NULL,
speed_limit INT DEFAULT NULL,
invite_user_id INT DEFAULT NULL,
uuid VARCHAR(255) UNIQUE,
token VARCHAR(255) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 用户表
CREATE TABLE v2_user (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
password_algo VARCHAR(10),
balance INT DEFAULT 0, -- 余额(分)
discount INT DEFAULT 0, -- 折扣
commission_type INT DEFAULT 0,
commission_rate INT DEFAULT 0,
commission_balance INT DEFAULT 0,
t INT DEFAULT 0, -- 传输协议
u BIGINT DEFAULT 0, -- 上行流量
d BIGINT DEFAULT 0, -- 下行流量
transfer_enable BIGINT DEFAULT 0, -- 流量上限
expired_at TIMESTAMP NULL,
banned INT DEFAULT 0,
plan_id INT DEFAULT NULL,
speed_limit INT DEFAULT NULL,
invite_user_id INT DEFAULT NULL,
uuid VARCHAR(255) UNIQUE,
token VARCHAR(255) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
用户认证流程
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 用户认证流程 │
│ │
│ 1. 注册 │
│ 用户 → Web 面板 → 填写邮箱/密码 │
│ → 邮箱验证 → 生成 UUID → 创建用户记录 │
│ │
│ 2. 登录 │
│ 用户 → Web 面板 → 邮箱+密码 │
│ → 验证 → 生成 Token → 返回会话 │
│ │
│ 3. 获取订阅 │
│ 用户 → 订阅链接(带 Token)→ 面板 API │
│ → 验证 Token → 查询用户套餐/节点 │
│ → 生成订阅配置 → 返回 YAML/Base64 │
│ │
│ 4. 节点连接 │
│ 客户端 → 节点(带 UUID)→ 节点端验证 │
│ → 查询用户流量上限 → 放行/拒绝 │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 用户认证流程 │
│ │
│ 1. 注册 │
│ 用户 → Web 面板 → 填写邮箱/密码 │
│ → 邮箱验证 → 生成 UUID → 创建用户记录 │
│ │
│ 2. 登录 │
│ 用户 → Web 面板 → 邮箱+密码 │
│ → 验证 → 生成 Token → 返回会话 │
│ │
│ 3. 获取订阅 │
│ 用户 → 订阅链接(带 Token)→ 面板 API │
│ → 验证 Token → 查询用户套餐/节点 │
│ → 生成订阅配置 → 返回 YAML/Base64 │
│ │
│ 4. 节点连接 │
│ 客户端 → 节点(带 UUID)→ 节点端验证 │
│ → 查询用户流量上限 → 放行/拒绝 │
└──────────────────────────────────────────────────────────┘
订阅链接生成
PHP
// V2board 订阅生成逻辑(简化版)
public function subscribe(Request $request)
{
$token = $request->input('token');
$user = User::where('token', $token)->first();
if (!$user) {
return response('Not Found', 404);
}
// 检查是否过期或超量
if ($user->expired_at < time() ||
($user->u + $user->d) >= $user->transfer_enable) {
return response('Expired', 403);
}
// 获取用户可用节点
$servers = Server::where('show', 1)->get();
// 生成 Clash 配置
$config = $this->generateClashConfig($user, $servers);
return response(base64_encode($config))
->header('Content-Type', 'text/plain');
}
private function generateClashConfig($user, $servers)
{
$proxies = [];
foreach ($servers as $server) {
$proxies[] = [
'name' => $server->name,
'type' => 'vless',
'server' => $server->host,
'port' => $server->port,
'uuid' => $user->uuid,
// ... 其他配置
];
}
return Yaml::dump([
'proxies' => $proxies,
'proxy-groups' => [
[
'name' => 'Auto',
'type' => 'url-test',
'proxies' => array_column($proxies, 'name'),
]
]
]);
}// V2board 订阅生成逻辑(简化版)
public function subscribe(Request $request)
{
$token = $request->input('token');
$user = User::where('token', $token)->first();
if (!$user) {
return response('Not Found', 404);
}
// 检查是否过期或超量
if ($user->expired_at < time() ||
($user->u + $user->d) >= $user->transfer_enable) {
return response('Expired', 403);
}
// 获取用户可用节点
$servers = Server::where('show', 1)->get();
// 生成 Clash 配置
$config = $this->generateClashConfig($user, $servers);
return response(base64_encode($config))
->header('Content-Type', 'text/plain');
}
private function generateClashConfig($user, $servers)
{
$proxies = [];
foreach ($servers as $server) {
$proxies[] = [
'name' => $server->name,
'type' => 'vless',
'server' => $server->host,
'port' => $server->port,
'uuid' => $user->uuid,
// ... 其他配置
];
}
return Yaml::dump([
'proxies' => $proxies,
'proxy-groups' => [
[
'name' => 'Auto',
'type' => 'url-test',
'proxies' => array_column($proxies, 'name'),
]
]
]);
}
📊 流量计费系统
流量统计原理
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 流量统计原理 │
│ │
│ 用户流量 → 节点 → xray stats API → 面板数据库 │
│ │
│ 流程: │
│ 1. xray 启用 stats 统计 │
│ 2. 节点端定时(每分钟)查询 stats API │
│ 3. 上报流量数据到面板 │
│ 4. 面板累加到用户 u/d 字段 │
│ 5. 检查是否超出上限 │
│ │
│ xray stats 配置: │
│ "stats": {}, │
│ "api": { │
│ "tag": "api", │
│ "services": ["StatsService"] │
│ } │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 流量统计原理 │
│ │
│ 用户流量 → 节点 → xray stats API → 面板数据库 │
│ │
│ 流程: │
│ 1. xray 启用 stats 统计 │
│ 2. 节点端定时(每分钟)查询 stats API │
│ 3. 上报流量数据到面板 │
│ 4. 面板累加到用户 u/d 字段 │
│ 5. 检查是否超出上限 │
│ │
│ xray stats 配置: │
│ "stats": {}, │
│ "api": { │
│ "tag": "api", │
│ "services": ["StatsService"] │
│ } │
└──────────────────────────────────────────────────────────┘
节点端流量上报
PYTHON
#!/usr/bin/env python3
# node-reporter.py - 节点流量上报脚本
import requests
import json
import time
# 配置
PANEL_URL = "https://your-panel.com"
NODE_ID = 1
NODE_TOKEN = "your-node-token"
XRAY_API = "127.0.0.1:10085"
def get_traffic_stats():
"""从 xray API 获取流量统计"""
# 通过 gRPC 或 HTTP API 获取
# 简化示例:使用 xray API HTTP 接口
response = requests.post(
f"http://{XRAY_API}/stats",
json={"pattern": "", "reset": False}
)
return response.json()
def upload_stats(stats):
"""上报到面板"""
response = requests.post(
f"{PANEL_URL}/api/v1/server/push",
headers={"Authorization": f"Bearer {NODE_TOKEN}"},
json={
"node_id": NODE_ID,
"stats": stats,
"timestamp": int(time.time())
}
)
return response.status_code == 200
def check_users_exceeded():
"""检查用户是否超量"""
response = requests.get(
f"{PANEL_URL}/api/v1/server/users",
headers={"Authorization": f"Bearer {NODE_TOKEN}"}
)
users = response.json()
for user in users:
total = user['u'] + user['d']
if total >= user['transfer_enable']:
# 禁用用户
disable_user(user['uuid'])
def disable_user(uuid):
"""通过 xray API 禁用用户"""
pass
if __name__ == '__main__':
while True:
stats = get_traffic_stats()
upload_stats(stats)
check_users_exceeded()
time.sleep(60) # 每分钟上报一次#!/usr/bin/env python3
# node-reporter.py - 节点流量上报脚本
import requests
import json
import time
# 配置
PANEL_URL = "https://your-panel.com"
NODE_ID = 1
NODE_TOKEN = "your-node-token"
XRAY_API = "127.0.0.1:10085"
def get_traffic_stats():
"""从 xray API 获取流量统计"""
# 通过 gRPC 或 HTTP API 获取
# 简化示例:使用 xray API HTTP 接口
response = requests.post(
f"http://{XRAY_API}/stats",
json={"pattern": "", "reset": False}
)
return response.json()
def upload_stats(stats):
"""上报到面板"""
response = requests.post(
f"{PANEL_URL}/api/v1/server/push",
headers={"Authorization": f"Bearer {NODE_TOKEN}"},
json={
"node_id": NODE_ID,
"stats": stats,
"timestamp": int(time.time())
}
)
return response.status_code == 200
def check_users_exceeded():
"""检查用户是否超量"""
response = requests.get(
f"{PANEL_URL}/api/v1/server/users",
headers={"Authorization": f"Bearer {NODE_TOKEN}"}
)
users = response.json()
for user in users:
total = user['u'] + user['d']
if total >= user['transfer_enable']:
# 禁用用户
disable_user(user['uuid'])
def disable_user(uuid):
"""通过 xray API 禁用用户"""
pass
if __name__ == '__main__':
while True:
stats = get_traffic_stats()
upload_stats(stats)
check_users_exceeded()
time.sleep(60) # 每分钟上报一次
计费模式
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 计费模式对比 │
│ │
│ 按量计费(流量): │
│ - 用户购买流量包(如 100GB) │
│ - 用完为止,不限制时间 │
│ - 适合轻度使用用户 │
│ - 实现:检查 u+d >= transfer_enable │
│ │
│ 按时计费(周期): │
│ - 用户购买时长(如月付/季付/年付) │
│ - 不限制流量 │
│ - 适合重度使用用户 │
│ - 实现:检查 expired_at < now() │
│ │
│ 混合计费(推荐): │
│ - 周期内有流量上限 │
│ - 如:月付 50GB │
│ - 到期或超量都停止 │
│ - 实现:同时检查时间和流量 │
│ │
│ 按速计费(高端): │
│ - 限制最大速度(如 100Mbps) │
│ - 不限流量 │
│ - 实现:xray speed_limit 配置 │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 计费模式对比 │
│ │
│ 按量计费(流量): │
│ - 用户购买流量包(如 100GB) │
│ - 用完为止,不限制时间 │
│ - 适合轻度使用用户 │
│ - 实现:检查 u+d >= transfer_enable │
│ │
│ 按时计费(周期): │
│ - 用户购买时长(如月付/季付/年付) │
│ - 不限制流量 │
│ - 适合重度使用用户 │
│ - 实现:检查 expired_at < now() │
│ │
│ 混合计费(推荐): │
│ - 周期内有流量上限 │
│ - 如:月付 50GB │
│ - 到期或超量都停止 │
│ - 实现:同时检查时间和流量 │
│ │
│ 按速计费(高端): │
│ - 限制最大速度(如 100Mbps) │
│ - 不限流量 │
│ - 实现:xray speed_limit 配置 │
└──────────────────────────────────────────────────────────┘
🎯 节点管理系统
节点端部署
BASH
# V2board 节点端程序部署
# 使用 Docker
docker run -d --name v2board-node \
--restart unless-stopped \
-e NODE_ID=1 \
-e NODE_TOKEN="your-node-token" \
-e PANEL_URL="https://your-panel.com" \
-p 443:443 \
-v /etc/v2board:/etc/v2board \
v2board/node:latest
# 或使用 PM2
npm install -g pm2
pm2 start node-server.js --name v2board-node
pm2 save
pm2 startup# V2board 节点端程序部署
# 使用 Docker
docker run -d --name v2board-node \
--restart unless-stopped \
-e NODE_ID=1 \
-e NODE_TOKEN="your-node-token" \
-e PANEL_URL="https://your-panel.com" \
-p 443:443 \
-v /etc/v2board:/etc/v2board \
v2board/node:latest
# 或使用 PM2
npm install -g pm2
pm2 start node-server.js --name v2board-node
pm2 save
pm2 startup
节点健康检查
PYTHON
#!/usr/bin/env python3
# health-check.py - 节点健康检查
import requests
import subprocess
import json
from datetime import datetime
class NodeHealthCheck:
def __init__(self):
self.checks = [
self.check_cpu,
self.check_memory,
self.check_disk,
self.check_network,
self.check_xray,
self.check_connectivity
]
def check_cpu(self):
"""检查 CPU 使用率"""
result = subprocess.run(
['top', '-bn1'],
capture_output=True, text=True
)
# 解析 CPU 使用率
cpu_line = [l for l in result.stdout.split('\n') if 'Cpu(s)' in l][0]
cpu_usage = float(cpu_line.split(',')[0].split()[-1])
return {
'name': 'cpu',
'status': 'ok' if cpu_usage < 80 else 'warning',
'value': f'{cpu_usage:.1f}%'
}
def check_memory(self):
"""检查内存使用率"""
result = subprocess.run(
['free', '-m'],
capture_output=True, text=True
)
lines = result.stdout.split('\n')
mem_line = lines[1].split()
total = int(mem_line[1])
used = int(mem_line[2])
usage = (used / total) * 100
return {
'name': 'memory',
'status': 'ok' if usage < 80 else 'warning',
'value': f'{usage:.1f}%'
}
def check_xray(self):
"""检查 xray 进程"""
result = subprocess.run(
['pgrep', '-x', 'xray'],
capture_output=True
)
return {
'name': 'xray',
'status': 'ok' if result.returncode == 0 else 'critical',
'value': 'running' if result.returncode == 0 else 'stopped'
}
def check_connectivity(self):
"""检查外网连通性"""
try:
response = requests.get(
'https://www.google.com/generate_204',
timeout=5
)
return {
'name': 'connectivity',
'status': 'ok' if response.status_code == 204 else 'warning',
'value': 'connected'
}
except:
return {
'name': 'connectivity',
'status': 'critical',
'value': 'disconnected'
}
def run_all(self):
results = []
for check in self.checks:
try:
results.append(check())
except Exception as e:
results.append({
'name': check.__name__,
'status': 'error',
'value': str(e)
})
return {
'timestamp': datetime.now().isoformat(),
'checks': results
}
if __name__ == '__main__':
checker = NodeHealthCheck()
report = checker.run_all()
print(json.dumps(report, indent=2))#!/usr/bin/env python3
# health-check.py - 节点健康检查
import requests
import subprocess
import json
from datetime import datetime
class NodeHealthCheck:
def __init__(self):
self.checks = [
self.check_cpu,
self.check_memory,
self.check_disk,
self.check_network,
self.check_xray,
self.check_connectivity
]
def check_cpu(self):
"""检查 CPU 使用率"""
result = subprocess.run(
['top', '-bn1'],
capture_output=True, text=True
)
# 解析 CPU 使用率
cpu_line = [l for l in result.stdout.split('\n') if 'Cpu(s)' in l][0]
cpu_usage = float(cpu_line.split(',')[0].split()[-1])
return {
'name': 'cpu',
'status': 'ok' if cpu_usage < 80 else 'warning',
'value': f'{cpu_usage:.1f}%'
}
def check_memory(self):
"""检查内存使用率"""
result = subprocess.run(
['free', '-m'],
capture_output=True, text=True
)
lines = result.stdout.split('\n')
mem_line = lines[1].split()
total = int(mem_line[1])
used = int(mem_line[2])
usage = (used / total) * 100
return {
'name': 'memory',
'status': 'ok' if usage < 80 else 'warning',
'value': f'{usage:.1f}%'
}
def check_xray(self):
"""检查 xray 进程"""
result = subprocess.run(
['pgrep', '-x', 'xray'],
capture_output=True
)
return {
'name': 'xray',
'status': 'ok' if result.returncode == 0 else 'critical',
'value': 'running' if result.returncode == 0 else 'stopped'
}
def check_connectivity(self):
"""检查外网连通性"""
try:
response = requests.get(
'https://www.google.com/generate_204',
timeout=5
)
return {
'name': 'connectivity',
'status': 'ok' if response.status_code == 204 else 'warning',
'value': 'connected'
}
except:
return {
'name': 'connectivity',
'status': 'critical',
'value': 'disconnected'
}
def run_all(self):
results = []
for check in self.checks:
try:
results.append(check())
except Exception as e:
results.append({
'name': check.__name__,
'status': 'error',
'value': str(e)
})
return {
'timestamp': datetime.now().isoformat(),
'checks': results
}
if __name__ == '__main__':
checker = NodeHealthCheck()
report = checker.run_all()
print(json.dumps(report, indent=2))
节点自动扩缩容
YAML
# Kubernetes HPA 配置(参考 K8s 文章)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: proxy-node-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: proxy-node
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70# Kubernetes HPA 配置(参考 K8s 文章)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: proxy-node-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: proxy-node
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
💳 订单与支付系统
订单流程
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 订单处理流程 │
│ │
│ 1. 用户选择套餐 │
│ → Web 面板 → 套餐列表 → 选择 → 创建订单 │
│ │
│ 2. 支付 │
│ → 选择支付方式(支付宝/微信/PayPal/加密货币) │
│ → 跳转支付 → 回调通知 │
│ │
│ 3. 订单完成 │
│ → 更新订单状态 │
│ → 更新用户套餐/流量/到期时间 │
│ → 发送通知(邮件/Telegram) │
│ │
│ 4. 售后 │
│ → 工单系统 │
│ → 退款处理 │
│ → 套餐变更 │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 订单处理流程 │
│ │
│ 1. 用户选择套餐 │
│ → Web 面板 → 套餐列表 → 选择 → 创建订单 │
│ │
│ 2. 支付 │
│ → 选择支付方式(支付宝/微信/PayPal/加密货币) │
│ → 跳转支付 → 回调通知 │
│ │
│ 3. 订单完成 │
│ → 更新订单状态 │
│ → 更新用户套餐/流量/到期时间 │
│ → 发送通知(邮件/Telegram) │
│ │
│ 4. 售后 │
│ → 工单系统 │
│ → 退款处理 │
│ → 套餐变更 │
└──────────────────────────────────────────────────────────┘
支付接口集成
PHP
// V2board 支付集成示例(简化)
class PaymentService
{
public function pay($order, $method)
{
switch ($method) {
case 'stripe':
return $this->stripePay($order);
case 'paypal':
return $this->paypalPay($order);
case 'crypto':
return $this->cryptoPay($order);
default:
throw new \Exception('Unsupported payment method');
}
}
public function callback($method, $data)
{
// 验证回调签名
$order = $this->verifyCallback($method, $data);
if ($order && $order->status === 'pending') {
// 更新订单状态
$order->status = 'paid';
$order->paid_at = now();
$order->save();
// 更新用户套餐
$this->activatePlan($order);
// 发送通知
$this->notify($order);
}
}
private function activatePlan($order)
{
$user = $order->user;
$plan = $order->plan;
$user->plan_id = $plan->id;
$user->transfer_enable = $plan->transfer_enable;
$user->expired_at = now()->addDays($plan->period);
$user->u = 0;
$user->d = 0;
$user->save();
}
}// V2board 支付集成示例(简化)
class PaymentService
{
public function pay($order, $method)
{
switch ($method) {
case 'stripe':
return $this->stripePay($order);
case 'paypal':
return $this->paypalPay($order);
case 'crypto':
return $this->cryptoPay($order);
default:
throw new \Exception('Unsupported payment method');
}
}
public function callback($method, $data)
{
// 验证回调签名
$order = $this->verifyCallback($method, $data);
if ($order && $order->status === 'pending') {
// 更新订单状态
$order->status = 'paid';
$order->paid_at = now();
$order->save();
// 更新用户套餐
$this->activatePlan($order);
// 发送通知
$this->notify($order);
}
}
private function activatePlan($order)
{
$user = $order->user;
$plan = $order->plan;
$user->plan_id = $plan->id;
$user->transfer_enable = $plan->transfer_enable;
$user->expired_at = now()->addDays($plan->period);
$user->u = 0;
$user->d = 0;
$user->save();
}
}
📈 监控与告警
监控指标
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 监控指标体系 │
│ │
│ 业务指标: │
│ ├── 注册用户数 │
│ ├── 活跃用户数(DAU/MAU) │
│ ├── 付费用户数 │
│ ├── 订单数量/金额 │
│ ├── 流量消耗 │
│ └── 用户留存率 │
│ │
│ 节点指标: │
│ ├── CPU/内存/磁盘使用率 │
│ ├── 网络带宽(入站/出站) │
│ ├── 连接数 │
│ ├── 节点延迟 │
│ ├── 节点可用性(SLA) │
│ └── 流量分布 │
│ │
│ 系统指标: │
│ ├── 面板响应时间 │
│ ├── API 错误率 │
│ ├── 数据库性能 │
│ ├── 队列积压 │
│ └── 告警触发次数 │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 监控指标体系 │
│ │
│ 业务指标: │
│ ├── 注册用户数 │
│ ├── 活跃用户数(DAU/MAU) │
│ ├── 付费用户数 │
│ ├── 订单数量/金额 │
│ ├── 流量消耗 │
│ └── 用户留存率 │
│ │
│ 节点指标: │
│ ├── CPU/内存/磁盘使用率 │
│ ├── 网络带宽(入站/出站) │
│ ├── 连接数 │
│ ├── 节点延迟 │
│ ├── 节点可用性(SLA) │
│ └── 流量分布 │
│ │
│ 系统指标: │
│ ├── 面板响应时间 │
│ ├── API 错误率 │
│ ├── 数据库性能 │
│ ├── 队列积压 │
│ └── 告警触发次数 │
└──────────────────────────────────────────────────────────┘
告警规则
YAML
# Prometheus 告警规则
groups:
- name: proxy-service
rules:
# 节点离线告警
- alert: NodeOffline
expr: up{job="proxy-node"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.instance }} 离线"
description: "节点已离线超过 1 分钟"
# CPU 使用率过高
- alert: HighCPU
expr: 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "CPU 使用率过高"
# 流量异常
- alert: TrafficAnomaly
expr: rate(node_network_receive_bytes_total[5m]) > 100000000
for: 10m
labels:
severity: warning
annotations:
summary: "流量异常(超过 100MB/s 持续 10 分钟)"
# 用户流量超限
- alert: UserTrafficExceeded
expr: v2board_user_traffic_ratio > 0.95
for: 1m
labels:
severity: info
annotations:
summary: "用户流量使用超过 95%"# Prometheus 告警规则
groups:
- name: proxy-service
rules:
# 节点离线告警
- alert: NodeOffline
expr: up{job="proxy-node"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.instance }} 离线"
description: "节点已离线超过 1 分钟"
# CPU 使用率过高
- alert: HighCPU
expr: 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "CPU 使用率过高"
# 流量异常
- alert: TrafficAnomaly
expr: rate(node_network_receive_bytes_total[5m]) > 100000000
for: 10m
labels:
severity: warning
annotations:
summary: "流量异常(超过 100MB/s 持续 10 分钟)"
# 用户流量超限
- alert: UserTrafficExceeded
expr: v2board_user_traffic_ratio > 0.95
for: 1m
labels:
severity: info
annotations:
summary: "用户流量使用超过 95%"
Telegram 告警机器人
PYTHON
#!/usr/bin/env python3
# alert-bot.py - Telegram 告警机器人
import requests
import json
BOT_TOKEN = "your-bot-token"
CHAT_ID = "your-chat-id"
def send_alert(title, message, severity="info"):
"""发送 Telegram 告警"""
emoji = {
'critical': '🔴',
'warning': '🟡',
'info': '🔵'
}
text = f"""
{emoji.get(severity, '⚪')} **{title}**
{message}
时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
""".strip()
requests.post(
f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage",
json={
"chat_id": CHAT_ID,
"text": text,
"parse_mode": "Markdown"
}
)
# AlertManager Webhook
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
data = request.json
for alert in data.get('alerts', []):
send_alert(
title=alert['labels']['alertname'],
message=alert['annotations']['description'],
severity=alert['labels']['severity']
)
return '', 200
if __name__ == '__main__':
app.run(port=5000)#!/usr/bin/env python3
# alert-bot.py - Telegram 告警机器人
import requests
import json
BOT_TOKEN = "your-bot-token"
CHAT_ID = "your-chat-id"
def send_alert(title, message, severity="info"):
"""发送 Telegram 告警"""
emoji = {
'critical': '🔴',
'warning': '🟡',
'info': '🔵'
}
text = f"""
{emoji.get(severity, '⚪')} **{title}**
{message}
时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
""".strip()
requests.post(
f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage",
json={
"chat_id": CHAT_ID,
"text": text,
"parse_mode": "Markdown"
}
)
# AlertManager Webhook
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
data = request.json
for alert in data.get('alerts', []):
send_alert(
title=alert['labels']['alertname'],
message=alert['annotations']['description'],
severity=alert['labels']['severity']
)
return '', 200
if __name__ == '__main__':
app.run(port=5000)
🔒 安全与合规
安全最佳实践
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 安全最佳实践 │
│ │
│ 面板安全: │
│ ├── 强制 HTTPS │
│ ├── 定期更换密码 │
│ ├── 二次验证(2FA) │
│ ├── 限制登录 IP │
│ ├── 防暴力破解(Fail2ban) │
│ └── 定期备份 │
│ │
│ 节点安全: │
│ ├── 节点端 Token 认证 │
│ ├── 限制面板 API 访问 IP │
│ ├── 节点间通信加密 │
│ ├── 防火墙规则 │
│ └── 日志审计 │
│ │
│ 数据安全: │
│ ├── 数据库加密 │
│ ├── 敏感字段加密 │
│ ├── 定期备份(异地) │
│ ├── 不记录用户访问日志 │
│ └── 遵守隐私法规 │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 安全最佳实践 │
│ │
│ 面板安全: │
│ ├── 强制 HTTPS │
│ ├── 定期更换密码 │
│ ├── 二次验证(2FA) │
│ ├── 限制登录 IP │
│ ├── 防暴力破解(Fail2ban) │
│ └── 定期备份 │
│ │
│ 节点安全: │
│ ├── 节点端 Token 认证 │
│ ├── 限制面板 API 访问 IP │
│ ├── 节点间通信加密 │
│ ├── 防火墙规则 │
│ └── 日志审计 │
│ │
│ 数据安全: │
│ ├── 数据库加密 │
│ ├── 敏感字段加密 │
│ ├── 定期备份(异地) │
│ ├── 不记录用户访问日志 │
│ └── 遵守隐私法规 │
└──────────────────────────────────────────────────────────┘
合规注意事项
PLAINTEXT
⚠️ 重要法律提醒:
1. 中国大陆法律
- 未经许可经营 VPN 业务属于违法行为
- 《网络安全法》对网络服务有严格要求
- 个人自用需遵守相关规定
2. 服务器所在国法律
- 不同国家对代理服务有不同规定
- 需要了解当地 DMCA、数据保留法规
- 部分国家禁止特定内容访问
3. 用户隐私保护
- GDPR(欧盟)
- CCPA(加州)
- PIPL(中国个人信息保护法)
4. 建议
- 仅用于个人/团队内部使用
- 不对外提供商业服务
- 了解并遵守当地法律法规
- 保护用户隐私数据⚠️ 重要法律提醒:
1. 中国大陆法律
- 未经许可经营 VPN 业务属于违法行为
- 《网络安全法》对网络服务有严格要求
- 个人自用需遵守相关规定
2. 服务器所在国法律
- 不同国家对代理服务有不同规定
- 需要了解当地 DMCA、数据保留法规
- 部分国家禁止特定内容访问
3. 用户隐私保护
- GDPR(欧盟)
- CCPA(加州)
- PIPL(中国个人信息保护法)
4. 建议
- 仅用于个人/团队内部使用
- 不对外提供商业服务
- 了解并遵守当地法律法规
- 保护用户隐私数据
💡 工程思考
技术选型建议
PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│ 技术选型建议 │
│ │
│ 面板选择: │
│ ├── 个人/小团队:V2board(功能全,易用) │
│ ├── 极客/开发者:自研(Go + React) │
│ └── 企业级:WHMCS + 定制模块 │
│ │
│ 节点管理: │
│ ├── 少量节点:手动部署 + 脚本 │
│ ├── 中等规模:Ansible 批量管理 │
│ └── 大规模:Kubernetes 集群 │
│ │
│ 数据库: │
│ ├── 用户数据:MySQL │
│ ├── 缓存:Redis │
│ ├── 流量统计:InfluxDB / TimescaleDB │
│ └── 日志:Elasticsearch │
│ │
│ 监控: │
│ ├── 指标:Prometheus + Grafana │
│ ├── 日志:Loki / ELK │
│ └── 告警:AlertManager + Telegram │
└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐
│ 技术选型建议 │
│ │
│ 面板选择: │
│ ├── 个人/小团队:V2board(功能全,易用) │
│ ├── 极客/开发者:自研(Go + React) │
│ └── 企业级:WHMCS + 定制模块 │
│ │
│ 节点管理: │
│ ├── 少量节点:手动部署 + 脚本 │
│ ├── 中等规模:Ansible 批量管理 │
│ └── 大规模:Kubernetes 集群 │
│ │
│ 数据库: │
│ ├── 用户数据:MySQL │
│ ├── 缓存:Redis │
│ ├── 流量统计:InfluxDB / TimescaleDB │
│ └── 日志:Elasticsearch │
│ │
│ 监控: │
│ ├── 指标:Prometheus + Grafana │
│ ├── 日志:Loki / ELK │
│ └── 告警:AlertManager + Telegram │
└──────────────────────────────────────────────────────────┘
成本估算
| 组件 | 月成本(估算) | 说明 |
|---|---|---|
| 面板服务器 | $5-10 | 1C1G VPS |
| 节点服务器(3 个) | $15-45 | 3 × 1C1G VPS |
| 域名 | $1-2 | 年付分摊 |
| SSL 证书 | $0 | Let’s Encrypt 免费 |
| 监控服务器 | $5 | 可选,与面板共用 |
| 备用/带宽 | $10-20 | 视使用量而定 |
| 合计 | $36-82/月 | 个人/小团队规模 |
📋 完整部署检查清单
PLAINTEXT
面板部署:
□ 部署 V2board 面板
□ 配置 MySQL + Redis
□ 配置 Nginx + TLS
□ 配置定时任务和队列
□ 测试注册/登录
节点配置:
□ 部署节点端程序
□ 配置 xray stats
□ 测试流量上报
□ 配置健康检查
□ 配置自动重启
用户系统:
□ 测试订阅链接生成
□ 测试流量统计
□ 测试到期/超量处理
□ 配置通知系统
支付系统(如需):
□ 集成支付接口
□ 测试支付流程
□ 测试回调处理
□ 配置退款流程
监控告警:
□ 部署 Prometheus + Grafana
□ 配置告警规则
□ 配置 Telegram 机器人
□ 测试告警触发
安全合规:
□ 配置 HTTPS
□ 启用 2FA
□ 配置防火墙
□ 设置定期备份
□ 了解法律法规面板部署:
□ 部署 V2board 面板
□ 配置 MySQL + Redis
□ 配置 Nginx + TLS
□ 配置定时任务和队列
□ 测试注册/登录
节点配置:
□ 部署节点端程序
□ 配置 xray stats
□ 测试流量上报
□ 配置健康检查
□ 配置自动重启
用户系统:
□ 测试订阅链接生成
□ 测试流量统计
□ 测试到期/超量处理
□ 配置通知系统
支付系统(如需):
□ 集成支付接口
□ 测试支付流程
□ 测试回调处理
□ 配置退款流程
监控告警:
□ 部署 Prometheus + Grafana
□ 配置告警规则
□ 配置 Telegram 机器人
□ 测试告警触发
安全合规:
□ 配置 HTTPS
□ 启用 2FA
□ 配置防火墙
□ 设置定期备份
□ 了解法律法规
结语
自建代理服务的运营是一个系统工程,涉及前端、后端、数据库、节点管理、监控告警等多个技术领域。本文从技术架构角度梳理了核心组件和实现思路。
核心要点回顾:
- ✅ 面板系统:V2board 是功能完善的开源方案
- ✅ 用户管理:UUID 认证 + Token 订阅
- ✅ 流量计费:xray stats API + 定时上报
- ✅ 节点管理:健康检查 + 自动重启
- ✅ 支付系统:多支付方式集成
- ✅ 监控告警:Prometheus + Grafana + Telegram
- ✅ 安全合规:HTTPS、2FA、隐私保护
技术发展趋势:
PLAINTEXT
当前主流:
V2board + xray + Prometheus
未来方向:
- 云原生:K8s + Helm + 服务网格
- 边缘计算:CDN + 边缘节点
- AI 运维:智能故障预测 + 自动修复
- 零信任:mTLS + 细粒度权限当前主流:
V2board + xray + Prometheus
未来方向:
- 云原生:K8s + Helm + 服务网格
- 边缘计算:CDN + 边缘节点
- AI 运维:智能故障预测 + 自动修复
- 零信任:mTLS + 细粒度权限
再次提醒:本文仅作技术学习参考。在中国大陆,未经许可经营 VPN 业务属于违法行为。请务必了解并遵守所在地的法律法规,将技术能力用于合法合规的用途。
愿你的技术之路越走越宽!🚀
