کرون جاب را ثبت کردهاید، crontab -l هم آن را نشان میدهد، اما هیچ اتفاقی نمیافتد. نه ایمیلی میآید، نه فایلی ساخته میشود، نه لاگی نوشته میشود. فقط سکوت. این سکوت خطرناکترین بخش ماجراست، چون کرون بهطور پیشفرض هیچ خطایی را جایی ثبت نمیکند که شما ببینید. کاری که در این وضعیت باید بکنید این نیست که جاب را دوباره بنویسید؛ باید بفهمید کرون اصلاً اجرا شده یا نه.
اول ثابت کنید کرون جاب اجرا میشود یا نه
قبل از هر تغییری، یک جاب تستی بسازید که هیچ وابستگی به اسکریپت اصلی ندارد:
*/1 * * * * /bin/date >> /tmp/cron-test.log 2>&1
یک دقیقه صبر کنید و بعد cat /tmp/cron-test.log بزنید. اگر فایل پر شد، سرویس cron سالم است و مشکل داخل اسکریپت شماست. اگر فایل ساخته نشد، مشکل در لایه سرویس است: یا crond روی سرور اجرا نشده، یا کاربری که با آن لاگین کردهاید با کاربری که کرون برایش ثبت شده یکی نیست.
اینجا اشتباه میکنند: کاربر با sudo crontab -e جاب را در جدول root ثبت میکند، بعد با کاربر عادی فایل خروجی را چک میکند و چون فایل نیست، نتیجه میگیرد کرون خراب است. در واقع جاب اجرا شده، ولی با دسترسی root و در مسیر دیگری نوشته. همیشه با crontab -l -u username بررسی کنید که جاب دقیقاً زیر نام کدام کاربر نشسته است.
مسیر مطلق، قاتل خاموش کرون جاب
کرون با یک محیط تقریباً خالی اجرا میشود. PATH معمولاً فقط /usr/bin:/bin است و متغیرهایی مثل HOME یا SHELL ممکن است ست نشده باشند. یعنی وقتی در اسکریپت مینویسید python3 manage.py، کرون نمیداند python3 کجاست، چون در PATH او نیست. خطا هم جایی نمیرود.
راهحل ساده است: در ابتدای crontab یک PATH صریح تعریف کنید و در خود جاب هم مسیر کامل باینری را بنویسید.
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
*/5 * * * * /usr/bin/php /home/user/public_html/cron.php >> /home/user/cron.log 2>&1
نکتهای که کمتر گفته میشود: % در crontab معنای خاص دارد و به newline تبدیل میشود. اگر در دستور جاب از date +%Y-%m-%d استفاده کنید، کرون آن را تکهتکه میکند و دستور با خطای عجیب شکست میخورد. باید % را با بکاسلش فرار بدهید: date +\%Y-\%m-\%d.
خروجی را کجا میفرستید؟
کرون هر خروجی استاندارد و هر خطای استاندارد را به ایمیل محلی کاربر میفرستد. اگر MTA روی سرور نصب نباشد، آن ایمیل در صف میماند یا دور ریخته میشود و شما هیچوقت خبردار نمیشوید. دو راه دارید: یا خروجی را به فایل بفرستید، یا آن را به syslog پمپ کنید.
*/10 * * * * /home/user/backup.sh 2>&1 | /usr/bin/logger -t mycron
بعد با journalctl -t mycron -f یا grep mycron /var/log/syslog خطا را زنده ببینید. این روش وقتی ارزش دارد که اسکریپت شما چند مرحله دارد و نمیدانید کدام مرحله میشکند. هزینهاش این است که لاگها روی دیسک جمع میشوند و اگر چرخش لاگ (logrotate) نداشته باشید، بعد از چند ماه فضای پارتیشن را پر میکنند. برای جابهای پرتکرار، فایل لاگ را با logrotate مدیریت کنید یا از logger استفاده کنید که خودش چرخش را انجام میدهد.
کرون جاب وردپرس چرا اجرا نمیشود
در وردپرس داستان یک لایه پیچیدهتر است. وردپرس بهطور پیشفرض از wp-cron.php استفاده میکند که نه یک کرون واقعی است و نه تضمینی دارد؛ فقط وقتی یک بازدیدکننده به سایت میآید اجرا میشود. اگر ترافیک سایت پایین باشد یا سایت کش شده باشد، جابهای زمانبندیشده ساعتها عقب میافتند. علامتش هم این است که ایمیلهای زمانبندیشده با تأخیر میرسند یا افزونههای پشتیبانگیری اصلاً اجرا نمیشوند.
راه درست، غیرفعال کردن wp-cron و جایگزینی آن با یک کرون واقعی سیستمی است. اول در wp-config.php این خط را اضافه کنید:
define('DISABLE_WP_CRON', true);
بعد یک جاب سیستمی بسازید که هر پنج دقیقه با WP-CLI اجرا شود:
*/5 * * * * /usr/local/bin/wp cron event run --due-now --path=/home/user/public_html --quiet
اگر با WP-CLI آشنا نیستید، دستورهای ضروری WP-CLI برای مدیریت وردپرس از ترمینال نقطه شروع خوبی است. و اگر مطمئن نیستید مشکل از کرون است یا از خود وردپرس، راهنمای wp-cron وردپرس تفاوت این دو را با مثال نشان میدهد.
چند دلیل که کمتر به آنها نگاه میکنید
- مجوز اجرا: اسکریپت باید
chmod +xداشته باشد، وگرنه کرون با خطای «Permission denied» بیصدا میمیرد. - پایان خط ویندوزی: اگر فایل را با ویرایشگر ویندوز ذخیره کردهاید،
\r\nدر انتهای خط اول باعث میشود کرون نام باینری را با یک کاراکتر اضافه بخواند و شکست بخورد. باfile script.shیاcat -Aچک کنید. - قفل فایل: اگر اجرای قبلی هنوز تمام نشده باشد و جاب بعدی برسد، دو نسخه همزمان روی یک فایل کار میکنند. با
flockجلوی این را بگیرید. - منطقه زمانی: کرون از ساعت سیستم استفاده میکند، نه از ساعت محلی شما. اگر سرور روی UTC باشد و شما جاب را برای ساعت ۲ بامداد تهران تنظیم کرده باشید، شش ساعت و نیم اختلاف دارید.
برای جابهای حساس، flock را جدی بگیرید:
*/15 * * * * /usr/bin/flock -n /tmp/backup.lock /home/user/backup.sh
سوئیچ -n یعنی اگر قفل آزاد نبود، اجرا نکن و بیصدا رد شو. این دقیقاً همان رفتار درستی است که برای پشتیبانگیری میخواهید.
کدام روش را انتخاب کنم؟
| روش | مناسب برای | هزینهای که میپردازید |
|---|---|---|
| crontab کاربر | اسکریپتهای شخصی، بکاپ، پاکسازی فایل موقت | محیط خالی، نیاز به مسیر مطلق |
| systemd timer | جابهایی که باید لاگ ساختاریافته و retry داشته باشند | نوشتن unit file، پیچیدگی بیشتر |
| wp-cron وردپرس | سایتهای پرترافیک با جابهای غیرحیاتی | اجرای نامطمئن و وابسته به بازدید |
| کرون سیستمی + WP-CLI | هر سایت وردپرسی که زمانبندی برایش مهم است | نیاز به دسترسی SSH و تنظیم اولیه |
اگر فقط یک انتخاب دارم، برای هر جاب حیاتی systemd timer را ترجیح میدهم، چون journalctl خروجی هر اجرا را با timestamp نگه میدارد و OnCalendar قابل تست است. اما برای ۹۰ درصد کارهای روزمره، crontab کافی است و سریعتر راه میافتد. فقط مسیرها را مطلق بنویسید و خروجی را جایی بفرستید که بعداً بتوانید بخوانید.
اگر روی هاست اشتراکی هستید و دسترسی به systemd یا لاگ سیستم ندارید، گزینههایتان محدودتر است. در هاست لینوکس معمولاً crontab از پنل مدیریت قابل تنظیم است و همان قواعد مسیر مطلق و ریدایرکت خروجی برقرار میماند. برای بررسی سریع اینکه مشکل از شبکه یا DNS نیست، ابزارهای رایگان سرورنت کار را کوتاه میکند، و اگر خطای DNS دیدید، راهنمای خطای DNS_PROBE_FINISHED_NXDOMAIN مسیر تشخیص را قدمبهقدم توضیح داده است.
پرسشهای پرتکرار
چرا کرون جاب من در crontab -l دیده میشود اما اجرا نمیشود؟
شایعترین دلیل، خالی بودن PATH در محیط کرون است. کرون با محیطی بسیار محدودتر از شل تعاملی شما اجرا میشود و باینریهایی مثل python3 یا node را پیدا نمیکند. مسیر کامل باینری را در جاب بنویسید و یک خط PATH= در ابتدای crontab اضافه کنید.
دلیل دوم، ثبت جاب زیر کاربر اشتباه است. با crontab -l -u username بررسی کنید جاب دقیقاً برای کدام کاربر ثبت شده و فایل خروجی را با همان کاربر بخوانید.
چطور بفهمم کرون جاب اجرا شده ولی خطا داده؟
خروجی و خطای استاندارد را به یک فایل ریدایرکت کنید: >> /home/user/cron.log 2>&1. بدون این ریدایرکت، کرون خروجی را به ایمیل محلی میفرستد و اگر MTA نصب نباشد، آن را از دست میدهید. برای جابهای پرتکرار، ارسال به logger و خواندن با journalctl تمیزتر است.
آیا wp-cron وردپرس جای کرون واقعی را میگیرد؟
نه. wp-cron فقط وقتی اجرا میشود که کسی از سایت بازدید کند، پس روی سایتهای کمترافیک یا کششده جابها با تأخیر زیاد اجرا میشوند. برای زمانبندی قابل اعتماد، DISABLE_WP_CRON را true کنید و یک جاب سیستمی با WP-CLI بسازید.
تفاوت crontab و systemd timer چیست و کدام را انتخاب کنم؟
crontab سادهتر است و برای اکثر کارها کافی است، اما لاگ ساختاریافته و مکانیزم retry ندارد. systemd timer خروجی هر اجرا را در journal نگه میدارد، وابستگی بین سرویسها را پشتیبانی میکند و زمانبندیاش با systemd-analyze calendar قابل تست است. اگر جاب حیاتی دارید و باید بدانید آخرین اجرا موفق بوده یا نه، systemd timer انتخاب درست است.
قدم بعدی: همین حالا یک جاب تستی با /bin/date بسازید و مطمئن شوید خروجیاش جایی نوشته میشود. تا وقتی این حلقه بسته نشود، هر جاب دیگری که اضافه کنید فقط یک حدس است.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!