Comment définir la variable d'environnement dans le service systemd?

162

J'ai un système Arch Linux avec systemd et j'ai créé mon propre service. Le service de configuration à l' /etc/systemd/system/myservice.serviceadresse ressemble à ceci:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

Maintenant, je veux avoir une variable d’environnement définie pour le fichier /bin/myforegroundcmd. Comment je fais ça?

lfagundes
la source

Réponses:

197

Les temps changent, tout comme les meilleures pratiques.

Le meilleur moyen actuel de le faire est de l'exécuter systemctl edit myservice, ce qui créera un fichier de remplacement ou vous permettra de modifier un fichier existant.

Dans les installations normales, cela créera un répertoire /etc/systemd/system/myservice.service.d. À l'intérieur de ce répertoire, vous créerez un fichier dont le nom se termine par .conf(généralement, override.conf). Dans ce fichier, vous pouvez ajouter ou remplacer toute partie de l'unité fournie par la distribution.

Par exemple, dans un fichier /etc/systemd/system/myservice.service.d/myenv.conf:

[Service]
Environment="SECRET=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN"
Environment="ANOTHER_SECRET=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd"

Notez également que si le répertoire existe et est vide, votre service sera désactivé! Si vous n'avez pas l'intention de mettre quelque chose dans le répertoire, assurez-vous qu'il n'existe pas.


Pour référence, l'ancienne manière était:

Pour ce faire, il est recommandé de créer un fichier /etc/sysconfig/myservicecontenant vos variables, puis de les charger EnvironmentFile.

Pour plus de détails, voir la documentation de Fedora sur la rédaction d'un script systemd .

Michael Hampton
la source
4
Je suppose que le sysconfigchemin est spécifique à Fedora mais la question concerne Arch Linux. La réponse de paluh est plus intéressante je pense
Ludovic Kuty
1
/etc/sysconfigest spécifique à Fedora. AFAIR Arch Linux insistait pour que les fichiers de configuration soient /etcsitués dans un emplacement spécifique à un paquet plutôt qu'à cet emplacement spécifique à Fedora. Par exemple /etc/myservice.conf, utiliser un fichier supplémentaire ne semble pas être la bonne solution ici.
Michał Górny
5
Non non Non. / etc / sysconfig n'est pas recommandé. Il est déconseillé, de même que / etc / default / * de debian, car ils sont inutiles et les noms n’ont de sens et n’ont de sens que pour des raisons de compatibilité avec les versions antérieures (tout / etc concerne la configuration du système, pas seulement / etc / sysconfig, et / etc / defaults est destiné aux substitutions, pas aux valeurs par défaut). Il suffit de placer les définitions directement dans le fichier unité, ou si cela n’est pas possible, dans un fichier d’environnement contenant un emplacement spécifique au paquet (comme le suggère le commentaire de Michał).
zbyszek
1
@FrederickNord C'est juste des paires variable = valeur, telles que DJANGO_SETTINGS_MODULE=project.settings, une par ligne.
Michael Hampton
1
@ MichaelHampton Pourriez-vous s'il vous plaît ajouter un lien de documentation pour "meilleure façon actuelle"?
jb.
77

La réponse dépend de si la variable est supposée être constante (c'est-à-dire, non censée être modifiée par l'utilisateur obtenant l'unité) ou variable (supposée être définie par l'utilisateur).

Puisqu'il s'agit de votre unité locale, la limite est assez floue et de toute façon cela fonctionnerait. Cependant, si vous commencez à le distribuer et que cela se termine /usr/lib/systemd/system, cela deviendra important.

Valeur constante

Si la valeur n'a pas besoin de changer par instance, la méthode recommandée consiste à la placer Environment=directement dans le fichier unité:

[Unit]
Description=My Daemon

[Service]
Environment="FOO=bar baz"
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

L'avantage de cela est que la variable est conservée dans un seul fichier avec l'unité. Par conséquent, le fichier d'unité est plus facile à déplacer entre les systèmes.

Valeur variable

Cependant, la solution ci-dessus ne fonctionne pas bien lorsque sysadmin est supposé changer la valeur de la variable d'environnement localement. Plus spécifiquement, la nouvelle valeur devra être définie chaque fois que le fichier d'unité est mis à jour.

Dans ce cas, un fichier supplémentaire doit être utilisé. Comment - dépend généralement de la politique de distribution.

Une solution particulièrement intéressante consiste à utiliser le /etc/systemd/system/myservice.service.drépertoire. Contrairement à d'autres solutions, ce répertoire est pris en charge par systemd lui-même et ne comporte donc aucun chemin spécifique à la distribution.

Dans ce cas, vous placez un fichier comme /etc/systemd/system/myservice.service.d/local.confcelui-ci qui ajoute les parties manquantes du fichier unité:

[Service]
Environment="FOO=bar baz"

Ensuite, systemd fusionne les deux fichiers lors du démarrage du service (n'oubliez pas systemctl daemon-reloadde modifier l'un des fichiers ). Et puisque ce chemin est utilisé directement par systemd, vous ne l'utilisez pas EnvironmentFile=pour cela.

Si la valeur n'est supposée être modifiée que sur certains des systèmes affectés, vous pouvez combiner les deux solutions, en fournissant une valeur par défaut directement dans l'unité et une substitution locale dans l'autre fichier.

Michał Górny
la source
systemctl daemon-reloadest la commande pour recharger systemd
Dmitry Buzolin
EnvironmentFile=C'est mieux quand les valeurs sont des secrets comme les mots de passe. Voir ma réponse pour plus de détails.
Don Kirkby
17

Les réponses de Michael et Michał sont utiles et répondent à la question initiale de savoir comment définir une variable d'environnement pour un service systemd. Cependant, une utilisation courante des variables d'environnement consiste à configurer des données sensibles, telles que des mots de passe, dans un endroit qui ne sera pas accidentellement engagé dans le contrôle de source avec le code de votre application.

Si c'est la raison pour laquelle vous souhaitez transmettre une variable d'environnement à votre service, ne l' utilisez pasEnvironment= dans le fichier de configuration de l'unité. Utilisez-le EnvironmentFile=et pointez-le sur un autre fichier de configuration lisible uniquement par le compte de service (et les utilisateurs ayant un accès root).

Les détails du fichier de configuration de l'unité sont visibles par tout utilisateur à l'aide de cette commande:

systemctl show my_service

Je mets un fichier de configuration à /etc/my_service/my_service.confet met mes secrets dedans:

MY_SECRET=correcthorsebatterystaple

Ensuite, dans mon fichier d'unité de service, j'ai utilisé EnvironmentFile=:

[Unit]
Description=my_service

[Service]
ExecStart=/usr/bin/python /path/to/my_service.py
EnvironmentFile=/etc/my_service/my_service.conf
User=myservice

[Install]
WantedBy=multi-user.target

J'ai vérifié que ps auxeces variables d'environnement ne sont pas visibles et que les autres utilisateurs n'y ont pas accès /proc/*/environ. Vérifiez sur votre propre système, bien sûr.

Don Kirkby
la source
8

Michael a donné une solution propre mais je voulais obtenir la variable env mise à jour à partir du script. Malheureusement, l’exécution de commandes bash n’est pas possible dans le fichier d’unité systemd. Heureusement, vous pouvez déclencher bash dans ExecStart:

http://www.dsm.fordham.edu/cgi-bin/man-cgi.pl?topic=systemd.service&sect=5

Notez que ce paramètre ne prend pas directement en charge les lignes de commande shell. Si des lignes de commande shell doivent être utilisées, elles doivent être explicitement passées à une implémentation shell quelconque.

Exemple dans notre cas est alors:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"
utilisateur1830432
la source
7
Cela ne fonctionnera pas pour plusieurs raisons (à moins que ce soit un service "one-shot", ce qui est plutôt inutile). J'ai réussi à obtenir ce qui suit au travail: /bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'. Les -agarantit l'environnement est exporté vers le sous-processus (sauf si vous voulez préfixe toutes les variables whateveravec export)
Otheus
pourquoi ça ne marche pas? Il faut toujours déclencher toute la commande qui inclut l'exécution du script, n'est-ce pas?
user1830432
Peut ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmd- être est-ce une meilleure solution dans ce cas.
kstep
@Otheus: Excellente réponse, enregistrée le jour où j'ai dû créer un fichier Tomcat 8 Unit.
Daniel
1
Il existe un moyen d'exécuter une commande bash "dans" un fichier de service systemd. Voir ce lien: coreos.com/os/docs/latest/…
Mark Lakata le