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


我有一个 使用systemd构建Linux系统 我已经创建了自己的服务。配置服务在 /etc/systemd/system/myservice.service 看起来像这样:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target

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


119
2017-08-01 19:43






答案:


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

当前 最好的办法就是跑步 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

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


143
2017-08-01 20:07



我想是的 sysconfig 路径是Fedora特有的,但问题是关于Arch Linux。我认为,paluh的答案更有趣 - Ludovic Kuty
/etc/sysconfig 是Fedora特有的。 AFAIR Arch Linux正在推动将配置文件放在某个特定于包的位置 /etc 而不是特定于Fedora的位置。喜欢 /etc/myservice.conf虽然使用额外的文件似乎不是正确的方法在这里。 - Michał Górny
不不不。 / etc / sysconfig不建议使用。不鼓励使用/ etc / default / *来自debian,因为它们毫无意义,名称毫无意义,只有出于向后兼容的原因才有意义(所有/ etc都是关于系统的配置,而不仅仅是/ etc / sysconfig和/ etc / defaults用于覆盖,而不是默认值)。只需将定义直接放在单元文件中,或者如果不可能,就在具有包特定位置的环境文件中(如Michał的评论建议)。 - zbyszek
@FrederickNord它只是变量=值对,例如 DJANGO_SETTINGS_MODULE=project.settings,每行一个。 - Michael Hampton♦
@MichaelHampton你能否为“当前最佳方式”添加文档链接? - jb.


答案取决于变量是否应该是常量(即,不应该被用户获取单元修改)或变量(应该由用户设置)。

由于它是你的本地单位,边界非常模糊,无论哪种方式都可行。但是,如果你开始分发它,它最终会进入 /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= 为了这。

如果应该仅在某些受影响的系统上更改该值,则可以组合这两种解决方案,直接在单元中提供默认值,在另一个文件中提供本地覆盖。


66
2018-04-23 07:48



systemctl daemon-reload 是重新加载systemd的命令 - Dmitry Buzolin
EnvironmentFile= 当值是密码这样的秘密时更好。看到 我的答案 详情。 - Don Kirkby


http://0pointer.de/public/systemd-man/systemd.exec.html#Environment=  - 你有两个选择(迈克尔已经指出了一个):

Environment=

EnvironmentFile=

34
2017-10-16 13:55





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

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

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

我们的例子就是:

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

6
2017-07-23 13:31



这不会有多种原因(除非它是“一次性”服务,这是毫无意义的)。我设法让以下工作: /bin/bash -a -c 'source /etc/sysconfig/whatever && exec whatever-program'。该 -a 确保将环境导出到子流程(除非您想要在中添加所有变量的前缀) whatever 同 export ) - Otheus
为什么它不起作用?它应该总是触发包括执行脚本的整个命令,不是吗? - user1830432
也许 ExecStart=/usr/bin/env ENV=script /bin/myforegroundcmd 在这种情况下是一个更好的解决方案。 - kstep
@Otheus:很棒的答案,白天我必须创建一个Tomcat 8 Unit文件。 - Daniel
有一种方法可以在系统服务文件中“执行”bash命令。看到这个链接: coreos.com/os/docs/latest/... - Mark Lakata


答案由 迈克尔 和 米哈尔 很有帮助,并回答了如何为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。当然,请检查您自己的系统。


6
2018-05-04 00:29