博客

  • 网站出现502或者数据库下线的脚本

    一键布局 PHP-FPM + MySQL 双重自愈与邮件告警 系统:

    第一步:创建轻量 PHP 探活文件

    在新服务器终端执行:

    cd /data/wwwroot/vitalityboutique.co.uk
    echo "<?php http_response_code(200); echo 'ok'; ?>" > healthz.php
    chmod 644 healthz.php

    验证测试:

    curl -I https://www.vitalityboutique.co.uk/healthz.php

    确认第一行返回 HTTP/2 200 或 HTTP/1.1 200 OK。

    第二步:部署邮件发信模块

    如果新服务器尚未安装 Python 3,先执行安装:

    yum install -y python3 || dnf install -y python3 || apt update && apt install -y python3

    创建发信组件 /usr/local/bin/send_alert_mail.py:

    cat << 'EOF' > /usr/local/bin/send_alert_mail.py
    #!/usr/bin/env python3
    import sys
    import smtplib
    from email.mime.text import MIMEText
    from email.header import Header
    from email.utils import formataddr
    
    if len(sys.argv) < 3:
        print("Usage: python3 send_alert_mail.py <to_email> <subject> <content>")
        sys.exit(1)
    
    to_email = sys.argv[1]
    subject = sys.argv[2]
    content = sys.argv[3] if len(sys.argv) > 3 else sys.stdin.read()
    
    smtp_host = "smtp.163.com"
    smtp_port = 465
    sender = "[email protected]"
    password = "你的授权码"  # 163 邮箱 SMTP 授权码
    
    message = MIMEText(content, 'plain', 'utf-8')
    message['From'] = formataddr(("Server Monitor", sender))
    message['To'] = formataddr(("Admin", to_email))
    message['Subject'] = Header(subject, 'utf-8')
    
    try:
        server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=15)
        server.login(sender, password)
        server.sendmail(sender, [to_email], message.as_string())
        server.quit()
        print("SUCCESS: 邮件发送成功")
    except Exception as e:
        print(f"ERROR: 邮件发送失败: {e}", file=sys.stderr)
        sys.exit(1)
    EOF
    
    chmod +x /usr/local/bin/send_alert_mail.py

    快速验证:

    /usr/local/bin/send_alert_mail.py "[email protected]" "【新服务器测试】发信验证 vitalityboutique.co.uk" "邮件发送模块配置正常。"

    终端输出 SUCCESS: 邮件发送成功 且 QQ 邮箱收到邮件即表示通过。

    第三步:部署双重监控自愈主脚本

    将目标网址绑定为 [https://log.vitalitys.site/test.php](https://log.vitalitys.site/test.php 并写入主脚本:

    cat << 'EOF' > /usr/local/bin/monitor_and_heal.sh
    #!/usr/bin/env bash
    export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    
    # ==============================================================================
    # 网站高可用守护脚本:PHP-FPM + MySQL 双重自愈与邮件告警
    # ==============================================================================
    
    # -------------- 配置项 --------------
    TARGET_URL="https://log.vitalitys.site/test.php"
    PHP_SERVICE="php-fpm"
    
    # 自动匹配数据库服务名(mysqld 或 mariadb)
    if systemctl list-unit-files | grep -q "^mariadb.service"; then
        DB_SERVICE="mariadb"
    else
        DB_SERVICE="mysqld"
    fi
    
    MAX_RETRIES=3
    RETRY_INTERVAL=3
    COOLDOWN_PERIOD=300
    
    LOCK_FILE="/var/run/site_monitor.lock"
    LOG_FILE="/var/log/site_healer.log"
    PHP_RESTART_FILE="/tmp/site_last_php_restart"
    DB_RESTART_FILE="/tmp/site_last_db_restart"
    
    ALERT_EMAIL="[email protected]"
    MAIL_SENDER="/usr/local/bin/send_alert_mail.py"
    
    # -------------- 基础函数 --------------
    log() {
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
    }
    
    send_email_alert() {
        local subject="$1"
        local content="$2"
        local svc_name="$3"
        
        if [ -x "$MAIL_SENDER" ] && [ -n "$ALERT_EMAIL" ]; then
            local body
            body=$(cat <<EOM
    监控自愈告警通知:
    
    - 监控目标: $TARGET_URL
    - 涉及服务: $svc_name
    - 服务器主机名: $(hostname)
    - 服务器IP: $(hostname -I | awk '{print $1}')
    - 告警详情: $content
    - 记录时间: $(date '+%Y-%m-%d %H:%M:%S')
    
    详细日志请登录服务器查看: $LOG_FILE
    EOM
    )
            "$MAIL_SENDER" "$ALERT_EMAIL" "$subject" "$body" > /dev/null 2>&1
        fi
    }
    
    check_http_status() {
        local code
        code=$(curl -o /dev/null -s -w "%{http_code}" --connect-timeout 5 -m 8 "$TARGET_URL")
        # 数字安全校验,防止非数字解析异常
        if [[ ! "$code" =~ ^[0-9]+$ ]]; then
            echo "000"
        else
            echo "$code"
        fi
    }
    
    check_mysql_status() {
        if command -v mysqladmin >/dev/null 2>&1; then
            mysqladmin ping --silent >/dev/null 2>&1
            return $?
        else
            systemctl is-active --quiet "$DB_SERVICE"
            return $?
        fi
    }
    
    # -------------- 并发保护(防重入) --------------
    exec 200>"$LOCK_FILE"
    if ! flock -n 200; then
        exit 0
    fi
    
    CURRENT_TIME=$(date +%s)
    
    # ==============================================================================
    # 模块一:MySQL 存活检测与自愈
    # ==============================================================================
    if ! check_mysql_status; then
        sleep 2
        if ! check_mysql_status; then
            can_restart_db=1
            if [ -f "$DB_RESTART_FILE" ]; then
                last_db_restart=$(cat "$DB_RESTART_FILE" 2>/dev/null || echo 0)
                time_diff=$((CURRENT_TIME - last_db_restart))
                if [ "$time_diff" -lt "$COOLDOWN_PERIOD" ]; then
                    log "WARN: 检测到 MySQL 异常,但处于冷却期内(上次重启于 ${time_diff}s 前),跳过重启。"
                    can_restart_db=0
                fi
            fi
    
            if [ "$can_restart_db" -eq 1 ]; then
                log "ALERT: 检测到 MySQL ($DB_SERVICE) 停止响应,开始执行重启..."
                if systemctl restart "$DB_SERVICE"; then
                    echo "$CURRENT_TIME" > "$DB_RESTART_FILE"
                    log "SUCCESS: MySQL ($DB_SERVICE) 重启成功。"
                    send_email_alert "【数据库自愈成功】MySQL 异常已自动重启恢复" \
                                     "检测到本地 MySQL 服务停止或无法响应 ping,已成功自动执行 systemctl restart $DB_SERVICE 重启拉起。" \
                                     "$DB_SERVICE"
                else
                    log "ERROR: MySQL ($DB_SERVICE) 重启失败!"
                    send_email_alert "【紧急故障】MySQL ($DB_SERVICE) 重启失败!" \
                                     "检测到 MySQL 宕机,尝试执行自动重启失败,请立即登录服务器排查数据库日志!" \
                                     "$DB_SERVICE"
                fi
            fi
        fi
    fi
    
    # ==============================================================================
    # 模块二:HTTP / PHP-FPM 检测与自愈
    # ==============================================================================
    failure_count=0
    last_code=""
    
    for i in 1 2 3; do
        code=$(check_http_status)
        last_code="$code"
        
        if [ "$code" != "200" ]; then
            failure_count=$((failure_count + 1))
        else
            break
        fi
        
        if [ "$i" -lt "$MAX_RETRIES" ]; then
            sleep "$RETRY_INTERVAL"
        fi
    done
    
    if [ "$failure_count" -ge "$MAX_RETRIES" ]; then
        can_restart_php=1
        if [ -f "$PHP_RESTART_FILE" ]; then
            last_php_restart=$(cat "$PHP_RESTART_FILE" 2>/dev/null || echo 0)
            time_diff=$((CURRENT_TIME - last_php_restart))
            if [ "$time_diff" -lt "$COOLDOWN_PERIOD" ]; then
                log "WARN: 状态码 $last_code 异常,但处于 PHP 冷却期内(上次重启于 ${time_diff}s 前),跳过重启。"
                can_restart_php=0
            fi
        fi
    
        if [ "$can_restart_php" -eq 1 ]; then
            log "ALERT: 目标 $TARGET_URL 连续 $MAX_RETRIES 次返回 $last_code,开始重启服务 $PHP_SERVICE..."
            if systemctl restart "$PHP_SERVICE"; then
                echo "$CURRENT_TIME" > "$PHP_RESTART_FILE"
                log "SUCCESS: 服务 $PHP_SERVICE 重启成功。"
                send_email_alert "【网站自愈成功】状态码 $last_code 已自动重启 $PHP_SERVICE" \
                                 "目标探活接口连续检测到 $last_code 错误,已自动重启 $PHP_SERVICE 服务并恢复。" \
                                 "$PHP_SERVICE"
            else
                log "ERROR: 服务 $PHP_SERVICE 重启失败!"
                send_email_alert "【紧急故障】服务 $PHP_SERVICE 重启失败!" \
                                 "检测到异常状态码 $last_code,尝试重启 $PHP_SERVICE 失败,请立即人工介入!" \
                                 "$PHP_SERVICE"
            fi
        fi
    fi
    EOF
    
    chmod +x /usr/local/bin/monitor_and_heal.sh

    验证测试:

    /usr/local/bin/monitor_and_heal.sh

    终端在 1 秒内静默无报错退出即可。

    第四步:配置 Crontab 周期守护

    1. 打开定时任务:
    crontab -e

    2, 移到最末行,添加以下内容(5 个星号):

    * * * * * /usr/local/bin/monitor_and_heal.sh >/dev/null 2>&1

    3,保存后查看确认:

    crontab -l

    4,确保 Cron 守护进程处于运行状态:

    systemctl status crond || systemctl status cron

    完成后,log.vitalitys.site 站点的 PHP-FPM 和 MySQL 就处于全天候自动监控自愈守护下了。