حلقهٔ انتظار ما 35 ساعت منتظر خودش ماند: pgrep -f و pkill -f روی SSH
عملیات
در 31 اکتبر یک حلقهٔ کوچک روی SSH راهاندازی کردیم: صبر کن تا یک کار ترجمه روی ماشین دیگری تمام شود، سپس یک کار پیگیری را اجرا کن. کار ترجمه همان شب تمام شد. وقتی دو روز بعد دوباره نگاه کردیم، حلقه 35 ساعت بود که منتظر مانده بود و کار پیگیری هرگز اجرا نشده بود.
حلقه چه بود
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f یک الگو را با خط فرمان کامل هر فرایند تطبیق میدهد. SSH دستور نقلقولشده را بهصورت bash -c 'while pgrep -f "i18n_job.py example.com" …' اجرا میکند و آن خط فرمان شامل الگو است. pgrep فرایند خودش را از نتایج حذف میکند، اما نه شلی را که آن را راهاندازی کرده است. بنابراین تا زمانی که حلقه زنده بود، یک تطابق پیدا میکرد: خودش.
همزادش: pkill -f شلی را که در آن اجرا میشود میکشد
همین اتفاق با pkill -f هم میافتد، با نتایج بدتر:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
خط فرمان خود شل شامل الگو است، بنابراین pkill شل را میکشد. کارگر متوقف میشود و هرگز دوباره راهاندازی نمیشود، و SSH با کد خروج 255 بدون پیام خطا خارج میشود. شبیه یک مشکل نقلقول به نظر میرسد، که همان چیزی بود که ما بار اول فکر کردیم.
بین سپتامبر تا پایان اکتبر حداقل یکدوازده بار به یکی از این دو حالت افتادیم: راهاندازی مجددهایی که هرگز رخ ندادند، یک حلقه که یک بررسی را سه ساعت و نیم نگه داشت، دیگری که صف ترجمه را 50 دقیقه نگه داشت، و این یکی.
اکنون چه کار میکنیم
- یک براکت در الگو:
pgrep -f "[i]18n_job.py example.com". بهعنوان یک عبارت منظم،[i]حرف i را تطبیق میدهد، بنابراین کار همچنان تطابق مییابد؛ خط فرمان شل شامل کاراکترهای[i]است که عبارت آنها را تطبیق نمیدهد. این فقط در صورتی کمک میکند که الگوی ساده در هیچ جای دیگر آن خط وجود نداشته باشد: یک دستور راهاندازی مجدد در ادامه همان خط دوباره تطابق مییابد. - پیدا کردن، توقف و راهاندازی در فراخوانیهای جداگانه SSH. یک فراخوانی فرایندها را با
pgrep -afفهرست میکند، فراخوانی بعدی PIDهای چاپشده را میکشد، و سومی فرایند جدید را راهاندازی میکند. - روی یک PID صبر کنید، نه یک الگو:
tail --pid=1234 -f /dev/null، یاwhile kill -0 1234; do sleep 30; done. - بهتر است که systemd خود کار را بر عهده بگیرد. آن را بهصورت یک واحد اجرا کنید و وضعیت آن را با
systemctl is-activeبررسی کنید، یا باsystemd-run --waitآن را راهاندازی کنید. هیچ الگویی برای اشتباه گرفتن وجود ندارد. - هر انتظار باید یک مهلت زمانی داشته باشد (
timeout 3h) و زمان انصراف خود را اعلام کند. یک حلقه بدون مهلت زمانی، بیصدا شکست میخورد.
Factory Floor، کیتی که ماشینهای ما را اجرا میکند، از طریق واحدهای systemd کار میکند: هرگز به دنبال فرآیندی بر اساس خط فرمان آن نمیگردد. حلقههایی که ما را به دردسر انداختند، همانهایی بودند که دستی تایپ کرده بودیم.