Si c'est important, c'est systemd qui devrait le démarrer
L'affirmation Tout processus de longue durée sur un hôte Linux de production devrait être une unité systemd. Pas pm2, pas une session screen détachée au printemps, pas un nohup & d...
L'affirmation
Tout processus de longue durée sur un hôte Linux de production devrait être une unité systemd. Pas pm2, pas une session screen détachée au printemps, pas un nohup & dans un script de déploiement. Ces outils répondent à « comment garder ça en vie après ma déconnexion » et échouent silencieusement aux questions qui comptent : qu'est-ce qui le démarre après un redémarrage, qu'est-ce qui le relance après un plantage, où vont ses journaux, et qu'est-ce qui l'empêche d'emporter l'hôte entier avec lui. systemd répond aux quatre, il est déjà installé, et cela coûte un fichier de vingt lignes.
Le fichier d'unité
[Unit]
Description=API des commandes
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=orders
WorkingDirectory=/srv/orders
ExecStart=/srv/orders/bin/server
Restart=always
RestartSec=2
Environment=PORT=8080
EnvironmentFile=/etc/orders/env
MemoryMax=1500M
TasksMax=256
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/srv/orders/data
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now orders
Passons chaque bloc en revue, car chaque ligne remplace un incident.
Une politique de relance qui distingue plantage et boucle de plantage
Restart=always avec RestartSec=2 ramène un processus planté en deux secondes. Ajoutez la limite de cadence qui sépare l'échec passager du déploiement cassé :
StartLimitIntervalSec=60
StartLimitBurst=5
Cinq échecs en une minute et systemd cesse de réessayer, marquant l'unité en échec — ce que votre surveillance devrait attraper via systemctl is-failed. Sans cette limite, un binaire qui plante au démarrage redémarre sans fin, générant des gigaoctets de journaux et une courbe de processeur qui ressemble à du service rendu.
Les limites de ressources : un mauvais service ou un mauvais hôte
MemoryMax=1500M signifie qu'une fuite dans ce service fait tuer ce service — puis relancer proprement par la politique ci-dessus — au lieu d'inviter le tueur de processus du noyau à choisir une victime, ce qu'il fait sans égard pour ce que vous jugez important. Sur un hôte de 4 Go portant une application et une base de données, une fuite applicative non plafonnée se termine généralement par la mort de la base. TasksMax joue le même rôle contre une explosion de fils ou de processus.
Les lignes de cloisonnement sont du durcissement gratuit
ProtectSystem=strict rend tout le système de fichiers en lecture seule pour le processus, hormis les chemins listés dans ReadWritePaths. ProtectHome, PrivateTmp et NoNewPrivileges ferment des voies courantes d'escalade. Cela transforme « l'attaquant exploite l'application » : au lieu de posséder l'hôte, il peut écrire dans un seul répertoire de données — un mardi sensiblement différent. Vérifiez votre position :
systemd-analyze security orders.service
La note est grossière, mais la liste détaillée est réellement utile, et la plupart des services peuvent en adopter la moitié sans changer de code.
Des journaux sans pile de journalisation
Tout ce que le processus écrit sur la sortie standard aboutit dans le journal, horodaté, limité en cadence et pivoté sans configuration :
journalctl -u orders -S -1h # la derniere heure
journalctl -u orders -p err -S today # erreurs seulement
journalctl -u orders -o json | jq . # structure, si vous journalisez en JSON
Pour un hôte unique ou un petit parc, cela remplace entièrement un expéditeur de journaux. Fixez un plafond dans /etc/systemd/journald.conf avec SystemMaxUse=2G et n'y pensez plus.
L'arrêt en douceur fait partie du contrat
À l'arrêt comme au déploiement, systemd envoie SIGTERM, attend, puis envoie SIGKILL. L'attente par défaut est de 90 secondes ; fixez-la sciemment avec TimeoutStopSec=30 et faites en sorte que l'application utilise la fenêtre — cesser d'accepter des connexions, terminer les requêtes en vol, fermer le bassin de base de données, sortir avec le code zéro. Un processus qui ignore SIGTERM se fait tuer en pleine transaction à chaque déploiement, et les enregistrements à moitié écrits refont surface des semaines plus tard sous forme de billets de soutien mystérieux. S'il faut plus de temps pour une purge finale, ExecStop= peut lancer d'abord un script d'écoulement explicite. Testez sans finesse : systemctl stop orders sous charge, puis cherchez dans les journaux les requêtes mortes inachevées.
Les tâches planifiées y ont leur place aussi
Une unité minuterie remplace l'entrée cron, et le couplage vous donne le même journal, les mêmes plafonds de ressources et deux propriétés que cron n'a pas : Persistent=true exécute une tâche manquée pendant que l'hôte était éteint, et un service encore en cours à l'arrivée du tic suivant n'est pas démarré deux fois.
[Timer]
OnCalendar=*-*-* 02:30
Persistent=true
RandomizedDelaySec=300
Le délai aléatoire compte sur un parc : vingt hôtes frappant la base exactement à 2 h 30, c'est une pointe auto-infligée.
Où les autres outils gardent leur place
pm2 demeure raisonnable comme commodité de développement, et dans une plateforme de conteneurs c'est l'orchestrateur qui joue ce rôle, systemd restant à l'écart. L'affirmation est plus étroite et plus ferme : sur un hôte que vous exploitez, quand le processus compte, le superviseur est le système d'initialisation que vous avez déjà. Si la réponse à « qu'est-ce qui redémarre ceci après une coupure de courant » implique qu'un humain se souvienne de quelque chose, c'est la panne que vous avez planifiée sans en choisir la date.