Naša čakacia slučka čakala 35 hodín sama na seba: pgrep -f a pkill -f cez SSH
Prevádzka
31. októbra sme spustili malú slučku cez SSH: počkať, kým na inom počítači neskončí prekladová úloha, a potom spustiť nadväznú úlohu. Prekladová úloha skončila tej noci. Keď sme sa pozreli znova o dva dni neskôr, slučka čakala už 35 hodín a nadväzná úloha sa nikdy nespustila.
Čo slučka robila
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f porovnáva vzor s celým príkazovým riadkom každého procesu. SSH spustí zadaný príkaz ako bash -c 'while pgrep -f "i18n_job.py example.com" …' a tento príkazový riadok obsahuje vzor. pgrep vynechá svoj vlastný proces z výsledkov, ale nie shell, ktorý ho spustil. Takže pokiaľ bola slučka nažive, našla zhodu: seba samu.
Jej dvojča: pkill -f zabije shell, v ktorom beží
To isté sa stane s pkill -f, s horšími následkami:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
Vlastný príkazový riadok shellu obsahuje vzor, takže pkill zabije shell. Pracovník sa zastaví a už sa nikdy nespustí a SSH ukončí prácu s kódom 255 bez chybovej správy. Vyzerá to ako problém s úvodzovkami, čo sme si zpočátku mysleli.
Medzi septembrom a koncom októbra sme aspoň tucetkrát narazili na jedno alebo druhé: reštarty, ktoré sa nikdy neudiali, slučka, ktorá držala kontrolu tri a pol hodiny, ďalšia, ktorá držala prekladový front 50 minút, a táto.
Čo robíme teraz
- Zátvorka vo vzore:
pgrep -f "[i]18n_job.py example.com". Ako regulárny výraz[i]zodpovedá písmenu i, takže úloha stále zodpovedá; príkazový riadok shellu obsahuje znaky[i], ktoré výraz nezodpovedá. Pomáha to len vtedy, ak sa čistý vzor na tom istom riadku nevyskytuje nikde inde: príkaz na reštart neskôr na tom istom riadku opäť zodpovedá. - Nájsť, zastaviť a spustiť v samostatných SSH volaniach. Jedno volanie vypíše procesy pomocou
pgrep -af, ďalšie zabije PID, ktoré vypísalo, tretie spustí nový proces. - Čakať na PID, nie na vzor:
tail --pid=1234 -f /dev/null, alebowhile kill -0 1234; do sleep 30; done. - Ešte lepšie: nech o úlohu sa stará systemd. Spustite ju ako unit a opýtajte sa
systemctl is-active, alebo ju spustite pomocousystemd-run --wait. Neexistuje žiadny vzor, ktorý by ste mohli pokaziť. - Každé čakanie má termín (
timeout 3h) a hovorí, kedy to vzdá. Slučka bez termínu zlyhá potichu.
Factory Floor, balík, ktorý spúšťa naše stroje, funguje cez systemd unit: nikdy nehľadá proces podľa príkazového riadku. Slučky, ktoré nás dostali, boli tie, ktoré sme zadali ručne.