背景 最近在搞一个比较复杂的crontab 定时任务, 核心思路就是通过crontab触发一个bash脚本,调用docker内的一个指令,来实现docker积压日志的删除清理操作。
然后问题就来了:我通过命令行调用脚本,能够正常执行,但是通过crontab,脚本就死活调用不成功。
以下就是记录的排查crontab调用不成功的过程:
案发现场 crontab上配置定时任务,把该有的环境变量都配置上,脚本的开头也加上了source /etc/profile
为了方便排查,调度改为2分钟一次
1 */2 * * * * /usr/bin/docker exec -it container-name bash -c 'sh /var/log/del_overdue_log.sh'
不出意外的话,定时任务调度失败了。
通过tail -f /var/log/cron
去cron调度日志里查看调用情况:
1 2 Oct 13 10:58:01 : CROND[27764]: (root) CMD (/usr/bin/docker exec -it container-name bash -c 'sh /var/log/del_overdue_log.sh' ) Oct 13 10:58:01 : (root) MAIL (mailed 54 bytes of output but got status 0x004b#012)
日志表示,我们的cron任务调度了,但是没有成功。 失败原因本来想通过mail发给当前root用户的,但发送失败了,原因是获得了一个失败状态吗。
如何解决这个mail发送失败的问题呢?
启用postfix 接收系统邮件 去/etc/postfix/main.cf
配置文件,搜索inet_interfaces,改成如下配置
重启postfix
1 systemctl restart postfix
此时,我们就可以收到cron调度失败的系统通知邮件了。
tail -f /var/spool/mail/root
查看邮件信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 From root@host.localdomain Fri Oct 13 12 :56 :01 2023 Return -Path: <root@host.localdomain>X -Original-To: rootDelivered -To: root@host.localdomainReceived : by host.localdomain (Postfix, from userid 0 ) id D94301329A7; Fri, 13 Oct 2023 12 :56 :01 +0800 (CST)From : "(Cron Daemon)" <root@host.localdomain>To : root@host.localdomainSubject : Cron <root@host> /usr/bin/docker exec -it container-name bash -c 'sh /var/log/del_overdue_log.sh'Content -Type: text/plain; charset=UTF-8 Auto -Submitted: auto-generatedPrecedence : bulkX -Cron-Env: <XDG_SESSION_ID=2387 >X -Cron-Env: <XDG_RUNTIME_DIR=/run/user/0 >X -Cron-Env: <LANG=en_US.UTF-8 >X -Cron-Env: <SHELL=/bin/sh>X -Cron-Env: <HOME=/root>X -Cron-Env: <PATH=/usr/bin:/bin>X -Cron-Env: <LOGNAME=root>X -Cron-Env: <USER=root>Message -Id: <20231013045601 .D94301329A7@host.localdomain>Date : Fri, 13 Oct 2023 12 :56 :01 +0800 (CST)the input device is not a TTY
会发现,失败的原因是我们的脚本调度器不是TTY(终端),所以失败了。
注意,问题定位后记得把postfix配置恢复原状
问题定位 找到日志了,任务调度失败的原因也找到了,分析一下我们的任务指令,发现我们在脚本调度命令里存在选项 docker exec -it
配置。
问题就在这个-it
上面了,这要求我们开启一个交互式终端来控制docker指令,而crontab调度器明显不是一个终端,所以失败了。
问题解决 把命令行里的-it
去掉,改为-d
后端调用就可以了。以下是最终版配置
1 * 1 * * * /usr/bin/docker exec -d container-name bash -c 'sh /var/log/del_overdue_log.sh' >/dev/null 2>$1
最终指令改了两点:
将-it
改为-d
, 解决crontab调度失败的问题。
在crontab命令上增加 >/dev/null 2>$1
配置,这是为了取消任务调度的系统日志产生,防止系统日志爆炸,也可以不加。
结论 授之以渔不如授之以渔, 出现问题不可怕,只要我们掌握了一些解决问题的方法论, 任何问题都能迎刃而解。
参考 ‘crontab’ in Linux with Examples
crontab不执行排查