LimitNPROC versus TasksMax: ce numără o unitate OpenVPN
LimitNPROC numără procesele unui utilizator în tot sistemul; TasksMax limitează cgroupul unei unități. Citiți valorile efective înainte de a ridica oricare dintre ele.
O pornire eșuată a OpenVPN vine adesea ca o plângere privind resursele împreună cu o sugestie bine intenționată de a ridica o limită, iar sugestia funcționează doar în aproximativ jumătate dintre cazuri. În spatele acestor eșecuri stau doi contoare fără legătură. Unul este o limită a nucleului asupra numărului de sarcini deținute de un identificator real de utilizator, moștenită la execuția procesului. Celălalt este un buget de cgroup pe care managerul de servicii îl aplică unei unități și tot ce aceasta ramifică. Citirea valorii efective a fiecăruia, pentru unitatea care rulează cu adevărat, costă mai puțin decât o presupunere.
Două contoare, două întrebări
LimitNPROC este RLIMIT_NPROC sub un nume systemd, iar unitatea sa de contabilizare este identificatorul real de utilizator. Fiecare proces deținut de acel utilizator contează în aceeași sumă, inclusiv munca începută de alte unități, de o sesiune de autentificare sau de o sarcină a planificatorului. Limita nu este, așadar, un plafon pe serviciu chiar și atunci când este stabilită în interiorul unei unități de serviciu, iar un serviciu care ramifică un ajutor pentru fiecare client concurează cu tot restul care rulează sub acel cont. O sarcină cron ocupată sub același utilizator poate împinge o unitate VPN dincolo de o linie pe care ea singură nu a trecut-o niciodată.
TasksMax are alt proprietar: controlerul pids de pe cgroupul unității, care numără sarcini în loc de procese. Firele de execuție sunt sarcini, așa că un demon care pare cu un singur fir, dar are un grup de lucrători, poate consuma un buget care la prima vedere pare mult mai mare decât numărul său de procese. EffectiveTasksMax raportează ce permite de fapt ierarhia cgroup după ce felioanele părinte și-au spus cuvântul.
De ce excepția root schimbă răspunsul
Nucleul nu aplică RLIMIT_NPROC unui proces care poartă CAP_SYS_RESOURCE sau CAP_SYS_ADMIN, motiv pentru care o unitate care rulează ca root fără a renunța la privilegii este efectiv nemărginită de acea setare. Această singură excepție explică sfaturile contradictorii pe care oamenii le schimbă: ridicarea LimitNPROC repară o implementare care a renunțat la privilegii și nu face absolut nimic pentru una care a rămas privilegiată. Verificați User= și dacă demonul își schimbă identitatea după pornire înainte de a atribui acelei setări meritul sau vina. O unitate privilegiată care trece ulterior la un cont fără privilegii revine sub limită în momentul trecerii, ceea ce face ca și cronologia eșecului să merite citită.
Etapa eșecului restrânge și mai mult. O respingere în timpul pregătirii execuției, o eroare de ramificare raportată de demonul însuși și o creare de sarcină respinsă în interiorul unui cgroup produc linii de journal diferite, iar doar una dintre ele primește răspuns printr-un buget mai mare.
Citiți valorile efective, nu pe cele ale shellului
ulimit -u unui shell interactiv descrie acel shell. Nu descrie ce i-a transmis managerul de servicii demonului și nu spune nimic despre contabilizarea cgroup. Întrebați în schimb unitatea, folosind numele de instanță care există pe gazdă și nu un exemplu dintr-un ghid.
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
În interiorul unui container răspunsul are nevoie de încă două citiri, pentru că o valoare internă de infinity descrie doar viziunea oaspetelui. Cereți proprietarului gazdei plafonul pids propriu al containerului și confirmați dacă managerul de servicii intern poate delega deloc acel controler. O unitate imbricată nu poate ține un buget pe care cgroupul ei părinte nu l-a acordat niciodată.
Dimensionați corectura și demonstrați-o sub sarcină
Alegeți un număr din sarcina de lucru, nu din textul erorii. Numărați procesele pe care demonul le pornește pentru fiecare client conectat sau fiecare apel de ajutor, înmulțiți cu concurența pe care implementarea trebuie să o suporte, adăugați o marjă pentru sesiunile administrative și apoi comparați acel număr cu cele două valori efective. O suprascriere locală de tip drop-in păstrează modificarea verificabilă și supraviețuiește actualizărilor de pachete, în timp ce editarea unui fișier de unitate livrat ascunde modificarea până când următoarea actualizare îl înlocuiește. Scrieți aritmetica lângă suprascriere, pentru că următoarea persoană care o va ridica sau coborî are nevoie de ipoteze, nu de numărul final.
Modificările de buget se aplică la execuție, așa că o simplă reîncărcare nu redimensionează procesele care rulează deja. Reporniți unitatea prin ciclul ei normal de viață, acceptați întreruperea sesiunii și citiți contoarele din nou pe instanța nouă.
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
Ceea ce dovedește reparația este o unitate repornită al cărei număr de sarcini stă sub ambele plafoane în timp ce duce sarcina prevăzută, plus un journal care nu mai raportează epuizarea sarcinilor sau a ramificărilor în aceeași etapă. Păstrați numerele dinainte și de după împreună cu modificarea, astfel încât următoarea investigație să înceapă de la o măsurătoare și nu de la același sfat. Dacă numărul atinge în continuare plafonul, aritmetica a fost greșită, nu limita prea mică din întâmplare, iar acea diferență merită cunoscută înainte de următoarea repornire.