cron muhiti va log'lari
Terminalda yozilganda mukammal ishlaydigan buyruq crontab’ga qo'shilgach ham xuddi shunday ishlashi kafolatlanmagan. Vaqtni eng ko'p yeydigan cron muammolarining eng katta toifasi aynan shu: skript qo'lda xatosiz ishlaydi, cron orqali esa go'yo hech narsa qilmaydi. Sababi deyarli har doim bitta — cron buyruqni interaktiv terminal sessiyasidan ancha kambag'al muhitda ishga tushiradi.
Nimalarni o'rganasiz
Bu maqola oxirida siz quyidagilarni qila olasiz:
- cron muhiti interaktiv terminal muhitidan qanday farq qilishini tushuntirish;
- crontab ichida
PATHva boshqa muhit o'zgaruvchilarini to'g'ri o'rnatish; - chiqishni email orqali olish uchun
MAILTOni sozlash yoki uni o'chirib qo'yish; datekabi buyruqlar cron ostida buzilmasligi uchun%belgisini to'g'ri ekranlash (escape qilish);- cron vazifasining natijasini
journalctlva syslog orqali tekshirish; - "qo'lda ishlaydi, cron ostida ishlamaydi" muammosini tizimli diagnostika qilish.
cron muhiti terminal muhitidan qanday farq qiladi
Terminalga kirganingizda shell (bash) .bashrc va .bash_profile fayllaringizni o'qiydi. Ular odatda PATH ni kengaytiradi, alias va funksiyalarni aniqlaydi. cron daemon esa buyruqni login bo'lmagan, interaktiv bo'lmagan minimal muhitda ishga tushiradi — bu fayllarning birortasi ham o'qilmaydi.
Endi buni cron’ning o'zi ko'radigan PATH bilan taqqoslang. Buning uchun vaqtinchalik crontab vazifasi orqali env chiqishini faylga yozamiz:
Bir daqiqa kuting, natijani tekshiring, keyin vazifani olib tashlang:
Farq darhol ko'rinadi: PATH ancha qisqa (/usr/bin:/bin), .local/bin va qo'lda qo'shilgan boshqa kataloglar yo'q, SHELL esa /bin/sh ga o'rnatilgan. Bu odatda bash ga qaraganda cheklanganroq, POSIX’ga yo'naltirilgan shell.
Tip
Sinovdan keyin * * * * * env > /tmp/cron-env.txt qatorini crontab -e bilan olib tashlang. Aks holda u hech qanday foydasiz har daqiqada faylni qayta yozib turaveradi.
Crontab ichida PATH ni tuzatish
Terminalda ishlaydigan, lekin cron ostida "command not found" deb xabar beradigan buyruqning sababi deyarli har doim shu qisqaroq PATH. Ikki xil yechim bor:
1-variant — buyruqda absolyut yo'ldan foydalanish (tavsiya etiladi):
2-variant — crontab boshida PATH ni aniq o'rnatish:
Crontab boshida KEY=value ko'rinishida yozilgan qatorlar muhit o'zgaruvchilarini o'rnatadi va ulardan pastdagi har bir vazifaga ta’sir qiladi. Ikkinchi yondashuv qulayroq ko'rinishi mumkin, lekin birinchisi ishonchliroq: skript qaysi katalogdan chaqirilishidan qat’i nazar bir xil ishlaydi va keyinchalik boshqa serverga ko'chirilsa, ma’lum bir PATH sozlamasiga bog'liq bo'lib qolmaydi.
Ishchi katalog ham terminalnikidek emas
cron odatda buyruqni foydalanuvchining $HOME katalogidan ishga tushiradi, siz uni sinab ko'rgan terminal esa boshqa joyda turgan bo'lishi mumkin. Nisbiy yo'llarga yoki nisbiy fayl havolalariga tayanadigan skript aynan shu sababli ishlamay qolishi mumkin:
cd /opt/myapp && skript ishga tushishidan oldin ishchi katalogni aniq belgilaydi. Bu skript ichidagi nisbiy yo'llar (masalan, ./config.yaml) to'g'ri topilishini kafolatlaydi.
Chiqishni boshqarish: MAILTO
Standart holatda, agar buyruq biror chiqish (stdout yoki stderr) bersa, cron uni email orqali yuborishga urinadi (tizimda mahalliy pochta yetkazib berish sozlangan deb hisoblanadi):
[email protected]
0 2 * * * /usr/local/bin/backup.sh
Fayl boshida o'rnatilgan bu qator undan pastdagi har bir vazifaning chiqishini ko'rsatilgan manzilga yuboradi. Ko'plab production serverlarda mahalliy pochta umuman sozlanmagan bo'ladi. Bunday holatda email "yuborishga urinadi", hech qayerga yetib bormaydi va shunchaki mahalliy pochta navbatida to'planib qoladi.
Agar email umuman kerak bo'lmasa, uni butunlay o'chirib qo'yish mumkin:
Chiqishni faylga yo'naltirish
Amalda eng ishonchli yondashuv — umuman email’ga tayanmasdan, chiqishni aniq faylga yuborish:
>> /var/log/backup.log standart chiqishni faylni qisqartirmasdan uning oxiriga qo'shadi, 2>&1 esa xatolar oqimini (stderr) o'sha faylga yo'naltiradi. Bu Oqimlar, pipe va yo'naltirish maqolasida ko'rib chiqilgan o'sha yo'naltirish mexanizmi. Farqi faqat shundaki, u interaktiv terminalda emas, cron muhitida ishlayapti.
Warning
Agar skript allaqachon o'zi log yozsa — masalan, bash scripting modulidagi backup skripti dagi log() funksiyasi — ustidan >> file 2>&1 qo'shish baribir foydali. Chunki u skriptning o'zi qayta ishlamaydigan xatolarni (masalan, interpreter ishga tusha olmaganini) ushlaydi. Ikkalasi bir-biriga zid emas, aksincha bir-birini to'ldiradi.
Crontab’da % belgisi maxsus
Crontab buyrug'i ichida ekranlanmagan % oddiy foiz belgisi emas. cron birinchi % ni yangi qatorga almashtiradi va undan keyingi hamma narsani buyruqqa standart kirish (stdin) sifatida uzatadi. Bu % ishlatadigan har qanday buyruqni — ko'pincha date ni — jimgina buzadi:
Bu yerda cron %Y%m%d... ni yangi qator va stdin’ga aylantiradi. Natijada skript qirqilgan --out backup- ni ko'radi va umuman sana bo'lmaydi — buni tushuntiradigan hech qanday xato ham chiqmaydi. Har bir % teskari slash bilan ekranlanishi kerak:
Tozaroq yechim — % ishlatadigan hamma narsani skript ichiga ko'chirish. U yerda oddiy shell qoidalari amal qiladi va ekranlash kerak bo'lmaydi. Crontab qatori esa shunchaki skriptni chaqiradi, unda % umuman bo'lmaydi. Bu crontab qatorlarini ixcham saqlab, haqiqiy mantiqni skript faylida yozish uchun yana bir sabab.
Chiqishni journalctl va syslog orqali kuzatish
cron vazifasi har safar ishga tushganda — chiqish beradimi yoki yo'qmi — daemon’ning o'zi bu faktni tizim log'iga yozadi. Ubuntu Server’da buni journalctl orqali ko'rish mumkin:
Jul 26 02:00:01 myserver CRON[18422]: (root) CMD (/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1)
Bu yozuv vazifa muvaffaqiyatli tugaganini emas, balki ishga tushganini tasdiqlaydi. Buyruqning o'zi xato bilan tugaganmi yoki yo'qmi, cron baribir bir xil turdagi yozuvni qoldiradi. Muvaffaqiyatni muvaffaqiyatsizlikdan ajratish uchun skriptning o'z chiqishini yoki uning exit kodini tekshirish kerak.
Xuddi shu yozuv an’anaviy matnli log'da ham bor (Linux loglari maqolasida ko'rib chiqilganidek, rsyslog xabarlarni journald dan o'qib, faylga yozadi):
Jul 26 02:00:01 myserver CRON[18422]: (root) CMD (/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1)
RHEL/CentOS/Rocky oilasida bu yozuvlar odatda /var/log/cron ga tushadi, syslog esa umuman bo'lmasligi mumkin. Aniq joy distributivga qarab farq qilgani uchun birinchi tekshiruvni journalctl -u crond dan boshlash universalroq.
Amaliy vaziyat: "qo'lda ishlaydi, cron ostida ishlamaydi"
Muammo. /usr/local/bin/sync-reports.sh skripti terminaldan qo'lda ishga tushirilganda muammosiz ishlaydi, lekin crontab’da sozlangan 0 3 * * * sync-reports.sh vazifasi hech qanday ko'rinadigan natija bermaydi.
1-qadam — vazifa umuman ishga tushayotganini tasdiqlash:
Vazifa ishga tushgan — demak, muammo cron’ning o'zida emas, buyruq ishlayotgan sharoitlarda.
2-qadam — PATH va nisbiy yo'l bo'yicha shubhani tekshirish:
Fayl PATH da bor, lekin crontab qatorida shunchaki sync-reports.sh deb yozilgan. Terminalda u PATH orqali topiladi, lekin cron’ning qisqaroq PATH i ham /usr/local/bin ni o'z ichiga oladimi, buni yuqoridagi env > /tmp/cron-env.txt usuli bilan alohida tekshirish kerak.
3-qadam — absolyut yo'l bilan va chiqishni log'ga yo'naltirib qayta urinish:
Tekshirish:
Xulosa. Endi haqiqiy sabab ko'rinib turibdi: skript ichida ishlatilgan rsync buyrug'i cron’ning qisqartirilgan PATH ida topilmayapti. Yechim — skript ichida rsync uchun absolyut yo'ldan (/usr/bin/rsync) foydalanish yoki crontab boshida PATH ni kengaytirish.
Keng tarqalgan xatolar
- Avval log'ni tekshirmasdan "ishlamayapti" deb xulosa qilish.
journalctl -u cron.serviceikkita butunlay boshqa muammoni darhol ajratib beradi: vazifa umuman ishga tushdimi yoki ishga tushib, keyin muvaffaqiyatsiz tugadimi. Birinchi diagnostika qadami sifatida uni o'tkazib yubormaslik kerak. MAILTOni hech qachon tekshirmasdan unga tayanish. Mahalliy pochta servisi sozlanmagan serverda xabar "yuboriladi" deb hisoblash xato — xabar hech qayerga yetib bormaydi. Production’da chiqishni faylga yoki markazlashgan log'ga yo'naltirish ishonchliroq.- Interaktiv shell bilan sinab, production’da boshqa shell ostida ishga tushirish. Agar skript
#!/bin/bashbilan boshlansa-yu,bashga xos konstruksiyalardan foydalansa, odatda muammo bo'lmaydi — cron skript faylini/bin/shorqali emas, to'g'ridan-to'g'ri ishga tushiradi. Muammoning ko'proq uchraydigan sababi teskarisi: oddiy POSIXshsemantikasini nazarda tutib yozilgan skript aslida faqatbashda bor xususiyatga tayanib chiqadi. Har ikki holatda ham skriptning execute ruxsatini (chmod +x) va shebang qatorini tekshirish foydali birinchi qadam.
Mashqlar
* * * * * env > /tmp/cron-env.txtvazifasi yordamida cron’ningPATHqiymatini terminalingizdagi$PATHbilan taqqoslang va farqlarni yozib qo'ying.- Har daqiqada ishlaydigan va o'z chiqishini
>> ~/cron-test.log 2>&1bilan~/cron-test.logga qo'shadigan test vazifasi yarating. Bir necha daqiqadan keyinjournalctl -u cron.serviceda ko'rganingizni faylning o'zi bilan taqqoslang. - Crontab boshiga
MAILTO=""qo'shing va u nimani o'chirib qo'yishini tushuntiring (haqiqiy pochta serveri bo'lmasa ham, sozlamaning ma’nosini tushunish yetarli). - Yuqoridagi "qo'lda ishlaydi, cron ostida ishlamaydi" vaziyatini
/usr/local/bindan tashqaridagi buyruqni (masalan,rsync) chaqiradigan o'z skriptingiz yordamida qayta yarating.
Xulosa
cron buyruqlarni terminalga qaraganda sezilarli darajada kambag'al muhitda ishga tushiradi: qisqaroq PATH, boshqa ishchi katalog, bash o'rniga sh. Buni oldindan bilish "qo'lda ishlaydi, cron ostida ishlamaydi" muammolarining ko'pchiligining oldini oladi. Absolyut yo'llar, aniq PATH, faylga yo'naltirilgan chiqish va journalctl -u cron.service ni tekshirish — bularning hammasi bitta asosiy muammoning turli qirralari. Keyingi maqola cron’ning zamonaviy muqobili — systemd timer ni ko'rib chiqadi. U aynan shu kuzatuvchanlik (observability) muammosini boshqacha hal qiladi.