我们的业务Docker
镜像是在centos/systemd镜像基础上构建的,业务进程由systemd
来启动。最近需要对业务逻辑进行改造,需要识别传入的环境变量。看上去是相当简单的改动,但在我们的进程中加入读取环境变量的逻辑却发现读取不到传入的变量内容,最终定位原因是在systemd
的环境变量的处理。
下面用简单的示例来说明。
首先基于centos/systemd
构建实验镜像。创建三个文件:Dockerfile
, envdemo.service
, envdemo.sh
。
Dockerfile
内容如下:
1 | FROM centos/systemd:latest |
envdemo.service
内容如下:
1 | [Unit] |
envdemo.sh
内容如下:
1 |
|
systemd
镜像的CMD
为/usr/sbin/init
, 它实际链接的就是systemd
:
1 | [root@c6b3a7575cf6 app]# ls -l /usr/sbin/init |
我们的进程/app/envdemo.sh
由systemd
启动运行。尽管docker
推荐在一个镜像中只运行一个进程,但在一些需要运行多个服务的场景,用systemd
启动这种方式还是比较优雅的。
构建我们的镜像:
1 | docker build --rm --no-cache -t envdemo . |
然后执行容器:
1 | docker run --name envdemo --rm -d --privileged -e ENV_DEMO="hello" envdemo |
之后进行容器查看:
1 | [root@default envdemo]# docker exec -it envdemo bash |
从我们的bash
进程中可以看到传入的环境变量的值。
查看日志/var/log/envdemo.log
, 却看到环境变量的值为空:
1 | [root@bb3f18d5bd77 app]# cat /var/log/envdemo.log |
调研后发现原来是systemd
启动进程时会清空所有的环境变量。知道了具体原因,就可以找到许多种不同的解决方案。
因为systemd
本身是容器的1
号进程,它会继承父进程的环境变量,因而在容器内可以从/proc/1/environ
中获取所有的环境变量信息。
修改envdemo.sh
为:
1 |
|
重新build
镜像并执行之后,可以看到成功获取到环境变量:
1 | [root@default envdemo]# docker exec -it envdemo bash |
除了上边的方法,也可以使用systemd
本身设置环境变量的方法来解决。systemd
支持两种设置环境变量的方式:
- 在
service
文件中通过Environment
指令设置,如:
1 | Environment=ENV_FOO="foo" |
- 也可以在使用
EnvironmentFile
指定一个文件,在文件中设置环境变量:
1 | EnvironmentFile=/app/env.file |
将envdemo.service
文件修改为:
1 | [Unit] |
将envdemo.sh
恢复为最初版本:
1 |
|
重新build
镜像。
创建环境变量文件env.file
:
1 | ENV_DEMO="env from file" |
启动容器时将env.file
映射到容器:
1 | docker run --name envdemo --rm -d --privileged -v `pwd`/env.file:/app/env.file envdemo |
进入容器查看, 可以看到env.file
中设置的环境变量:
1 | [root@default envdemo]# docker exec -it envdemo bash |
使用这种方式需要准备不同的文件并映射到不同的容器实例。我们实际部署使用docker-compose
, 更倾向使用只需要在docker-compose.yml
中设置不同的环境变量,而不用准备不同环境变量文件的方式。
能够解决问题的方法多种多样,也可以通过Dockerfile
中CMD
指定处理环境变量的程序来覆盖原有的CMD
, 在这程序最后再exec
调用/usr/sbin/init
, 比如:
1 | #!/bin/bash |
参考:
- https://stackoverflow.com/questions/52571577/issues-in-accessing-docker-environment-variables-in-systemd-service-files
- https://wiki.archlinux.org/title/Systemd/User#Environment_variables
- https://dev.to/darnahsan/how-to-make-systemd-have-access-to-environment-variables-583b
- https://www.flatcar.org/docs/latest/setup/systemd/environment-variables/
- https://www.bmc.com/blogs/docker-cmd-vs-entrypoint/
- https://www.cloudbees.com/blog/understanding-dockers-cmd-and-entrypoint-instructions
- https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/