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"
1 2 3 | # cat /etc/profile.d/env.sh export MY_ENV="helloworld" # |
创建service使用的脚本/usr/local/bin/systemctl_env_test.sh
1 2 3 4 5 | #!/bin/bash LOG_FILE="/tmp/systemctl_env_test.log" echo "========================================================" > $LOG_FILE env | sort >> $LOG_FILE echo "========================================================" >> $LOG_FILE |
1 2 3 4 5 6 7 8 | #!/bin/bash LOG_FILE="/tmp/systemctl_env_test.log" echo "Start...." > $LOG_FILE echo "======= Before load /etc/profile.d/env.sh" >> $LOG_FILE env >> $LOG_FILE source /etc/profile.d/env.sh >> $LOG_FILE echo "======= After load /etc/profile.d/env.sh" >> $LOG_FILE env >> $LOG_FILE |
添加权限sudo chmod a+x /usr/local/bin/systemctl_env_test.sh
创建system service文件,/usr/lib/systemd/system/systemctl_env_test.service
1 2 3 4 5 6 7 8 9 10 11 | [Unit] Description=systemctl_env_test service After=syslog.target network.target [Service] Type=forking ExecStart=/usr/local/bin/systemctl_env_test.sh PrivateTmp=false [Install] WantedBy=multi-user.target |
导入service: systemctl daemon-reload
启动service命令: systemctl start systemctl_env_test.service
执行输出
当在shell
下直接运行/usr/local/bin/systemctl_env_test.sh
时,日志文件systemctl_env_test.log
的输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # bash /usr/local/bin/systemctl_env_test.sh # # cat /tmp/systemctl_env_test.log ======================================================== HISTCONTROL=ignoredups HISTSIZE=1000 HOME=/root HOSTNAME=jk-server LANG=zh_CN.UTF-8 LESSOPEN=||/usr/bin/lesspipe.sh %s LOGNAME=root LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36: MAIL=/var/spool/mail/root MY_ENV=helloworld PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin PWD=/root SELINUX_LEVEL_REQUESTED= SELINUX_ROLE_REQUESTED= SELINUX_USE_CURRENT_RANGE= SHELL=/bin/bash SHLVL=2 SSH_CLIENT=192.168.187.1 54467 22 SSH_CONNECTION=192.168.187.1 54467 192.168.187.81 22 SSH_TTY=/dev/pts/0 TERM=xterm-color USER=root _=/usr/bin/env XDG_RUNTIME_DIR=/run/user/0 XDG_SESSION_ID=77 ======================================================== # |
可以看到/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
时,输出如下:
1 2 3 4 5 6 7 8 9 10 11 | # systemctl start systemctl_env_test.service # # cat /tmp/systemctl_env_test.log ======================================================== LANG=zh_CN.UTF-8 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin PWD=/ SHLVL=1 _=/usr/bin/env ======================================================== # |
可以看到除了/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
关键字来添加如下环境变量:
1 2 3 | Environment="One=1" "Three=3" Environment="Two=2" Environment="Four=4" |
此时/usr/lib/systemd/system/systemctl_env_test.service
变为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [Unit] Description=systemctl_env_test service After=syslog.target network.target [Service] Type=forking Environment="One=1" "Three=3" Environment="Two=2" Environment="Four=4" ExecStart=/usr/local/bin/systemctl_env_test.sh PrivateTmp=false [Install] WantedBy=multi-user.target |
执行systemctl start
后查看脚本env
的输出日志,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # systemctl start systemctl_env_test.service && cat /tmp/systemctl_env_test.log ======================================================== Four=4 LANG=zh_CN.UTF-8 One=1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin PWD=/ SHLVL=1 Three=3 Two=2 _=/usr/bin/env ======================================================== # |
可以看出systemd
的service
文件中设置的环境变量,可以被ExecStart
指定的脚本读取到。
使用EnvironmentFile导入环境变量文件
使用Environment
导入少数固定的环境变量是可行的,但是如果需要导入大量的,或者时常要变动的环境变量,那么使用EnvironmentFile
关键字通过导入文件的方式会更合适。
试验方法如下:
创建测试用环境变量文件/usr/local/etc/environment_file_test/load.conf
, 内容如下:
1 2 | MY_ENV="helloworld" GLOBAL_ENV="nihaoshijie" |
修改原文件/usr/lib/systemd/system/systemctl_env_test.service
,使用EnvironmentFile
关键字来导入文件/usr/local/etc/environment_file_test/load.conf
。
1 | EnvironmentFile=/usr/local/etc/environment_file_test/load.conf |
此时/usr/lib/systemd/system/systemctl_env_test.service
变为:
1 2 3 4 5 6 7 8 9 10 11 12 | [Unit] Description=systemctl_env_test service After=syslog.target network.target [Service] Type=forking EnvironmentFile=/usr/local/etc/environment_file_test/load.conf ExecStart=/usr/local/bin/systemctl_env_test.sh PrivateTmp=false [Install] WantedBy=multi-user.target |
执行systemctl start
后查看脚本env
的输出日志,结果如下:
1 2 3 4 5 6 7 8 9 10 11 | # systemctl start systemctl_env_test.service && cat /tmp/systemctl_env_test.log ======================================================== GLOBAL_ENV=nihaoshijie LANG=zh_CN.UTF-8 MY_ENV=helloworld PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin PWD=/ SHLVL=1 _=/usr/bin/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
, 添加后的文件内容为:
1 2 3 4 5 6 | #!/bin/bash LOG_FILE="/tmp/systemctl_env_test.log" echo "========================================================" > $LOG_FILE source /etc/profile.d/env.sh env | sort >> $LOG_FILE echo "========================================================" >> $LOG_FILE |
其中/etc/profile.d/env.sh
文件的内容如下:
1 | export MY_ENV="helloworld" |
还原文件/usr/lib/systemd/system/systemctl_env_test.service
去除Environment
和EnvironmentFile
的设置,变为:
1 2 3 4 5 6 7 8 9 10 11 | [Unit] Description=systemctl_env_test service After=syslog.target network.target [Service] Type=forking ExecStart=/usr/local/bin/systemctl_env_test.sh PrivateTmp=false [Install] WantedBy=multi-user.target |
执行systemctl start
后查看脚本env
的输出日志,结果如下:
1 2 3 4 5 6 7 8 9 10 | # systemctl start systemctl_env_test.service && cat /tmp/systemctl_env_test.log ======================================================== LANG=zh_CN.UTF-8 MY_ENV=helloworld PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin PWD=/ SHLVL=1 _=/usr/bin/env ======================================================== # |
可以看出也能加载出MY_ENV
这个环境变量。
注意:这边是shell脚本中source /etc/profile.d/env.sh
, 所以文件中需要加上export
,父shell才能读取对应的内容。