Невдалий запуск OpenVPN часто надходить як скарга на ресурси разом із доброзичливою порадою підвищити якийсь ліміт, і ця порада допомагає лише приблизно в половині випадків. За цими збоями стоять два не пов'язані лічильники. Один — ліміт ядра на кількість завдань, що належать реальному ідентифікатору користувача, і він успадковується під час запуску процесу. Другий — бюджет cgroup, який менеджер служб застосовує до юніта й усього, що він породжує. Прочитати ефективне значення кожного з них для юніта, який справді працює, дешевше, ніж здогад.

Два лічильники, два питання

LimitNPROC — це RLIMIT_NPROC під іменем systemd, а його одиниця обліку — реальний ідентифікатор користувача. Кожен процес, що належить цьому користувачу, рахується в ту саму суму, включно з роботою, яку розпочали інші юніти, сеанс входу або завдання планувальника. Тому ліміт не є стелею на службу, навіть коли він заданий усередині юніта служби, і служба, що породжує помічника для кожного клієнта, конкурує з усім іншим, що працює під цим обліковим записом. Зайняте завдання cron під тим самим користувачем може виштовхнути юніт VPN за лінію, яку він сам ніколи не перетинав.

TasksMax має іншого власника: контролер pids на cgroup юніта, який рахує завдання, а не процеси. Потоки — це завдання, тому демон, що виглядає однопотоковим, але має пул обробників, може витратити бюджет, який на перший погляд здається значно більшим за кількість його процесів. EffectiveTasksMax повідомляє, що насправді дозволяє ієрархія cgroup після того, як батьківські зрізи сказали своє.

Чому виняток для root змінює відповідь

Ядро не застосовує RLIMIT_NPROC до процесу, що несе CAP_SYS_RESOURCE або CAP_SYS_ADMIN, тому юніт, який працює як root без відмови від привілеїв, практично не обмежений цим налаштуванням. Саме цей єдиний виняток пояснює суперечливі поради, якими обмінюються люди: підвищення LimitNPROC виправляє розгортання, що відмовилося від привілеїв, і не робить геть нічого для того, що залишилося привілейованим. Перевірте User= і те, чи змінює демон особу після запуску, перш ніж приписувати цьому налаштуванню заслугу чи вину. Привілейований юніт, який згодом переходить на непривілейований обліковий запис, у момент переходу знову опиняється під лімітом, і це робить хронологію збою також вартою прочитання.

Стадія збою ще більше звужує це. Відмова під час підготовки виконання, помилка розгалуження, про яку повідомляє сам демон, і відхилене створення завдання всередині cgroup дають різні рядки journal, і лише на один із них відповідає більший бюджет.

Читайте ефективні значення, а не значення оболонки

ulimit -u інтерактивної оболонки описує цю оболонку. Він не описує, що менеджер служб передав демону, і нічого не каже про облік cgroup. Запитуйте натомість сам юніт, використовуючи ім'я екземпляра, яке існує на хості, а не приклад із посібника.

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

Усередині контейнера відповідь потребує ще двох читань, бо внутрішнє значення infinity описує лише погляд гостя. Запитайте у власника хоста власну стелю pids контейнера і підтвердьте, чи може внутрішній менеджер служб узагалі делегувати цей контролер. Вкладений юніт не може тримати бюджет, якого його батьківський cgroup ніколи не видавав.

Оцініть розмір виправлення і доведіть його під навантаженням

Беріть число з робочого навантаження, а не з тексту помилки. Порахуйте процеси, які демон запускає на кожного підключеного клієнта або на кожен виклик помічника, помножте на паралельність, яку розгортання має витримати, додайте запас на адміністративні сеанси, а потім порівняйте це число з двома ефективними значеннями. Локальне перевизначення drop-in залишає зміну придатною для перегляду і переживає оновлення пакетів, тоді як редагування постаченого файлу юніта приховує зміну, доки наступне оновлення не замінить його. Запишіть арифметику поруч із перевизначенням, бо наступній людині, яка підвищуватиме чи знижуватиме його, потрібні припущення, а не підсумкове число.

Зміни бюджету застосовуються під час виконання, тому саме перезавантаження не змінює розмір уже працюючих процесів. Перезапустіть юніт через його звичайний життєвий цикл, прийміть переривання сеансу і прочитайте лічильники знову на новому екземплярі.

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

Виправлення доводить перезапущений юніт, у якого кількість завдань залишається нижче обох стель, поки він несе передбачене навантаження, плюс journal, який більше не повідомляє про вичерпання завдань або розгалужень на тій самій стадії. Збережіть числа до і після разом зі зміною, щоб наступне розслідування починалося з вимірювання, а не з тієї самої поради. Якщо число досі сягає стелі, то арифметика була хибною, а не ліміт випадково замалий, і цю різницю варто знати перед наступним перезапуском.

Схожі статті