在systemd服务中使用环境变量
问题起源
在systemd
中使用脚本配置一个自有java程序的自启动时,发现一个自定义的环境变量无法在systemd
中读取,在shell
中手动启动就可以正常工作。
上网查了一下systemd
读取环境变量的相关知识,最终选择在脚本中source
了对应的环境变量文件,解决了问题。
将systemd
读取环境变量的机制和几种方法整理罗列如下,以作备忘。
systemd 读取环境变量的机制
根据systemd/User中的说明如下:
Environment variables
The user instance of systemd does not inherit any of the environment variables set in places like
.bashrc
etc. There are several ways to set environment variables for the systemd user instance:
- For users with a
$HOME
directory, create a .conf file in the~/.config/environment.d/
directory with lines of the formNAME=VAL
. Affects only that user’s user unit. See environment.d(5) for more information.- Use the
DefaultEnvironment
option in/etc/systemd/user.conf
file. Affects all user units.- Add a drop-in configuration file in
/etc/systemd/system/user@.service.d/
. Affects all user units; see #Service example- At any time, use
systemctl --user set-environment
orsystemctl --user import-environment
. Affects all user units started after setting the environment variables, but not the units that were already running.- Using the
dbus-update-activation-environment --systemd --all
command provided by dbus. Has the same effect assystemctl --user import-environment
, but also affects the D-Bus session. You can add this to the end of your shell initialization file.- For “global” environment variables for the user environment you can use the
environment.d
directories which are parsed by some generators. See environment.d(5) and systemd.generator(7) for more information.- You can also write a systemd.environment-generator(7) script which can produce environment variables that vary from user to user, this is probably the best way if you need per-user environments (this is the case for
XDG_RUNTIME_DIR
,DBUS_SESSION_BUS_ADDRESS
, etc).One variable you may want to set is
PATH
.After configuration, the command
systemctl --user show-environment
can be used to verify that the values are correct.
搜刮网上的一些说法后,总结如下:
/etc/profile
或者/etc/security/limit.d
这些文件中配置的环境变量仅对通过pam
登录的用户生效,而systemd
是不读这些配置的,所以这就造成登录到终端时查看环境变量和手动启动应用都一切正常,但是systemd
无法正常启动应用- 如果需要给
systemd
配置默认参数,全局的配置在/etc/systemd/system.conf
和/etc/systemd/user.conf
中。同时还会加载两个配置文件对应的目录中所有的.conf
配置文件/etc/systemd/system.conf.d/*.conf
和/etc/systemd/user.conf.d/*.conf
,一般的服务单元使用system.conf
即可。加载优先级system.conf
最低,所以system.conf.d
目录中的配置会覆盖system.conf
的配置 - 目前已知的是更改system.conf配置,需要重启系统才能生效,还没找到如何重新加载此配置
虚拟机实验
实验目的: 看通过systemd
运行的程序可以读取到哪些环境变量。
实验方法: 配置同一个脚本,脚本输出env
的内容,比较shell
下和systemd
下启动该脚本的输出内容的区别。
试验文件准备
创建环境变量文件/etc/profile.d/env.sh
,在其中设置环境变量MY_ENV="helloworld"
|
|
创建service使用的脚本/usr/local/bin/systemctl_env_test.sh
|
|
|
|
添加权限sudo chmod a+x /usr/local/bin/systemctl_env_test.sh
创建system service文件,/usr/lib/systemd/system/systemctl_env_test.service
|
|
导入service: systemctl daemon-reload
启动service命令: systemctl start systemctl_env_test.service
执行输出
当在shell
下直接运行/usr/local/bin/systemctl_env_test.sh
时,日志文件systemctl_env_test.log
的输出如下:
|
|
可以看到/etc/profile.d/env.sh
中设置的MY_ENV=helloworld
能够被脚本/usr/local/bin/systemctl_env_test.sh
读取到。
使用systemctl
运行systemctl_env_test.service
来执行脚本/usr/local/bin/systemctl_env_test.sh
时,输出如下:
|
|
可以看到除了/etc/profile.d/env.sh
中设置的MY_ENV=helloworld
没有被读取外,连基础的HOSTNAME
,SHELL
等环境变量都没有。
结论
可以看到
shell
中运行脚本可以读取到全部正常的环境变量systemd
中运行脚本,只读取到最最基础的LANG
,PATH
和PWD
几个环境变量,/etc/profile
下的环境变量并没有读取。
systemd添加使用环境变量的方法
使用Environment设置环境变量
可以在systemd
的service
中使用Environment
来设置环境变量。
修改原文件/usr/lib/systemd/system/systemctl_env_test.service
,使用Environment
关键字来添加如下环境变量:
|
|
此时/usr/lib/systemd/system/systemctl_env_test.service
变为:
|
|
执行systemctl start
后查看脚本env
的输出日志,结果如下:
|
|
可以看出systemd
的service
文件中设置的环境变量,可以被ExecStart
指定的脚本读取到。
使用EnvironmentFile导入环境变量文件
使用Environment
导入少数固定的环境变量是可行的,但是如果需要导入大量的,或者时常要变动的环境变量,那么使用EnvironmentFile
关键字通过导入文件的方式会更合适。
试验方法如下:
创建测试用环境变量文件/usr/local/etc/environment_file_test/load.conf
, 内容如下:
|
|
修改原文件/usr/lib/systemd/system/systemctl_env_test.service
,使用EnvironmentFile
关键字来导入文件/usr/local/etc/environment_file_test/load.conf
。
|
|
此时/usr/lib/systemd/system/systemctl_env_test.service
变为:
|
|
执行systemctl start
后查看脚本env
的输出日志,结果如下:
|
|
可以看出systemd
的service
文件中设置的环境变量,可以被ExecStart
指定的脚本读取到。
注意: /usr/local/etc/environment_file_test/load.conf
文件是配置文件,就是key=value
的格式,和/etc/profile.d/
中导入的shell文件是不同的,需要加以区别。所以load.conf配置文件中,不能使用export xx=yy的格式
在执行命令或脚本中设置或者source环境变量文件
除了在systemd
的service
文件中指定Environment
或EnvironmentFile
外,还有一个方法就是在ExecStart
,ExecStop
等执行的脚本中直接指定环境变量或者source环境变量文件。
在上面的几个例子中,也可以不在/usr/lib/systemd/system/systemctl_env_test.service
文件中使用Environment
或EnvironmentFile
,而是直接在执行的shell脚本/usr/local/bin/systemctl_env_test.sh
中source /etc/profile.d/environment_file_test.sh
试验方法如下:
修改ExecStart
执行的shell脚本/usr/local/bin/systemctl_env_test.sh
, 在里面添加source /etc/profile.d/env.sh
, 添加后的文件内容为:
|
|
其中/etc/profile.d/env.sh
文件的内容如下:
|
|
还原文件/usr/lib/systemd/system/systemctl_env_test.service
去除Environment
和EnvironmentFile
的设置,变为:
|
|
执行systemctl start
后查看脚本env
的输出日志,结果如下:
|
|
可以看出也能加载出MY_ENV
这个环境变量。
注意:这边是shell脚本中source /etc/profile.d/env.sh
, 所以文件中需要加上export
,父shell才能读取对应的内容。