在 systemd 服务文件中访问 docker 环境变量的问题
issues in accessing docker environment variables in systemd service files
1) 我是 运行 一个带有以下命令的 docker 容器(使用 -e 选项传递一些环境变量)
$ docker run --name=xyz -d -e CONTAINER_NAME=xyz -e SSH_PORT=22 -e NWMODE=HOST -e XDG_RUNTIME_DIR=/run/user/0 --net=host -v /mnt:/mnt -v /dev:/dev -v /etc/sysconfig/network-scripts:/etc/sysconfig/network-scripts -v /:/hostroot/ -v /etc/hostname:/etc/host_hostname -v /etc/localtime:/etc/localtime -v /var/run/docker.sock:/var/run/docker.sock --privileged=true cf3681e04bfb
2) 运行 上面的容器后,我检查容器内的env变量NWMODE,它正确显示如下:
$ docker exec -it xyz bash
$ env | grep NWMODE
NWMODE=HOST
3) 现在,我创建了一个示例服务 'b',如下所示,它执行脚本 b.sh(我尝试访问 NWMODE):
root@ubuntu16:/etc/systemd/system# cat b.service
[Unit]
Description=testing service b
[Service]
ExecStart=/bin/bash /etc/systemd/system/b.sh
root@ubuntu16:/etc/systemd/system# cat b.sh
#!/bin/bash`
systemctl import-environment
echo "NWMODE:" $NWMODE`
4) 现在如果我启动服务 'b' 并查看其日志,它表明它无法访问 NWMODE 环境变量
$ systemctl start b
$ journalctl -fu b
...
systemd[1]: Started testing service b.
bash[641]: NWMODE: //blank for $NWMODE here`
5) 现在 b.sh 中没有 'systemctl import-environment',如果我执行以下操作,那么 b.service 日志会显示 NWMODE 环境变量的正确值:
$ systemctl import-environment
$ systemctl start b
虽然上面的第 5 步有效,但我不能这样做,因为我系统中的所有服务将由 systemd 自动启动。在那种情况下,任何人都可以让我知道如何访问服务文件中的环境变量(使用上面的 'docker run...' cmd 传递)(例如上面的 b.sh 中)。这可以通过 systemctl import-environment
以某种方式实现还是有其他方式?
systemd 取消设置所有环境变量以提供干净的环境。 Afaik 旨在成为一项安全功能。
解决方法:创建文件 /etc/systemd/system.conf.d/myenvironment.conf
:
[Manager]
DefaultEnvironment=CONTAINER_NAME=xyz NWMODE=HOST XDG_RUNTIME_DIR=/run/user/0
systemd 将设置在此文件中声明的环境变量。
您可以设置一个 ENTRYPOINT
脚本,在 运行 systemd
之前自动创建此文件。示例:
RUN echo '#! /bin/bash \n\
echo "[Manager] \n\
DefaultEnvironment=$(while read -r Line; do echo -n "$Line" ; done < <(env) \n\
" >/etc/systemd/system.conf.d/myenvironment.conf \n\
exec /lib/systemd/systemd \n\
' >/usr/local/bin/setmyenv && chmod +x /usr/bin/setmyenv
ENTRYPOINT /usr/bin/setmyenv
无需在 Dockerfile
中创建脚本,您可以将其存储在外部并使用 COPY
添加:
#! /bin/bash
echo "[Manager]
DefaultEnvironment=$(while read -r Line; do echo -n "$Line" ; done < <(env)
" >/etc/systemd/system.conf.d/myenvironment.conf
exec /lib/systemd/systemd
TL;DR
运行 使用 bash 的命令,首先将 docker 环境变量存储到一个文件中(或者将它们通过管道传递给两个 awk
),提取并导出变量,最后 运行 你的主脚本。
ExecStart=/bin/bash -c "cat /proc/1/environ | tr '[=10=]' '\n' > /home/env_file; export MY_ENV_VARIABLE=$(awk -F= -v key="MY_ENV_VARIABLE" '==key {print }' /home/env_file); /usr/bin/python3 /usr/bin/my_python_script.py"
不管@mviereck 说的是真的,我还是找到了解决这个问题的另一种方法。
我的用例是在 Docker 运行 命令 (docker run -e MY_ENV_VARIABLE="some_val"
) 中将环境变量传递到我的 system-d 容器,并在 python 中使用它运行 通过 system-d 单元文件的脚本。
据此post(https://forums.docker.com/t/where-are-stored-the-environment-variables/65762)可以在容器内部的运行ning进程/proc/1/environ
中找到容器环境变量。执行 cat
确实表明环境变量 MY_ENV_VARIABLE=some_val
确实存在,尽管形式有些混乱。
$ cat /proc/1/environ
HOSTNAME=271fbnd986bdMY_ENV_VARIABLE=some_valcontainer=dockerLC_ALL=CDEBIAN_FRONTEND=noninteractiveHOME=/rootroot@271fb0d986bd
现在的主要任务是提取 MY_ENV_VARIABLE="some_val"
值并将其传递给 system-d 单元文件中的 ExecStart
指令。
(提取码参考自)
# this outputs a nice key,value pair
$ cat /proc/1/environ | tr '[=12=]' '\n'
HOSTNAME=861f23cd1b33
MY_ENV_VARIABLE=some_val
container=docker
LC_ALL=C
DEBIAN_FRONTEND=noninteractive
HOME=/root
# we can store this in a file for use, too
$ cat /proc/1/environ | tr '[=12=]' '\n' > /home/env_var_file
# we can then reuse the file to extract the value of interest against a key
$ awk -F= -v key="MY_ENV_VARIABLE" '==key {print }' /home/env_file
some_val
现在在 system-d 单元文件的 ExecStart
指令中我们可以这样做:
[Service]
Type=simple
ExecStart=/bin/bash -c "cat /proc/1/environ | tr '[=13=]' '\n' > /home/env_file; export MY_ENV_VARIABLE=$(awk -F= -v key="MY_ENV_VARIABLE" '==key {print }' /home/env_file); /usr/bin/python3 /usr/bin/my_python_script.py"
1) 我是 运行 一个带有以下命令的 docker 容器(使用 -e 选项传递一些环境变量)
$ docker run --name=xyz -d -e CONTAINER_NAME=xyz -e SSH_PORT=22 -e NWMODE=HOST -e XDG_RUNTIME_DIR=/run/user/0 --net=host -v /mnt:/mnt -v /dev:/dev -v /etc/sysconfig/network-scripts:/etc/sysconfig/network-scripts -v /:/hostroot/ -v /etc/hostname:/etc/host_hostname -v /etc/localtime:/etc/localtime -v /var/run/docker.sock:/var/run/docker.sock --privileged=true cf3681e04bfb
2) 运行 上面的容器后,我检查容器内的env变量NWMODE,它正确显示如下:
$ docker exec -it xyz bash
$ env | grep NWMODE
NWMODE=HOST
3) 现在,我创建了一个示例服务 'b',如下所示,它执行脚本 b.sh(我尝试访问 NWMODE):
root@ubuntu16:/etc/systemd/system# cat b.service
[Unit]
Description=testing service b
[Service]
ExecStart=/bin/bash /etc/systemd/system/b.sh
root@ubuntu16:/etc/systemd/system# cat b.sh
#!/bin/bash`
systemctl import-environment
echo "NWMODE:" $NWMODE`
4) 现在如果我启动服务 'b' 并查看其日志,它表明它无法访问 NWMODE 环境变量
$ systemctl start b
$ journalctl -fu b
...
systemd[1]: Started testing service b.
bash[641]: NWMODE: //blank for $NWMODE here`
5) 现在 b.sh 中没有 'systemctl import-environment',如果我执行以下操作,那么 b.service 日志会显示 NWMODE 环境变量的正确值:
$ systemctl import-environment
$ systemctl start b
虽然上面的第 5 步有效,但我不能这样做,因为我系统中的所有服务将由 systemd 自动启动。在那种情况下,任何人都可以让我知道如何访问服务文件中的环境变量(使用上面的 'docker run...' cmd 传递)(例如上面的 b.sh 中)。这可以通过 systemctl import-environment
以某种方式实现还是有其他方式?
systemd 取消设置所有环境变量以提供干净的环境。 Afaik 旨在成为一项安全功能。
解决方法:创建文件 /etc/systemd/system.conf.d/myenvironment.conf
:
[Manager]
DefaultEnvironment=CONTAINER_NAME=xyz NWMODE=HOST XDG_RUNTIME_DIR=/run/user/0
systemd 将设置在此文件中声明的环境变量。
您可以设置一个 ENTRYPOINT
脚本,在 运行 systemd
之前自动创建此文件。示例:
RUN echo '#! /bin/bash \n\
echo "[Manager] \n\
DefaultEnvironment=$(while read -r Line; do echo -n "$Line" ; done < <(env) \n\
" >/etc/systemd/system.conf.d/myenvironment.conf \n\
exec /lib/systemd/systemd \n\
' >/usr/local/bin/setmyenv && chmod +x /usr/bin/setmyenv
ENTRYPOINT /usr/bin/setmyenv
无需在 Dockerfile
中创建脚本,您可以将其存储在外部并使用 COPY
添加:
#! /bin/bash
echo "[Manager]
DefaultEnvironment=$(while read -r Line; do echo -n "$Line" ; done < <(env)
" >/etc/systemd/system.conf.d/myenvironment.conf
exec /lib/systemd/systemd
TL;DR
运行 使用 bash 的命令,首先将 docker 环境变量存储到一个文件中(或者将它们通过管道传递给两个 awk
),提取并导出变量,最后 运行 你的主脚本。
ExecStart=/bin/bash -c "cat /proc/1/environ | tr '[=10=]' '\n' > /home/env_file; export MY_ENV_VARIABLE=$(awk -F= -v key="MY_ENV_VARIABLE" '==key {print }' /home/env_file); /usr/bin/python3 /usr/bin/my_python_script.py"
不管@mviereck 说的是真的,我还是找到了解决这个问题的另一种方法。
我的用例是在 Docker 运行 命令 (docker run -e MY_ENV_VARIABLE="some_val"
) 中将环境变量传递到我的 system-d 容器,并在 python 中使用它运行 通过 system-d 单元文件的脚本。
据此post(https://forums.docker.com/t/where-are-stored-the-environment-variables/65762)可以在容器内部的运行ning进程/proc/1/environ
中找到容器环境变量。执行 cat
确实表明环境变量 MY_ENV_VARIABLE=some_val
确实存在,尽管形式有些混乱。
$ cat /proc/1/environ
HOSTNAME=271fbnd986bdMY_ENV_VARIABLE=some_valcontainer=dockerLC_ALL=CDEBIAN_FRONTEND=noninteractiveHOME=/rootroot@271fb0d986bd
现在的主要任务是提取 MY_ENV_VARIABLE="some_val"
值并将其传递给 system-d 单元文件中的 ExecStart
指令。
(提取码参考自
# this outputs a nice key,value pair
$ cat /proc/1/environ | tr '[=12=]' '\n'
HOSTNAME=861f23cd1b33
MY_ENV_VARIABLE=some_val
container=docker
LC_ALL=C
DEBIAN_FRONTEND=noninteractive
HOME=/root
# we can store this in a file for use, too
$ cat /proc/1/environ | tr '[=12=]' '\n' > /home/env_var_file
# we can then reuse the file to extract the value of interest against a key
$ awk -F= -v key="MY_ENV_VARIABLE" '==key {print }' /home/env_file
some_val
现在在 system-d 单元文件的 ExecStart
指令中我们可以这样做:
[Service]
Type=simple
ExecStart=/bin/bash -c "cat /proc/1/environ | tr '[=13=]' '\n' > /home/env_file; export MY_ENV_VARIABLE=$(awk -F= -v key="MY_ENV_VARIABLE" '==key {print }' /home/env_file); /usr/bin/python3 /usr/bin/my_python_script.py"