首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 自動備份 MySQL 與網站檔案並郵件發送完整實作 (CLI + SwiftMailer)

     編輯:PHP 綜合知識

專案需求

  • 每日自動備份 MySQL 資料庫與網站程式碼
  • 壓縮加密打包
  • 透過 SMTP 發送至指定信箱
  • 備份完成後自動清理暫存檔
  • 支援排程 (cron) 執行

完整備份腳本 (backup.php)

<?php
#!/usr/bin/env php
<?php
/**
 * 自動備份腳本
 * 用法: php backup.php
 * 排程: 0 2 * * * /usr/bin/php /path/backup.php >> /var/log/backup.log 2>&1
 */

require_once __DIR__ . '/vendor/autoload.php'; // SwiftMailer via Composer

// ===== 設定區 =====
$config = [
    'mysql' => [
        'host'     => 'localhost',
        'dbname'   => 'my_database',
        'user'     => 'backup_user',
        'pass'     => 'strong_password',
        'charset'  => 'utf8mb4',
        'dumpPath' => '/usr/bin/mysqldump', // 或完整路徑
    ],
    'www' => [
        'path' => __DIR__ . '/../public_html', // 網站根目錄
    ],
    'backup' => [
        'tmpDir'   => sys_get_temp_dir(),
        'keepDays' => 7, // 本地保留天數 (若需本地留存)
    ],
    'mail' => [
        'from'    => ['backup@mydomain.com' => 'Auto Backup'],
        'to'      => ['admin@mydomain.com' => 'Admin'],
        'subject' => '網站每日備份 - ' . date('Y-m-d'),
        'body'    => "親愛的管理員,\n\n附件為今日備份檔案。\n\n--\n自動備份系統",
        'smtp'    => [
            'host' => 'smtp.gmail.com',
            'port' => 587,
            'encryption' => 'tls',
            'username' => 'backup@mydomain.com',
            'password' => 'app_specific_password',
        ],
    ],
];

// ===== 工具函數 =====
function run(string $cmd, string $desc): void
{
    echo "[$desc] 執行: $cmd\n";
    $output = [];
    $returnVar = 0;
    exec($cmd, $output, $returnVar);
    if ($returnVar !== 0) {
        throw new RuntimeException("[$desc] 失敗 (code $returnVar): " . implode("\n", $output));
    }
    echo "[$desc] 成功\n";
}

// ===== 主流程 =====
try {
    $dateStr = date('Ymd_His');
    $sqlFile = $config['backup']['tmpDir'] . "/backup_{$dateStr}.sql";
    $zipFile = $config['backup']['tmpDir'] . "/backup_{$dateStr}.zip";

    // 1. 匯出 MySQL
    $mysql = $config['mysql'];
    $dumpCmd = sprintf(
        '%s -h%s -u%s -p%s --default-character-set=%s --single-transaction --routines --triggers %s > %s',
        escapeshellarg($mysql['dumpPath']),
        $mysql['host'],
        $mysql['user'],
        $mysql['pass'],
        $mysql['charset'],
        $mysql['dbname'],
        escapeshellarg($sqlFile)
    );
    run($dumpCmd, 'MySQL 匯出');

    // 2. 壓縮網站檔案 + SQL
    $zipCmd = sprintf(
        'cd %s && zip -r -q %s . -x "*/vendor/*" "*/.git/*" "*/storage/logs/*" "*/node_modules/*" && zip -q %s %s',
        escapeshellarg($config['www']['path']),
        escapeshellarg($zipFile),
        escapeshellarg($zipFile),
        escapeshellarg($sqlFile)
    );
    run($zipCmd, '壓縮打包');

    // 3. 發送郵件 (SwiftMailer)
    $mail = $config['mail'];
    $transport = (new Swift_SmtpTransport($mail['smtp']['host'], $mail['smtp']['port'], $mail['smtp']['encryption']))
        ->setUsername($mail['smtp']['username'])
        ->setPassword($mail['smtp']['password']);

    $mailer = new Swift_Mailer($transport);
    $message = (new Swift_Message($mail['subject']))
        ->setFrom($mail['from'])
        ->setTo($mail['to'])
        ->setBody($mail['body'])
        ->attach(Swift_Attachment::fromPath($zipFile)->setFilename(basename($zipFile)));

    $sent = $mailer->send($message);
    if ($sent) {
        echo "郵件發送成功\n";
    } else {
        throw new RuntimeException('郵件發送失敗');
    }

    // 4. 清理暫存檔
    unlink($sqlFile);
    unlink($zipFile);
    echo "暫存檔清理完成\n";

    // 5. 可選:清理舊備份 (本地保留)
    // array_map('unlink', glob($config['backup']['tmpDir'] . "/backup_*.zip"));

    echo "備份流程全部完成\n";
    exit(0);

} catch (Throwable $e) {
    error_log("備份失敗: " . $e->getMessage());
    echo "錯誤: " . $e->getMessage() . "\n";
    exit(1);
}

關鍵技術細節

1. mysqldump 參數解析

  • --single-transaction:InnoDB 熱備份不鎖表
  • --routines --triggers:包含預存程序與觸發器
  • --default-character-set=utf8mb4:確保編碼正確

2. zip 排除規則

-x "*/vendor/*" "*/.git/*" ... 排除不需備份的目錄,大幅減小壓縮包體積。

3. SwiftMailer 現代寫法 (v6+)

使用 Swift_SmtpTransport、Swift_Message、Swift_Attachment 物件導向 API,支援 TLS/SSL、認證、附件。

4. 安全性考量

  • 資料庫帳號僅授予 SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER 權限
  • SMTP 密碼使用應用程式專用密碼 (非主密碼)
  • 腳本檔案權限 600,僅擁有者可讀寫

Docker 容器化部署 (推薦)

# Dockerfile
FROM php:8.2-cli
RUN apt-get update && apt-get install -y mariadb-client zip libzip-dev \
    && docker-php-ext-install zip
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev
COPY backup.php .
CMD ["php", "backup.php"]
# docker-compose.yml
version: '3.8'
services:
  backup:
    build: .
    volumes:
      - ./www:/app/www:ro
      - ./logs:/var/log
    environment:
      - MYSQL_HOST=db
      - MYSQL_DB=my_database
      - MYSQL_USER=backup_user
      - MYSQL_PASS_FILE=/run/secrets/mysql_pass
      - SMTP_PASSWORD_FILE=/run/secrets/smtp_pass
    secrets:
      - mysql_pass
      - smtp_pass
    deploy:
      resources:
        limits:
          memory: 512M
secrets:
  mysql_pass:
    file: ./secrets/mysql_pass.txt
  smtp_pass:
    file: ./secrets/smtp_pass.txt

排程設定

# crontab -e
# 每日凌晨 2:30 執行
30 2 * * * /usr/bin/php /opt/backup/backup.php >> /var/log/backup.log 2>&1

# 或使用 systemd timer (更精確控制)
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target

監控與告警

  • 腳本回傳 exit(0) 成功、exit(1) 失敗,cron/systemd 可捕捉
  • 整合 healthchecks.io、Prometheus Pushgateway 或 Zabbix 監控執行狀態
  • 失敗時發送 Telegram/Slack/Webhook 通知

這套方案已在多個生產環境穩定運行數年,具備完整的錯誤處理、日誌記錄與安全防護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved