Les plates-formes telles que Kubernetes, Nomad ou toute plate-forme en tant que service (Paas) hébergée dans le cloud offrent une variété de fonctionnalités puissantes. De la mise à l'échelle des charges de travail à la gestion des secrets en passant par les stratégies de déploiement, ces orchestrateurs de charges de travail sont optimisés pour aider à faire évoluer l'infrastructure de différentes manières.
Mais les opérateurs doivent-ils toujours payer le prix d’une évolutivité maximale ? Parfois, le coût de la complexité et de l’abstraction dépasse leurs avantages. De nombreux constructeurs en viennent plutôt à s’appuyer sur des architectures de déploiement radicalement simples pour faciliter la gestion. Deux serveurs privés virtuels derrière un équilibreur de charge constituent une pile considérablement plus simple à gérer par rapport à un cluster tentaculaire de microserveurs répartis sur une flotte d'hôtes de conteneurs. Cela peut commencer à porter ses fruits lorsqu’il y a moins de pièces mobiles à déboguer lorsque des problèmes surviennent ou à mettre à niveau lorsque vient le temps de les maintenir.
La base de nombreuses distributions Linux modernes est systemd , et elle est dotée d'un ensemble solide de fonctionnalités souvent comparables aux orchestrateurs de conteneurs ou aux systèmes PaaS. Dans cet article, nous explorerons comment vous pouvez exploiter les dernières fonctionnalités de systemd pour acquérir de nombreuses capacités de ces autres grands systèmes sans les problèmes de gestion et dynamiser n'importe quel serveur Linux ordinaire pour en faire une plate-forme d'applications très performante.
Sur un seul hôte, l'écriture d'un fichier systemd .service
est un moyen idéal pour exécuter un processus géré. La plupart du temps, vous n'avez même pas besoin de modifier l'application : systemd prend en charge une variété de types de services différents et peut s'adapter en conséquence.
Par exemple, considérons ce simple .service
qui définit comment exécuter un service Web simple :
[Unit] Description=a simple web service [Service] ExecStart=/usr/bin/python3 -m http.server 8080
N'oubliez pas les valeurs par défaut des services systemd : ExecStart=
doit être un chemin absolu, les processus ne doivent pas passer en arrière-plan et vous devrez peut-être définir les variables d'environnement requises avec l'option Environment=
.
Lorsqu'il est placé dans un fichier comme /etc/systemd/system/webapp.service
, cela crée un service que vous pouvez contrôler avec systemctl
:
systemctl start webapp
démarrera le processus.systemctl status webapp
affichera si le service est en cours d'exécution, sa disponibilité et la sortie de stderr
et stdout
, ainsi que l'ID du processus et d'autres informations.systemctl stop webapp
mettra fin au service.
De plus, toutes les sorties imprimées sur stderr
et stdout
seront agrégées par journald et accessibles via le journal système (avec journalctl
) ou ciblées spécifiquement à l'aide de l'indicateur --unit
:
journalctl --unit webapp
Étant donné que journald effectue une rotation et gère son stockage par défaut, la collecte des journaux via le journal est une bonne stratégie pour gérer le stockage des journaux.
Le reste de cet article explorera les options permettant d’améliorer un service comme celui-ci.
Les orchestrateurs de conteneurs comme Kubernetes prennent en charge la possibilité d'injecter en toute sécurité des secrets : des valeurs extraites de banques de données sécurisées et exposées aux charges de travail en cours d'exécution. Les données sensibles telles que les clés API ou les mots de passe nécessitent un traitement différent de celui des variables d'environnement ou des fichiers de configuration pour éviter toute exposition involontaire.
L'option LoadCredential=
systemd prend en charge le chargement de valeurs sensibles à partir de fichiers sur le disque et leur exposition aux services en cours d'exécution de manière sécurisée. Comme les plates-formes hébergées qui gèrent les secrets à distance, systemd traitera les informations d'identification différemment des valeurs telles que les variables d'environnement pour garantir leur sécurité.
Pour injecter un secret dans un service systemd, commencez par placer un fichier contenant la valeur secrète dans un chemin du système de fichiers. Par exemple, pour exposer une clé API à une unité .service
, créez un fichier dans /etc/credstore/api-key
pour conserver le fichier lors des redémarrages ou dans /run/credstore/api-key
pour éviter de conserver le fichier de manière permanente (le path peut être arbitraire, mais systemd traitera ces chemins credstore
comme valeurs par défaut). Dans les deux cas, le fichier doit avoir des autorisations restreintes à l'aide d'une commande telle que chmod 400 /etc/credstore/api-key
.
Sous la section [Service]
du fichier .service
, définissez l'option LoadCredential=
et transmettez-lui deux valeurs séparées par deux points ( :
) : le nom de l'identifiant et son chemin. Par exemple, pour appeler notre fichier /etc/credstore/api-key
« jeton », définissez l'option de service systemd suivante :
LoadCredential=token:/etc/credstore/api-key
Lorsque systemd démarre votre service, le secret est exposé au service en cours d'exécution sous un chemin du formulaire ${CREDENTIALS_DIRECTORY}/token
où ${CREDENTIALS_DIRECTORY}
est une variable d'environnement renseignée par systemd. Le code de votre application doit lire chaque secret défini de cette manière pour une utilisation dans des bibliothèques ou du code nécessitant des valeurs sécurisées telles que des jetons API ou des mots de passe. Par exemple, en Python, vous pouvez lire ce secret avec un code comme celui-ci :
from os import environ from pathlib import Path credentials_dir = Path(environ["CREDENTIALS_DIRECTORY"]) with Path(credentials_dir / "token").open() as f: secret = f.read().strip()
Vous pouvez ensuite utiliser la variable secret
avec le contenu de votre secret pour toutes les bibliothèques pouvant nécessiter un jeton API ou un mot de passe.
Une autre capacité des orchestrateurs comme Nomad est la possibilité de redémarrer automatiquement une charge de travail qui est tombée en panne. Que ce soit en raison d'une erreur d'application non gérée ou d'une autre cause, le redémarrage des applications défaillantes est une fonctionnalité très utile qui constitue souvent la première ligne de défense lors de la conception d'une application résiliente.
L' option Restart=
systemd contrôle si systemd redémarrera automatiquement un processus en cours d'exécution. Il existe plusieurs valeurs potentielles pour cette option, mais pour les services de base, le paramètre on-failure
est bien adapté pour satisfaire la plupart des cas d'utilisation.
Un autre paramètre à prendre en compte lors de la configuration du redémarrage automatique est l' option RestartSec=
, qui dicte la durée pendant laquelle systemd attendra avant de redémarrer le service. En règle générale, cette valeur doit être personnalisée pour éviter de redémarrer les services défaillants dans une boucle serrée et de consommer potentiellement trop de temps CPU consacré au redémarrage des processus. Une valeur courte qui n'attend pas trop longtemps comme 5s
est généralement suffisante.
Des options telles que RestartSec=
qui acceptent des périodes de durée ou des valeurs basées sur le temps peuvent analyser une variété de formats comme 5min 10s
ou 1hour
en fonction de vos besoins. Consultez le manuel de systemd.time pour plus d’informations.
Enfin, deux autres options dictent l'agressivité avec laquelle systemd tentera de redémarrer les unités en panne avant d'abandonner. StartLimitIntervalSec=
et StartLimitBurst=
contrôleront la fréquence à laquelle une unité est autorisée à démarrer dans une période de temps donnée. Par exemple, les paramètres suivants :
StartLimitBurst=5 StartLimitIntervalSec=10
Il permettra uniquement à une unité d'essayer de démarrer un maximum de 5 fois sur une période de 10 secondes. Si le service configuré tente de démarrer une sixième fois dans un délai de 10 secondes, systemd cessera de tenter de redémarrer l'unité et la marquera comme failed
.
En combinant tous ces paramètres, vous pouvez inclure les options suivantes pour votre unité .service
afin de configurer les redémarrages automatiques :
[Unit] StartLimitBurst=5 StartLimitIntervalSec=10 [Service] Restart=on-failure RestartSec=1
Cette configuration redémarrera un service s'il échoue (c'est-à-dire s'il se ferme de manière inattendue, par exemple avec un code de sortie différent de zéro) après avoir attendu une seconde et cessera de tenter de redémarrer le service s'il tente de démarrer plus de cinq fois au cours du parcours. de 10 secondes.
L’un des principaux avantages de l’exécution dans un conteneur est le sandboxing de sécurité. En segmentant un processus d'application du système d'exploitation sous-jacent, il est beaucoup plus difficile de transformer les vulnérabilités susceptibles d'être présentes dans le service en une véritable compromission. Les environnements d'exécution comme Docker y parviennent grâce à une combinaison de groupes de contrôle et d'autres primitives de sécurité.
Vous pouvez activer plusieurs options systemd pour appliquer des restrictions similaires qui peuvent aider à protéger un hôte sous-jacent contre un comportement imprévisible de la charge de travail :
ProtectSystem=
peut restreindre l'accès en écriture aux chemins système sensibles tels que /boot
et /usr
. La documentation de cette option énumère toutes les options disponibles, mais d'une manière générale, définir cette option sur full
est une valeur par défaut raisonnable pour protéger ces chemins de système de fichiers.ProtectHome=
peut définir les répertoires /home
, /root
et /run/user
en lecture seule avec le paramètre en read-only
ou, lorsqu'il est défini sur true
, les monter dans le système de fichiers du service en tant que répertoires vides. À moins que votre application ait un besoin spécifique d'accéder à ces répertoires, définir cette valeur sur true
peut renforcer le système en toute sécurité contre tout accès illégitime à ces répertoires.PrivateTmp=
conserve un /tmp
et /var/tmp
distincts pour le service configuré afin que les fichiers temporaires de ce service et des autres processus restent privés. À moins qu’il n’y ait une raison impérieuse pour que les processus partagent des informations via des fichiers temporaires, il s’agit d’une option utile à activer.NoNewPrivileges=
est un autre moyen sûr et simple de renforcer un service en garantissant que le processus exécuté ne peut pas élever ses privilèges. Si vous n'êtes pas sûr de la possibilité d'utiliser d'autres options de renforcement, c'est généralement l'une des moins problématiques à activer.
La page de manuel de systemd.exec est une ressource utile pour explorer les différentes options qui s'appliquent aux charges de travail exécutables telles que les services.
Les pages de manuel du projet systemd sont complètes et utiles pour découvrir toutes les options disponibles pour exécuter vos propres applications. Que vous exécutiez un service persistant comme un serveur Web ou une unité .timer
périodique pour remplacer une tâche cron, la documentation systemd peut vous offrir des conseils utiles.