如何在基于systemd的Ubuntu Docker镜像中加载环境变量?
我之前也踩过一模一样的坑——systemd作为Docker容器的PID 1启动时,会初始化一个完全独立的环境,根本不会继承Dockerfile里定义的或者docker run时传递的环境变量。用runuser -l试图通过登录shell加载变量之所以没用,是因为登录shell读取的是系统默认的profile配置(比如/etc/profile、~/.profile),这些配置里根本没有Docker注入的变量。
下面给你两个实用的解决方案,按需选择:
方案一:将Docker环境变量持久化到登录shell配置中
我们可以在启动systemd之前,把/proc/1/environ里的变量写入到系统级的profile脚本里,这样任何登录shell(包括runuser -l启动的)都会自动加载这些变量。
修改你的Dockerfile如下:
FROM ubuntu-systemd ENV VNC_PW="some-password" # 创建一个脚本,把/proc/1/environ的变量导入到profile.d RUN echo '#!/bin/bash' > /usr/local/bin/import-docker-env.sh && \ echo 'while IFS= read -r -d "" env_var; do' >> /usr/local/bin/import-docker-env.sh && \ echo ' # 只导入合法的环境变量(避免无效行)' >> /usr/local/bin/import-docker-env.sh && \ echo ' if [[ "$env_var" =~ ^[A-Z_]+= ]]; then' >> /usr/local/bin/import-docker-env.sh && \ echo ' echo "export $env_var" >> /etc/profile.d/docker-env.sh' >> /usr/local/bin/import-docker-env.sh && \ echo ' fi' >> /usr/local/bin/import-docker-env.sh && \ echo 'done < /proc/1/environ' >> /usr/local/bin/import-docker-env.sh && \ chmod +x /usr/local/bin/import-docker-env.sh # 先执行导入脚本,再启动systemd ENTRYPOINT ["/bin/bash", "-c", "/usr/local/bin/import-docker-env.sh && /lib/systemd/systemd"]
这个脚本会把/proc/1/environ里以null分隔的环境变量,转换成export KEY=VALUE的格式写入/etc/profile.d/docker-env.sh——系统中所有用户的登录shell都会自动加载这个目录下的脚本,所以runuser -l root启动的shell就能拿到VNC_PW这些变量了。
方案二:在systemd Unit中直接加载环境变量
如果你不想把变量持久化到文件里(比如担心敏感信息泄露),可以直接在Unit文件的ExecStart里先导入环境变量,再执行脚本:
修改你的Unit文件中的ExecStart行:
ExecStart=/bin/bash -c 'while IFS= read -r -d "" env_var; do export "$env_var"; done < /proc/1/environ; /usr/sbin/runuser -l root -c "/some/script.sh"'
或者更简洁一点(如果你的环境里没有带特殊字符的变量):
ExecStart=/bin/bash -c 'export $(cat /proc/1/environ | tr "\0" "\n") && /usr/sbin/runuser -l root -c "/some/script.sh"'
这种方式的好处是,变量只在脚本执行的上下文里存在,不会被写到磁盘上,安全性更高。
额外补充:为什么runuser -l原来没用?
再理一下原理:systemd启动时会重置环境,只保留少数核心变量(比如PATH、LANG)。runuser -l确实会启动一个登录shell,但这个shell读取的是系统默认的profile配置,这些配置里并没有Docker注入的VNC_PW——除非我们手动把这些变量添加到profile里,或者在执行脚本时直接导入。
内容的提问来源于stack exchange,提问作者Joaquin Morcate

