本文目录导读:

在PHP项目中实现告警回调自动触发修复脚本,通常有以下几个关键步骤和方案:
告警回调接收端设计
创建告警接收接口
// alert-webhook.php
<?php
header('Content-Type: application/json');
// 验证来源(安全校验)
$validTokens = ['your_secret_token_123'];
$receivedToken = $_SERVER['HTTP_X_ALERT_TOKEN'] ?? '';
if (!in_array($receivedToken, $validTokens)) {
http_response_code(403);
die(json_encode(['error' => 'Invalid token']));
}
// 解析告警数据
$rawData = file_get_contents('php://input');
$alertData = json_decode($rawData, true);
// 验证数据完整性
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
die(json_encode(['error' => 'Invalid JSON']));
}
// 处理告警
$result = handleAlert($alertData);
echo json_encode(['status' => 'success', 'result' => $result]);
function handleAlert($alertData) {
$alertType = $alertData['alert_type'] ?? '';
$severity = $alertData['severity'] ?? 'warning';
$message = $alertData['message'] ?? '';
// 记录告警日志
logAlert($alertType, $severity, $message);
// 判断是否需要自动修复
if (shouldAutoRepair($alertType, $severity)) {
return executeRepairScript($alertType, $alertData);
}
return ['action' => 'logged_only'];
}
function shouldAutoRepair($alertType, $severity) {
// 定义可自动修复的告警类型和级别
$autoRepairRules = [
'disk_full' => ['critical', 'warning'],
'high_cpu' => ['critical'],
'service_down' => ['critical', 'warning'],
'database_connection' => ['critical']
];
return isset($autoRepairRules[$alertType])
&& in_array($severity, $autoRepairRules[$alertType]);
}
执行修复脚本
安全执行脚本
function executeRepairScript($alertType, $alertData) {
$scriptMap = [
'disk_full' => '/opt/scripts/clean_disk.sh',
'high_cpu' => '/opt/scripts/restart_service.sh',
'service_down' => '/opt/scripts/restart_service.sh',
'database_connection' => '/opt/scripts/db_repair.sh'
];
if (!isset($scriptMap[$alertType])) {
return ['error' => 'No repair script configured for this alert type'];
}
$scriptPath = $scriptMap[$alertType];
// 安全检查:脚本必须存在且可执行
if (!file_exists($scriptPath) || !is_executable($scriptPath)) {
logError("Repair script not found or not executable: $scriptPath");
return ['error' => 'Script not available'];
}
// 构建命令,传递参数
$command = escapeshellcmd($scriptPath) . ' ' .
escapeshellarg($alertType) . ' ' .
escapeshellarg(json_encode($alertData));
// 使用proc_open来更好地控制执行
$descriptorspec = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"], // stdout
2 => ["pipe", "w"] // stderr
];
$process = proc_open($command, $descriptorspec, $pipes);
if (is_resource($process)) {
fclose($pipes[0]); // 关闭stdin
$output = stream_get_contents($pipes[1]);
$errorOutput = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$returnCode = proc_close($process);
// 记录执行结果
logRepairResult($alertType, $returnCode, $output, $errorOutput);
return [
'success' => $returnCode === 0,
'return_code' => $returnCode,
'output' => $output,
'error' => $errorOutput
];
}
return ['error' => 'Failed to execute script'];
}
修复脚本示例
Shell脚本示例
#!/bin/bash
# /opt/scripts/clean_disk.sh
ALERT_TYPE=$1
ALERT_DATA=$2
# 解析告警数据中的磁盘信息
DISK_PATH=$(echo $ALERT_DATA | python3 -c "import sys,json; print(json.load(sys.stdin).get('disk_path', '/'))" 2>/dev/null)
# 清理策略
case $DISK_PATH in
"/tmp")
find /tmp -type f -atime +7 -delete
;;
"/var/log")
find /var/log -name "*.log.*" -mtime +30 -delete
journalctl --vacuum-time=7d
;;
"/")
apt-get clean
docker system prune -f --volumes
;;
*)
# 通用清理
find $DISK_PATH -type f -size +100M -atime +30 -delete 2>/dev/null
;;
esac
# 报告清理结果
df -h $DISK_PATH | tail -1
exit $?
防止重复执行和限流
// 使用文件锁防止重复执行
function acquireRepairLock($alertType) {
$lockFile = "/tmp/repair_lock_{$alertType}.lock";
$fp = fopen($lockFile, 'c');
if (!flock($fp, LOCK_EX | LOCK_NB)) {
return false; // 已经有进程在执行
}
// 设置锁的超时时间(例如5分钟)
$lockData = ['pid' => getmypid(), 'time' => time()];
fwrite($fp, json_encode($lockData));
fflush($fp);
return $fp;
}
function releaseRepairLock($fp) {
flock($fp, LOCK_UN);
fclose($fp);
}
// 在handleAlert中使用
$lock = acquireRepairLock($alertType);
if ($lock === false) {
return ['action' => 'skipped', 'reason' => 'Repair already in progress'];
}
try {
$result = executeRepairScript($alertType, $alertData);
releaseRepairLock($lock);
return $result;
} catch (Exception $e) {
releaseRepairLock($lock);
throw $e;
}
告警平台配置示例
Prometheus Alertmanager配置
# alertmanager.yml
receivers:
- name: 'php-auto-repair'
webhook_configs:
- url: 'https://your-server.com/alert-webhook.php'
send_resolved: false
http_config:
headers:
X-Alert-Token: 'your_secret_token_123'
Zabbix配置
// Zabbix告警脚本
<?php
// /usr/lib/zabbix/alertscripts/php_remediation.php
$webhookUrl = 'https://your-server.com/alert-webhook.php';
$alertData = [
'alert_type' => $argv[1],
'severity' => $argv[2],
'message' => $argv[3],
'host' => $argv[4],
'timestamp' => time()
];
$ch = curl_init($webhookUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($alertData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Alert-Token: your_secret_token_123'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
安全注意事项
- 命令注入防护:使用
escapeshellcmd和escapeshellarg - 权限控制:Web服务器用户应有最小权限
- 执行时间限制:设置超时机制
function executeScriptWithTimeout($command, $timeout = 30) {
$process = proc_open($command, $descriptorspec, $pipes);
// 设置非阻塞模式
stream_set_blocking($pipes[1], false);
stream_set_blocking($pipes[2], false);
$startTime = time();
$output = '';
$errorOutput = '';
while (true) {
// 检查是否超时
if (time() - $startTime > $timeout) {
proc_terminate($process, 9); // SIGKILL
return ['error' => 'Execution timeout'];
}
$status = proc_get_status($process);
if (!$status['running']) {
break;
}
// 读取输出
$output .= stream_get_contents($pipes[1]);
$errorOutput .= stream_get_contents($pipes[2]);
usleep(100000); // 100ms sleep
}
// 读取剩余输出
$output .= stream_get_contents($pipes[1]);
$errorOutput .= stream_get_contents($pipes[2]);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
$returnCode = proc_close($process);
return [
'output' => $output,
'error' => $errorOutput,
'return_code' => $returnCode
];
}
这个方案提供了一个安全、可扩展的告警自动修复框架,根据具体需求,可以调整修复规则、添加更多告警类型,或集成到现有的告警系统中。