0x1.背景
业务中遇到Python镜像使用crontab执行任务不执行的情况,通过一系列定位分析后发现crontab在处理环境变量和crontab任务文件解析存在问题。
环境
- 镜像: Python:3.7.5
- Crontab版本: 3.0pl1-127+deb8u2
crontab任务文件
1 | * * * * * /usr/local/bin/python job.py >> /tmp/job.log 2>&1 |
0x2.环境变量
python程序执行时采用os.environ
读取环境变量,在调试程序时发现docker镜像打包运行后,程序报错无法读取环境变量值。登陆到k8s环境中手动执行env命令发现是可以获取注入的环境变量,修改python文件打印env环境信息
1 | environ({'HOME': '/root', 'LOGNAME': 'root', 'PATH': '/usr/bin:/bin', 'SHELL': '/bin/sh', 'PWD': '/root', 'LC_CTYPE': 'C.UTF-8'}) |
上述环境是基础的环境变量信息,python程序无法在执行环境获取新增的环境变量。在查找资料过程中发现crontab守护进程会从/etc/environment
和/etc/default/locale
获取环境变量。在Debian环境中采用service的方式运行/usr/sbin/cron
, 定位对应的执行脚本
1 | cat /etc/init.d/cron |
shell脚本采用加载environment和locale文件将环境变量注入到当前进程环境中,根据crontab在bug#543895
描述推荐使用locale文件。
经过上述分析,需要调整Dockerfile相关文件操作;这里采用entrypoint.sh文件注入环境变量
1 | env >> /etc/default/locale |
0x3.任务解析与权限
crontab任务创建有两种方式,一种是直接使用crontab命令手动编辑写入,另一种是将写好的任务文件复制到/var/spool/cron/crontabs
目录中。
排查某个业务问题时,发现crontab任务不能执行(采用文件复制形式);进入POD容器中使用cat命令查看任务文件也是显示正常,但是任务还是不能执行。后来通过crontab查看保存后,任务能正常执行。
回溯两次操作时,发现两个有意思的问题。问题一是crontab命令编辑任务文件保存后,该任务文件的组权限变成root:crontab
(原root:root);问题二是crontab命令编辑查看文件后直接保存,文件大小发生改变,此过程无人为更改内容。
问题一的出现是因为crontab命令编辑文件时会设置任务文件的组,Debian镜像环境中只有root用户,用户组列表中是存在crontab组。由于Docker环境中只有root用户执行,不需要调整任务文件的权限;但非Docker环境中,crontab的任务文件一般设置600权限居多;cron守护进程读取文件时会校验权限。
1 |
|
问题二的出现比较诡异,文件大小变化但cat查看文件内容没有明显变化。通过xxd命令查看文件改动前后的十六进制信息
crontab改动前
1
2
3
400000000: 3020 3020 2a20 2a20 2a20 2f75 7372 2f6c 0 0 * * * /usr/l
00000010: 6f63 616c 2f62 696e 2f70 7974 686f 6e20 ocal/bin/python
//省略...
000000f0: 2e6c 6f67 2032 3e26 31 .log 2>&1crontab改动后
1
2
3
400000000: 3020 3020 2a20 2a20 2a20 2f75 7372 2f6c 0 0 * * * /usr/l
00000010: 6f63 616c 2f62 696e 2f70 7974 686f 6e20 ocal/bin/python
//省略...
000000f0: 2e6c 6f67 2032 3e26 310a .log 2>&1.注意到文件结尾处的区别,改动后多了
0x0a
标志信息即文件换行符LF
。由于任务文件最后一行没有换行符号导致不能加载该条命令。
1 | //https://github.com/ushell/learncode/blob/master/crontab/debian/cron-3.0pl1/misc.c#L329 |
0x4.手工调试
基于Debian系统的crontab没有日志记录,调试代码时需要打开 cron.h文件中DEBUGGING
;重新编译后,crontab执行时会打印一些关键的DEBUG信息方便排查。
注意编译后参数-x的值问题
1 | 示例 |
0x5.总结
crontab是Linux环境中最常用的定时任务执行程序,由于操作系统不同crontab的实现方式也不一样。在Docker环境中,基于Debian系统使用的是3.0pl1-127
版本,Alpine系统中使用的busybox版本。前者程序比较完善但是不支持日志记录(需要修改源码), 后者比较轻量级支持前后台及日志记录等功能。
由于crontab最新一版的更新是2004年左右,Debian系统打算采用cronie
代替目前crontab。业务系统中可以根据其他版本的定时程序代替。