一键布局 PHP-FPM + MySQL 双重自愈与邮件告警 系统:
第一步:创建轻量 PHP 探活文件
在新服务器终端执行:
cd /var/www/html echo "<?php http_response_code(200); echo 'ok'; ?>" > test.php chmod 644 healthz.php
验证测试:
curl -I https://log.vitalitys.site/test.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 周期守护
- 打开定时任务:
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 就处于全天候自动监控自愈守护下了。