如何在systemd服务中设置环境变量?


162

我有一个带systemdArch Linux系统,并且已经创建了自己的服务。的配置服务/etc/systemd/system/myservice.service如下所示:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

现在我想为设置环境变量/bin/myforegroundcmd。我怎么做?

Answers:


196

时代在变化,最佳实践也在变化。

当前要做到这一点最好的方法是运行systemctl edit myservice,它会为你创建一个覆盖文件或让您编辑现有的一个。

在正常安装中,这将创建一个目录/etc/systemd/system/myservice.service.d,并在该目录内创建一个文件名.conf(通常为override.conf)结尾的文件,您可以在该文件中添加或覆盖发行版随附的设备的任何部分。

例如,在一个文件中/etc/systemd/system/myservice.service.d/myenv.conf

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

另请注意,如果目录存在且为空,则将禁用您的服务!如果您不打算在目录中放置某些内容,请确保该内容不存在。


作为参考,旧方法是:

推荐的方法是创建一个/etc/sysconfig/myservice包含变量的文件,然后使用加载它们EnvironmentFile

有关完整的详细信息,请参阅有关如何编写systemd脚本的 Fedora文档。


4
我想这条sysconfig路径特定于Fedora,但问题是关于Arch Linux。我认为paluh的答案更有趣
Ludovic Kuty 2013年

1
/etc/sysconfig是Fedora特有的。AFAIR Arch Linux一直在推动配置文件位于特定于软件包的/etc位置,而不是特定于Fedora的位置。就像一样/etc/myservice.conf,尽管在这里使用多余的文件似乎不是正确的方法。
米哈尔戈尔诺-

5
不不不。不建议/ etc / sysconfig。不鼓励与debian中的/ etc / default / *一起使用,因为它们是毫无意义的,并且名称是没有意义的,并且仅出于向后兼容的原因才有意义(/ etc的所有都是关于系统的配置,而不仅仅是/ etc / sysconfig和/ etc / defaults用于覆盖,而不是默认值)。只需将定义直接放在单元文件中,或者如果不可能,则将其放在具有程序包特定位置的环境文件中(如Michał的注释所建议的)。
zbyszek

1
@FrederickNord这只是变量=值对,例如DJANGO_SETTINGS_MODULE=project.settings,每行一对。
迈克尔·汉普顿

1
@MichaelHampton能否请您添加“当前最佳方法”的文档链接?
jb。

77

答案取决于变量是应该是常量(即不应由用户获取单位来修改)还是变量(应由用户设置)。

由于它是您的本地单位,因此边界非常模糊,无论哪种方式都可以。但是,如果您开始分发它并且最终将在中发行/usr/lib/systemd/system,那么这将变得很重要。

定值

如果不需要为每个实例更改值,则首选方法是将其作为放置Environment=在单元文件中:

[Unit]
Description=My Daemon

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

[Install]
WantedBy=multi-user.target

这样做的好处是变量与单元一起保存在单个文件中。因此,单位文件更易于在系统之间移动。

可变值

但是,当sysadmin应该在本地更改环境变量的值时,上述解决方案效果不佳。更具体地说,每次更新单位文件时都需要设置新值。

对于这种情况,将使用一个额外的文件。方式-通常取决于分配政策。

一种特别有趣的解决方案是使用/etc/systemd/system/myservice.service.d目录。与其他解决方案不同,此目录由systemd本身支持,因此没有特定于发行版的路径。

在这种情况下,您将放置一个像/etc/systemd/system/myservice.service.d/local.conf这样的文件,该文件将添加单位文件的缺失部分:

[Service]
Environment="FOO=bar baz"

之后,systemd在启动服务时合并这两个文件(请记住systemctl daemon-reload在更改它们中的一个之后再进行合并)。并且由于该路径直接由systemd使用,因此您无需使用此路径EnvironmentFile=

如果应该仅在某些受影响的系统上更改该值,则可以结合使用两种解决方案,直接在设备中提供默认值,并在另一个文件中提供本地替代。


systemctl daemon-reload是重新加载systemd的命令
Dmitry Buzolin 18-4-22

EnvironmentFile=当值是密码之类的秘密时,则更好。有关详细信息,请参见我的答案
唐·柯比


17

通过这些问题的答案迈克尔米哈尔是有益的,并回答了如何设置一个环境变量的systemd服务原来的问题。但是,环境变量的一种常见用法是在一个不会意外地使用应用程序代码进行源代码控制的地方配置敏感数据,例如密码。

如果这就是为什么你需要一个环境变量传递给你的服务,使用Environment=的单元配置文件中 使用EnvironmentFile=并将其指向仅服务帐户(和具有root访问权限的用户)可读的另一个配置文件。

使用以下命令,任何用户都可以看到单元配置文件的详细信息:

systemctl show my_service

我将配置文件放在其中/etc/my_service/my_service.conf,并将我的秘密放在其中:

MY_SECRET=correcthorsebatterystaple

然后在服务单元文件中,我使用了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

我检查了ps auxe看不到这些环境变量,并且其他用户没有访问权限/proc/*/environ。当然,请检查您自己的系统。


8

Michael提供了一个简单的解决方案,但我想从脚本中获取更新的env变量。不幸的是,在systemd单元文件中无法执行bash命令。幸运的是,您可以在ExecStart中触发bash:

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

请注意,此设置不直接支持Shell命令行。如果要使用shell命令行,则需要将它们显式传递给某种shell实现。

那么我们的例子是:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"

7
由于多种原因,这将无法正常工作(除非它是“一次性”服务,这毫无意义)。我设法得到以下工作:/bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'。在-a确保环境导出到子进程(除非你想在前缀所有变量whateverexport
Otheus

为什么它不起作用?它应该始终触发包括执行脚本在内的整个命令,不是吗?
user1830432

ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmd在这种情况下,也许是更好的解决方案。
kstep

@Otheus:很好的答案,在我不得不创建Tomcat 8 Unit文件时保存了一天。
丹尼尔(Daniel)

1
有一种方法可以在systemd服务文件中执行bash命令。请参阅此链接:coreos.com/os/docs/latest/...
马克Lakata
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.