问题背景
我们使用docker时,典型的用法就是在启动docker容器时,通过给docker run命令加上-e选项来给要在docker里面执行的命令传入参数. 然后在docker里面的进程中通过读取环境变量的方式得到这个输入值,如下.
docker run -d -it -e testkey=testvalue 93fd78260bd1 bashdocker exec -it 189 bashenv
可以看到testkey=testvalue这个环境变量可以成功的看到.
但是有次我在docker里面启动了一个crontab service,然后用bash写了一个脚本. 我自己手动调用这个脚本可以正常运行,环境变量也可以正常解析. 但是crontab定时拉起来的进程里面就得不到这些环境变量,让人觉得很是诡异.
问题定位
我们知道在Linux里面,子进程是通过父进程fork+exec出来的,在exec的时候,可以通过一个额外的参数envp来指定子进程的环境变量,如果不指定,那么就是用父进程的环境变量.大部分的服务都会使用父进程环境变量.
所有我们可以想象,crond在启动子进程的时候,是显示指定了环境变量的,不然我们需要的环境变量不会没有了.
我们先写一个最简单的脚本来验证一下.脚本内容如下,其中exec &> /root/t1.log表示整个脚本的标准输出和标准错误直接写入到文件/root/t1.log中,env命令打印出当前进程的环境变量.脚本的效果就是将脚本的环境变量输出到/root/t1.log中.
我们先在登录的shell中直接运行一下,
注意到环境变量有很多,而且其中的PATH里面包含了挺多的路径的.
然后我们加上如下的crontab任务,就是每分钟都是运行一下这个脚本.
然后看了下执行输出的日志,发现里面的环境变量只有固定的几个值,而且明显的PATH环境变量只有很少的路径了.
为了验证其环境变量的值和crond的也不相同,我们找到crond的pid,然后通过proc得到其环境变量,可以看到PATH的值也是不同的.
ps auxf |grep crondstrings -n 1 /proc/1018/environ
原来crontab启动一个子进程的时候,其只会设置最基本的环境变量.也就是不管启动crond的进程给传入了什么样的环境变量,crond在fork出子进程,exec的时候只会给envp传入固定的值,导致拉起的脚本里面没有额外的环境变量.
在docker里面运行crontab
那么在docker里面使用crontab的时候,如果需要用到传入的环境变量,可以在entrypoint的脚本中加入下面的命令将环境变量的值写入的文件/dockerenv中
env > /dockerenv
然后再将crond打开
service cron start
在crontab拉起的脚本中一开始就加入
source /dockerenv
这样传入给docker的环境变量就可以在docker中的crond拉起的定时任务中使用了.
不知道大家有没有遇到这个坑,如果有更好的解决方法,欢迎讨论.