Crontab 是 Linux 定時任務標準工具,但 PHP CLI 執行環境與 Web (FPM) 差異極大,導致「手動跑通、排程失敗」是常見痛點。本文系統整理 Crontab 語法、PHP CLI 環境配置、併發防重鎖、日誌輸出、監控告警與常見坑點清單。
* * * * * 執行命令
- - - - -
| | | └─ 週幾 (0-7) (Sun=0 or 7)
| | └──── 月份 (1-12)
| └────── 日期 (1-31)
| └────── 小時 (0-23)
└──────── 分鐘 (0-59)
特殊符號:
* 所有值
, 列舉 (1,15,30)
- 範圍 (1-5)
/ 步長 (*/10 = 每 10 分鐘)
@yearly @monthly @weekly @daily @hourly @reboot
# 每天凌晨 2:30 執行備份
30 2 * * * /usr/bin/php /var/www/scripts/backup.php >> /var/log/backup.log 2>&1
# 每小時第 5 分鐘清理快取
5 * * * * /usr/bin/php /var/www/scripts/clear_cache.php
# 每週一 03:00 產生報表 (僅工作日)
0 3 * * 1-5 /usr/bin/php /var/www/scripts/report.php
# 開機自動啟動佇列 Worker
@reboot /usr/bin/php /var/www/artisan queue:work --sleep=3 --tries=3 >> /var/log/queue.log 2>&1
# 在 crontab 檔案開頭定義 (crontab -e)
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
# PHP 專用環境變數
PHP_INI_SCAN_DIR=/etc/php.d
APP_ENV=production
# 專案根目錄 (供腳本定位 .env、vendor)
APP_BASE=/var/www/html
# 然後寫任務
30 2 * * * cd /var/www/html && /usr/bin/php artisan backup:run --env=production >> /var/log/backup.log 2>&1
<?php
// scripts/backup.php
require __DIR__ . '/../vendor/autoload.php';
$lockFile = sys_get_temp_dir() . '/backup_' . basename(__FILE__, '.php') . '.lock';
$fp = fopen($lockFile, 'c+');
if (!flock($fp, LOCK_EX | LOCK_NB)) {
// 已有實例在執行
error_log("[$lockFile] Another instance is running, exit.");
exit(0);
}
// 註冊釋放鎖 (腳本結束自動釋放,但顯式釋放更佳)
register_shutdown_function(function() use ($fp, $lockFile) {
flock($fp, LOCK_UN);
fclose($fp);
@unlink($lockFile);
});
// 實際業務邏輯
// ...
echo "Backup completed at " . date('c') . "\n";
# /usr/local/bin/php-cron-wrapper
#!/bin/bash
# 用法:php-cron-wrapper "lock_name" "php_script.php" [args...]
LOCK_NAME=$1
shift
SCRIPT=$1
shift
LOCK_FILE="/var/lock/php-cron-${LOCK_NAME}.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "[$(date)] Lock $LOCK_NAME held, skipping." >&2
exit 0
fi
# 執行 PHP 腳本,保留所有參數
exec php "$SCRIPT" "$@"
# crontab 使用
30 2 * * * /usr/local/bin/php-cron-wrapper backup /var/www/html/artisan backup:run >> /var/log/backup.log 2>&1
| 需求 | 方案 |
|---|---|
| 標準輸出/錯誤捕獲 | >> /var/log/xxx.log 2>&1 (追加模式) |
| 系統日誌整合 | logger -t backup "message" 或 Monolog SyslogHandler |
| 執行時間記錄 | 腳本開頭 $start = microtime(true); 結尾 error_log("Duration: " . (microtime(true) - $start)); |
| 失敗告警 | 腳本非零退出碼觸發監控系統 (Prometheus Node Exporter textfile, Zabbix, Datadog) |
| 健康檢查端點 | 定時任務完成後 curl 回報健康檢查服務 (Healthchecks.io, Cronitor) |
which php vs php -v,crontab 用絕對路徑 /usr/bin/php 或 /usr/local/bin/php。require '../config.php' 失效。解法:cd /var/www/html && php script.php 或腳本內 chdir(__DIR__)。$_ENV、getenv() 讀不到 .env。解法:crontab 定義或腳本 Dotenv::load() 指定路徑。php.ini 可能與 FPM 不同。解法:php -d memory_limit=512M -d max_execution_time=0 script.php。date() 輸出非預期時區。解法:date_default_timezone_set('Asia/Taipei') 或 php.ini date.timezone。export LANG=zh_TW.UTF-8)。掌握「絕對路徑、環境變數、工作目錄、防重鎖、日誌輸出」五大鐵律,並建立標準化包裝器 php-cron-wrapper,能讓 PHP 定時任務在生產環境穩定、可觀測、可維護運行。