← شروع آموزش

WIUNIX ACADEMY

زمان‌بندی با systemd timer؛ نوبت ازدست‌رفته و اجرای تکراری رو مدیریت کن

صفحهٔ 2 از 4؛ قبل از ادامه، مراحل قبلی و چیزهایی رو که لازم داری مرور کن.

آموزش متناسب با محیط تو

کجا اجرا می‌کنی؟

سیستم‌عامل و نسخهٔ سرورت رو انتخاب کن تا مراحل، دستورها و راه‌حل‌های همون محیط رو ببینی.

Arch Linux rollingبررسی‌شده با مستندات رسمی ·

آموزش متناسب با مقادیر تو

تنظیمات این آموزش

مقادیر نمونه رو با مشخصات سرورت عوض کن تا متن و دستورها با هم تغییر کنن. رمز، کلید خصوصی و توکن اینجا وارد نکن. مقدارها فقط توی همین زبانه می‌مونن و به سرور یا لینک اشتراک‌گذاری فرستاده نمی‌شن. قبل از اجرا، DNS، گواهی‌ها و تنظیمات دسترسی شبکه رو روی سرورت بررسی کن.

اگر آی‌پی، دامنه یا پورت‌های نمونه با سرورت فرق دارن، بخش تنظیمات پایین رو باز کن. مقدارها رو عوض کن و «اعمال در آموزش» رو بزن؛ بعد دستورهای تازه رو کپی کن.

تنظیم مقادیر برای محیط من3 مقدار قابل‌تغییر

بذار سرویس بعد از پایان کار دوباره غیرفعال بشه

نوع oneshot تا پایان اسکریپت منتظر می‌مونه. بعد از موفقیت، سرویس به حالت غیرفعال برمی‌گرده تا نوبت بعدی دوباره بتونه شروعش کنه؛ برای همین RemainAfterExit رو نمی‌ذاریم. systemd هویت موقت بدون دسترسی مدیر می‌سازه و پوشهٔ خصوصی داده رو بین اجراها نگه می‌داره. این پوشه رو دستی نساز و با شناسهٔ موقت این کاربر، مالکیت فایل‌های نامرتبط رو عوض نکن.

برای تمام‌شدن اجرای این کار کوچک، دو دقیقه مهلت گذاشتیم؛ تمام‌شدن این مهلت می‌تونه کار رو متوقف کنه. برای برنامهٔ واقعی باید مدت مجاز و اثر قطع‌شدن رو از خود کار تعیین کنی. خروج موفق سرویس هم فقط ادعای برنامه است؛ خروجی معنادار اون رو جدا ببین.

ini · /etc/systemd/system/academy-daily-marker.service

محتوای فایل؛ در ترمینال اجرا نکن

[Unit]
Description=Academy daily marker job

[Service]
Type=oneshot
DynamicUser=yes
StateDirectory=academy-daily-marker
StateDirectoryMode=0700
ExecStart=/bin/sh /usr/local/libexec/academy-daily-marker.sh
TimeoutStartSec=2min

زمان‌بندی روزانه با جبران نوبت ازدست‌رفته

این timer هر روز ساعت 3:00 UTC درخواست اجرا می‌ده. بعد، شروع رو تا 300 ثانیه به‌صورت تصادفی عقب می‌ندازه و یک ثانیه هم بازهٔ دقت در نظر می‌گیره. شلوغی زمان‌بند سیستم می‌تونه تأخیر بیشتری ایجاد کنه. این پراکندگی کمک می‌کنه میزبان‌ها یک‌باره شروع نکنن، اما ظرفیت رزرو نمی‌کنه و تضمین SLA نیست.

گزینهٔ Persistent زمان آخرین تحریک timer رو روی دیسک نگه می‌داره. اگر timer مدتی غیرفعال بوده و یک یا چند نوبت گذشته باشه، بعد از فعال‌شدن یک اجرای جبرانی درخواست می‌شه؛ همان تأخیر تصادفی هنوز اعمال می‌شه. روزهای ازدست‌رفته تک‌تک تکرار نمی‌شن و موفقیت برنامه هم اینجا ثبت نمی‌شه. پس فعال‌کردن timer ممکنه زودتر از نوبت معمول کار رو شروع کنه؛ برای برنامهٔ واقعی قبل از فعال‌سازی این پیامد رو در نظر بگیر.

ini · /etc/systemd/system/academy-daily-marker.timer

محتوای فایل؛ در ترمینال اجرا نکن

[Unit]
Description=Academy daily UTC schedule

[Timer]
OnCalendar=*-*-* 3:00:00 UTC
Persistent=yes
RandomizedDelaySec=300s
AccuracySec=1s
Unit=academy-daily-marker.service

[Install]
WantedBy=timers.target
04

قدم بعدی

فایل‌ها رو بررسی کنیم و یک بار خود کار رو اجرا کنیم

قبل از بارگذاری فایل‌ها، نحو پوسته و سازگاری فایل واحد با systemd نصب‌شده رو بررسی می‌کنیم. همهٔ پیام‌ها رو بخون، حتی هشدار گزینه‌ای که نادیده گرفته شده. بعد فقط سرویس نمونه رو شروع کن و لاگ و پوشهٔ داده‌اش رو ببین؛ هنوز timer رو فعال نکردیم.

بعد از موفقیت oneshot، غیرفعال‌بودن سرویس عادیه؛ نتیجه باید success و کد خروج صفر باشه. اجرای اول باید COMPLETED ثبت کنه و یک فایل متنی تاریخ‌دار بسازه. در حالت DynamicUser، مسیر عمومی داده ممکنه پیوندی به فضای خصوصی systemd باشه. با دسترسی مجاز بررسیش کن و برای دیدن فایل، مالکیت یا مجوزهای کلی رو عوض نکن.

bash

ترمینال میزبان آزمایشی با دسترسی مجاز

sudo /bin/sh -n '/usr/local/libexec/academy-daily-marker.sh' || exit 1
sudo systemd-analyze verify '/etc/systemd/system/academy-daily-marker.service' '/etc/systemd/system/academy-daily-marker.timer' || exit 1
sudo systemctl daemon-reload || exit 1
sudo systemctl start 'academy-daily-marker.service' || exit 1
sudo systemctl show 'academy-daily-marker.service' -p ActiveState -p Result -p ExecMainStatus -p StateDirectory
sudo journalctl -u 'academy-daily-marker.service' --since '-5 min' --no-pager -n 40
sudo ls -l '/var/lib/academy-daily-marker/'
از کجا بفهمم درست انجام شده؟

کار موفق تموم شده و فایل تاریخ‌دارش وجود داره. بررسی نحو یا فعال‌بودن timer به‌تنهایی کافی نیست.

05

قدم بعدی

تکرار بی‌ضرر رو ببین و بعد timer رو فعال کن

در اجرای دومِ همان روز UTC، انتظار داریم ALREADY_DONE ببینیم و همان یک فایل کامل باقی بمونه. وقتی این نتیجه رو دیدی، timer رو فعال و شروع کن. خود سرویس oneshot رو برای شروع هنگام بوت فعال نمی‌کنیم؛ timer باید موقع رسیدن نوبت، سرویس رو فراخوانی کنه.

زمان NEXT و تنظیمات مؤثر رو ببین. مقدار LAST یا LastTriggerUSec زمان تحریک timer است، نه پایان موفق برنامه. اجرای دستی سرویس هم شاهد اجرای زمان‌بندی‌شده نیست. لاگ و خروجی خود برنامه رو جدا نگه دار؛ فایل زمانیِ Persistent دفتر ثبت timer است، نه صف کارهای عقب‌افتاده.

bash

ترمینال میزبان آزمایشی با دسترسی مجاز

sudo systemctl start 'academy-daily-marker.service' || exit 1
sudo journalctl -u 'academy-daily-marker.service' --since '-5 min' --no-pager -n 40
sudo systemctl enable --now 'academy-daily-marker.timer' || exit 1
systemctl is-enabled --quiet 'academy-daily-marker.timer' || exit 1
systemctl is-active --quiet 'academy-daily-marker.timer' || exit 1
systemctl list-timers --all 'academy-daily-marker.timer'
systemctl show 'academy-daily-marker.timer' -p ActiveState -p SubState -p LastTriggerUSec -p NextElapseUSecRealtime -p RandomizedDelayUSec -p AccuracyUSec
از کجا بفهمم درست انجام شده؟

اجرای تکراری ALREADY_DONE ثبت کرده و timer فعال است و با نوبت بعدی معقول منتظر می‌مونه.

اجرای طولانی، خواب میزبان و شکست کار چه فرقی دارن؟

اگر موقع رسیدن نوبت، همان سرویس هنوز فعال باشه، systemd نمونهٔ هم‌زمان تازه نمی‌سازه و همان اجرا رو ادامه می‌ده. این حفاظت فقط برای همین واحد روی همین ماشین است؛ واحد دیگر، اجرای مستقیم اسکریپت یا ماشین دوم رو قفل نمی‌کنه. نوبت‌های ازدست‌رفته هم صف پایدار تشکیل نمی‌دن.

اگر نوبت تقویمی موقع خواب میزبان برسه، بعد از بیدارشدن اجرا درخواست می‌شه. چند نوبت در یک دورهٔ خواب به یک فعال‌سازی تبدیل می‌شن. در این نمونه WakeSystem خاموشه، پس timer ماشین رو بیدار نمی‌کنه. ساعت یکنواختِ timerهای فاصله‌ای هم معمولاً هنگام suspend متوقف می‌شه. این رفتار خواب با جبران مدتی که خود timer غیرفعال بوده فرق داره.

Persistent قرار نیست سرویس شکست‌خورده رو تا رسیدن به موفقیت تکرار کنه. نوبت تقویمی بعدی ممکنه دوباره شروعش کنه؛ تلاش مجدد برنامه باید سیاست روشن و محدود خودش رو داشته باشه. برای کاری که تکرارش خطر داره، حلقهٔ شروع مجددِ سریع نساز. اگر می‌خوای بعد از پایان یک کار طولانی فاصله بیفته، زمان‌بندی از پایان اجرا مناسب‌تره.

06

قدم بعدی

اگر نوبت اجرا نرسید، از خود زمان‌بندی شروع کن

تنظیمات مؤثر timer رو کنار نوبت بعدی، مسیر فایل بارگذاری‌شده و ساعت سیستم بذار. غیرفعال‌بودن timer، نام اشتباه سرویس، عبارت مبتنی بر ساعت محلی، تأخیر تصادفی یا بارگذاری‌نشدن فایل تازه می‌تونه علت باشه. اول شواهد رو ببین؛ فایل زمان Persistent رو برای مرتب‌شدن نمایش پاک نکن.

اگر نوبت بعدی خالی یا غیرمنتظره است، پیام parser، فایل‌های تکمیلی و ساعت واقعی رو بررسی کن. منتظربودن timer هیچ نتیجه‌ای دربارهٔ خروجی اجرای قبلی برنامه نمی‌ده.

bash

ترمینال میزبان آزمایشی با دسترسی مجاز

systemctl status 'academy-daily-marker.timer' --no-pager
systemctl cat 'academy-daily-marker.timer'
systemctl show 'academy-daily-marker.timer' -p FragmentPath -p DropInPaths -p ActiveState -p LastTriggerUSec -p NextElapseUSecRealtime
systemctl list-timers --all 'academy-daily-marker.timer'
timedatectl status
از کجا بفهمم درست انجام شده؟

علت نرسیدن یا دیررسیدن نوبت در تنظیمات مؤثر یا وضعیت ساعت روشن شده و هنوز زمان‌بندی تغییری نکرده.

همهٔ آموزش‌های آکادمی ↗