PHP项目告警回调如何触发自动修复脚本执行

wen PHP项目 30

本文目录导读:

PHP项目告警回调如何触发自动修复脚本执行

  1. 告警回调接收端设计
  2. 执行修复脚本
  3. 修复脚本示例
  4. 防止重复执行和限流
  5. 告警平台配置示例
  6. 安全注意事项

在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);

安全注意事项

  1. 命令注入防护:使用escapeshellcmdescapeshellarg
  2. 权限控制:Web服务器用户应有最小权限
  3. 执行时间限制:设置超时机制
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
    ];
}

这个方案提供了一个安全、可扩展的告警自动修复框架,根据具体需求,可以调整修复规则、添加更多告警类型,或集成到现有的告警系统中。

抱歉,评论功能暂时关闭!