Nasza pętla czekała 35 godzin sama na siebie: pgrep -f i pkill -f przez SSH
Operacje
31 października uruchomiliśmy małą pętlę przez SSH: czekaj, aż zadanie tłumaczenia na innej maszynie się zakończy, a następnie uruchom zadanie następcze. Zadanie tłumaczenia zakończyło się tej samej nocy. Gdy zajrzeliśmy ponownie dwa dni później, pętla czekała już 35 godzin, a zadanie następcze nigdy się nie uruchomiło.
Czym była ta pętla
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f dopasowuje wzorzec do pełnej linii poleceń każdego procesu. SSH uruchamia cytowane polecenie jako bash -c 'while pgrep -f "i18n_job.py example.com" …', a ta linia poleceń zawiera ten wzorzec. pgrep pomija własny proces w wynikach, ale nie powłokę, która go uruchomiła. Zatem dopóki pętla żyła, znajdowała dopasowanie: samą siebie.
Jego bliźniak: pkill -f zabija powłokę, w której działa
To samo dzieje się z pkill -f, ze gorszymi skutkami:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
Własna linia poleceń powłoki zawiera wzorzec, więc pkill zabija powłokę. Worker zostaje zatrzymany i nigdy nie zostaje uruchomiony ponownie, a SSH kończy działanie z kodem 255 bez komunikatu o błędzie. Wygląda to jak problem z cytowaniem, za co wzięliśmy to za pierwszym razem.
Od września do końca października wpadaliśmy w jedno lub drugie przynajmniej tuzin razy: restarty, które nigdy nie nastąpiły, pętla, która trzymała sprawdzenie przez trzy i pół godziny, inna, która trzymała kolejkę tłumaczeń przez 50 minut, i ta tutaj.
Co robimy teraz
- Nawias w wzorcu:
pgrep -f "[i]18n_job.py example.com". Jako wyrażenie regularne[i]dopasowuje literę i, więc zadanie nadal pasuje; linia poleceń powłoki zawiera znaki[i], których wyrażenie nie dopasowuje. Pomaga to tylko wtedy, gdy zwykły wzorzec nie pojawia się nigdzie indziej w tej linii: polecenie restartu później w tej samej linii dopasuje się ponownie. - Znajdowanie, zatrzymywanie i uruchamianie w osobnych wywołaniach SSH. Jedno wywołanie wyświetla listę procesów za pomocą
pgrep -af, następne zabija wypisane PID-y, trzecie uruchamia nowy proces. - Czekaj na PID, a nie na wzorzec:
tail --pid=1234 -f /dev/nulllubwhile kill -0 1234; do sleep 30; done. - Jeszcze lepiej: niech systemd przejmie zadanie. Uruchom je jako jednostkę i zapytaj
systemctl is-activealbo uruchom za pomocąsystemd-run --wait. Nie ma tu wzorca, który można by źle zastosować. - Każde oczekiwanie ma termin (
timeout 3h) i informuje, kiedy się poddaje. Pętla bez terminu kończy działanie po cichu.
Factory Floor, zestaw, który obsługuje nasze maszyny, działa w oparciu o jednostki systemd: nigdy nie szuka procesu po jego linii poleceń. Pętle, które nas dopadły, to te, które wpisaliśmy ręcznie.