Nieudane uruchomienie OpenVPN często przychodzi jako skarga na zasoby wraz z życzliwą sugestią podniesienia jakiegoś limitu, a sugestia działa tylko w około połowie przypadków. Za tymi niepowodzeniami stoją dwa niepowiązane liczniki. Jeden to limit jądra na liczbę zadań należących do rzeczywistego identyfikatora użytkownika, dziedziczony przy uruchamianiu procesu. Drugi to budżet cgroup, który menedżer usług stosuje do jednostki i wszystkiego, co ona rozwidla. Odczytanie wartości efektywnej każdego z nich, dla jednostki, która naprawdę działa, kosztuje mniej niż zgadywanie.

Dwa liczniki, dwa pytania

LimitNPROC to RLIMIT_NPROC pod nazwą systemd, a jego jednostką rozliczeniową jest rzeczywisty identyfikator użytkownika. Każdy proces należący do tego użytkownika liczy się do tej samej sumy, w tym praca rozpoczęta przez inne jednostki, sesję logowania lub zadanie harmonogramu. Limit nie jest więc pułapem na usługę, nawet gdy jest ustawiony wewnątrz jednostki usługi, a usługa, która rozwidla pomocnika dla każdego klienta, konkuruje ze wszystkim innym działającym na tym koncie. Zajęte zadanie cron tego samego użytkownika może wypchnąć jednostkę VPN za linię, której ona sama nigdy nie przekroczyła.

TasksMax ma innego właściciela: kontroler pids na cgroup jednostki, który liczy zadania, a nie procesy. Wątki są zadaniami, więc demon wyglądający na jednowątkowy, ale mający pulę robotników, może zużyć budżet, który na pierwszy rzut oka wygląda znacznie większy niż liczba jego procesów. EffectiveTasksMax podaje, na co hierarchia cgroup naprawdę pozwala po tym, jak nadrzędne wycinki powiedziały swoje.

Dlaczego wyjątek root zmienia odpowiedź

Jądro nie stosuje RLIMIT_NPROC do procesu noszącego CAP_SYS_RESOURCE lub CAP_SYS_ADMIN, dlatego jednostka działająca jako root bez zrzekania się uprawnień jest w praktyce nieograniczona tym ustawieniem. Ten jeden wyjątek wyjaśnia sprzeczne porady, którymi ludzie się wymieniają: podniesienie LimitNPROC naprawia wdrożenie, które zrzekło się uprawnień, i nie robi absolutnie nic temu, które pozostało uprzywilejowane. Zanim przypiszesz temu ustawieniu zasługę lub winę, sprawdź User= i to, czy demon zmienia tożsamość po starcie. Uprzywilejowana jednostka, która później przechodzi na konto bez uprawnień, w momencie przejścia wraca pod limit, co sprawia, że os czasu awarii też warto przeczytać.

Etap awarii zawęża to jeszcze bardziej. Odmowa podczas przygotowania wykonania, błąd rozwidlenia zgłoszony przez sam demon i odrzucone utworzenie zadania w cgroup dają różne linie journala, a tylko na jedną z nich odpowiada większy budżet.

Czytaj wartości efektywne, a nie te z powłoki

ulimit -u interaktywnej powłoki opisuje tę powłokę. Nie opisuje, co menedżer usług przekazał demonowi, i nic nie mówi o rozliczaniu cgroup. Zapytaj zamiast tego jednostkę, używając nazwy instancji, która istnieje na hoście, a nie przykładu z poradnika.

systemctl show [email protected] -p User -p LimitNPROC -p TasksCurrent -p TasksMax -p EffectiveTasksMax
cat /proc/$(systemctl show -p MainPID --value [email protected])/limits | grep -i processes

Wewnątrz kontenera odpowiedź wymaga jeszcze dwóch odczytów, bo wewnętrzna wartość infinity opisuje tylko widok gościa. Zapytaj właściciela hosta o własny pułap pids kontenera i potwierdź, czy wewnętrzny menedżer usług może w ogóle delegować ten kontroler. Zagnieżdżona jednostka nie może utrzymać budżetu, którego jej nadrzędny cgroup nigdy nie przyznał.

Określ rozmiar poprawki i udowodnij ją pod obciążeniem

Wybierz liczbę z obciążenia, a nie z tekstu błędu. Policz procesy, które demon uruchamia na każdego połączonego klienta lub każde wywołanie pomocnika, pomnóż przez współbieżność, którą wdrożenie ma udźwignąć, dodaj margines na sesje administracyjne, a potem porównaj tę liczbę z dwiema wartościami efektywnymi. Lokalne nadpisanie drop-in utrzymuje zmianę weryfikowalną i przeżywa aktualizacje pakietów, podczas gdy edytowanie dostarczonego pliku jednostki ukrywa zmianę, dopóki następna aktualizacja go nie zastąpi. Zapisz arytmetykę obok nadpisania, bo następna osoba, która je podniesie lub obniży, potrzebuje założeń, a nie ostatecznej liczby.

Zmiany budżetu są stosowane przy wykonaniu, więc samo przeładowanie nie zmienia rozmiaru już działających procesów. Zrestartuj jednostkę przez jej normalny cykl życia, zaakceptuj przerwanie sesji i odczytaj liczniki ponownie na nowej instancji.

sudo systemctl daemon-reload
sudo systemctl restart [email protected]
systemctl show [email protected] -p TasksCurrent -p EffectiveTasksMax
sudo journalctl -u [email protected] -b --no-pager | tail -n 20

Poprawkę potwierdza zrestartowana jednostka, której liczba zadań pozostaje pod obiema pułapami, gdy dźwiga zamierzone obciążenie, plus journal, który na tym samym etapie nie zgłasza już wyczerpania zadań ani rozwidleń. Zachowaj liczby przed i po wraz ze zmianą, aby następne dochodzenie zaczęło się od pomiaru, a nie od tej samej porady. Jeśli liczba nadal osiąga pułap, to arytmetyka była błędna, a nie limit przypadkiem za mały, i tę różnicę warto znać przed następnym restartem.

Powiązane artykuły