Passer 8192 sockets via l'activation de socket systemd - échoue avec E2BIG
J'essaye de demander à systemd de démarrer un démon et de lui transmettre 8 192 sockets d'écoute. J'ai un fichier .serviceand .socketqui fonctionne de manière fiable avec un nombre plus "normal" de sockets d'écoute, comme ceci:
# a-daemon.socket
[Unit]
Description=A Daemon (sockets)
After=network.target
[Socket]
Accept=no
ListenStream=8192
# a-daemon.service
[Unit]
Description=A Daemon
After=network.target
Requires=a-daemon.socket
[Install]
WantedBy=multi-user.target
[Service]
Type=notify
ExecStart=/usr/local/sbin/a-daemon
Mais si je change a-daemon.socketpour une version avec 8 192 ListenStreamlignes, une pour chaque port TCP de 8192 à 16383 inclus, alors le démon ne démarrera plus. L' unité de prise peut être démarrée très bien, mais l' unité de service échoue; le seul message d'erreur que je reçois est
systemd[17563]: a-daemon.service: Failed to execute command: Argument list too long
systemd[17563]: a-daemon.service: Failed at step EXEC spawning /usr/local/sbin/a-daemon: Argument list too long
Si je comprends bien, cela ne peut pas être un problème avec la liste d'arguments , car systemd ne met pas les numéros de socket fd sur la ligne de commande du démon ou quelque chose comme ça. J'ai deviné que c'était plutôt un problème avec une limite sur le nombre de fichiers ouverts simultanés, donc j'ai mis DefaultLimitNOFILE=32768en place /etc/systemd/system.confet un paramètre équivalent dans /etc/security/limits.confet redémarré. Pas de changement. Ensuite, j'ai mis ExecStartPre=/usr/sbin/prlimit -nle fichier .service et essayé de le redémarrer, ce qui a confirmé que l'augmentation de la limite avait pris effet:
prlimit[18134]: RESOURCE DESCRIPTION SOFT HARD UNITS
prlimit[18134]: NOFILE max number of open files 32768 32768 files
Mais le service échoue toujours, de la même manière. Et maintenant, je suis à court d'idées. Pouvez-vous suggérer quelque chose que je pourrais essayer de faire pour que cela fonctionne?
(Je suis conscient que l'écoute sur 8 192 ports TCP consécutifs est une chose étrange à faire. S'il vous plaît, croyez-moi sur parole que j'ai une bonne raison que je ne peux pas partager.)
Réponses
Sur les yeux fixés sur les pages de manuel systemd un peu plus, je me suis aperçu qu'il y a quelque chose systemd met dans la argvzone dont la taille est proportionnelle au nombre de prises d' écoute:
sd_listen_fds_with_names()est similairesd_listen_fds(), mais renvoie également facultativement un tableau de chaînes avec des noms d'identification pour les descripteurs de fichiers passés, si cela est disponible et que le paramètre names est non NULL. Ces informations sont lues à partir de la$LISTEN_FDNAMESvariable [environnement], qui peut contenir une liste de noms séparés par deux-points. Pour les services activés par socket, ces noms peuvent être configurés avec leFileDescriptorName=paramètre des fichiers d'unité de socket, voirsystemd.socket(5)pour plus de détails.
(gras - je souligne) Ce que cela signifie, c'est que si vous avez 8192 ListenStreamentrées dans un fichier d'unité de socket, systemd essaiera de placer LISTEN_FDNAMES=[name]:[name]:..., avec 8192 répétitions, où nameest le FileDescriptorNameparamètre, dans l'environnement du service. FileDescriptorNamepar défaut, le nom de base complet du fichier d'unité de socket. Cela peut facilement dépasser la limite assez basse et fixe de Linux sur la longueur d'une seule variable d'environnement nom + valeur ( MAX_ARG_STRLEN, généralement 128k), et donc provoquer un execve(2)échec avec E2BIG.
Pour un démon qui ne se soucie pas des noms, le remède est de mettre
UnsetEnvironment=LISTEN_FDNAMES
dans le fichier de service ( [Service]section). Cela élimine la variable d'environnement problématique et rend le noyau heureux.
Je ne sais pas ce que vous faites si vous avez réellement besoin des noms fd pour quelque chose et que vous atteignez cette limite.