LimitNPROC face à TasksMax : ce que compte une unité OpenVPN
LimitNPROC compte les processus d’un utilisateur sur tout le système ; TasksMax borne le cgroup d’une unité. Lisez les valeurs effectives avant de relever l’une ou l’autre.
Un démarrage raté d’OpenVPN arrive souvent sous la forme d’une plainte sur les ressources accompagnée d’une suggestion bien intentionnée de relever une limite, et cette suggestion ne fonctionne qu’environ une fois sur deux. Derrière ces échecs se trouvent deux compteurs sans rapport. L’un est une limite du noyau sur le nombre de tâches possédées par un identifiant utilisateur réel, héritée lors de l’exécution du processus. L’autre est un budget cgroup que le gestionnaire de services applique à une unité et à tout ce qu’elle bifurque. Lire la valeur effective de chacun, pour l’unité qui tourne réellement, coûte moins qu’une supposition.
Deux compteurs, deux questions
LimitNPROC est RLIMIT_NPROC sous un nom systemd, et son unité de comptabilité est l’identifiant utilisateur réel. Chaque processus possédé par cet utilisateur compte dans le même total, y compris le travail lancé par d’autres unités, par une session de connexion ou par une tâche du planificateur. La limite n’est donc pas un plafond par service même lorsqu’elle est définie dans une unité de service, et un service qui bifurque un auxiliaire pour chaque client entre en concurrence avec tout le reste exécuté sous ce compte. Une tâche cron chargée sous le même utilisateur peut pousser une unité VPN au-delà d’une ligne qu’elle n’a jamais franchie seule.
TasksMax a un autre propriétaire : le contrôleur pids du cgroup de l’unité, qui compte des tâches plutôt que des processus. Les threads sont des tâches, si bien qu’un démon d’apparence mono-thread mais doté d’un pool de workers peut consommer un budget qui, à première vue, semble bien supérieur à son nombre de processus. EffectiveTasksMax indique ce que la hiérarchie cgroup autorise réellement une fois que les tranches parentes ont dit leur mot.
Pourquoi l’exception root change la réponse
Le noyau n’applique pas RLIMIT_NPROC à un processus qui porte CAP_SYS_RESOURCE ou CAP_SYS_ADMIN, ce qui explique qu’une unité exécutée en root sans abandonner ses privilèges soit en pratique illimitée par ce réglage. Cette unique exception explique les conseils contradictoires que les gens échangent : relever LimitNPROC répare un déploiement qui a abandonné ses privilèges et ne fait absolument rien pour celui qui les a conservés. Vérifiez User= et si le démon change d’identité après le démarrage avant d’attribuer un mérite ou un blâme à ce réglage. Une unité privilégiée qui bascule ensuite vers un compte non privilégié retombe sous la limite au moment du basculement, ce qui rend la chronologie de l’échec digne d’être lue aussi.
L’étape de l’échec resserre encore. Un refus pendant la préparation de l’exécution, une erreur de bifurcation signalée par le démon lui-même et une création de tâche rejetée dans un cgroup produisent des lignes de journal différentes, et une seule d’entre elles se résout par un budget plus grand.
Lisez les valeurs effectives, pas celles du shell
ulimit -u d’un shell interactif décrit ce shell. Il ne décrit pas ce que le gestionnaire de services a transmis au démon et ne dit rien de la comptabilité cgroup. Interrogez plutôt l’unité, en utilisant le nom d’instance qui existe sur l’hôte et non un exemple tiré d’un guide.
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
Dans un conteneur, la réponse demande deux lectures de plus, car une valeur interne d’infinity ne décrit que la vue de l’invité. Demandez au propriétaire de l’hôte le plafond pids propre au conteneur, et vérifiez si le gestionnaire de services interne peut déléguer ce contrôleur. Une unité imbriquée ne peut pas tenir un budget que son cgroup parent n’a jamais accordé.
Dimensionnez le correctif et prouvez-le sous charge
Choisissez un nombre à partir de la charge de travail, pas du texte de l’erreur. Comptez les processus que le démon lance par client connecté ou par appel d’auxiliaire, multipliez par la concurrence que le déploiement est censé supporter, ajoutez une marge pour les sessions administratives, puis comparez ce chiffre aux deux valeurs effectives. Une substitution locale de type drop-in garde le changement relisible et survit aux mises à jour de paquets, alors que modifier un fichier d’unité livré cache la modification jusqu’à ce que la prochaine mise à jour le remplace. Notez l’arithmétique à côté de la substitution, car la prochaine personne qui la relèvera ou l’abaissera a besoin des hypothèses, pas du nombre final.
Les changements de budget s’appliquent à l’exécution, donc un simple rechargement ne redimensionne pas les processus déjà en cours. Redémarrez l’unité via son cycle de vie normal, en acceptant l’interruption de session, et relisez les compteurs sur la nouvelle instance.
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
Ce qui prouve la réparation, c’est une unité redémarrée dont le nombre de tâches reste sous les deux plafonds tout en portant la charge prévue, plus un journal qui ne signale plus d’épuisement de tâches ou de bifurcations au même stade. Conservez les chiffres avant et après avec le changement pour que la prochaine enquête parte d’une mesure et non du même conseil. Si le nombre atteint encore le plafond, l’arithmétique était fausse et non la limite trop petite par accident, et cette différence vaut la peine d’être connue avant le prochain redémarrage.