Нашата циклична проверка изчака 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. Стартирайте я като unit и проверете със
systemctl is-activeили я стартирайте сsystemd-run --wait. Няма грешен шаблон, който да объркате. - Всяко изчакване има краен срок (
timeout 3h) и посочва кога се отказва. Цикъл без краен срок се проваля незабележимо.
Factory Floor, комплектът, който управлява нашите машини, работи чрез systemd units: той никога не търси процес по командния ред. Циклите, които ни подведоха, бяха тези, които въведохме ръчно.