背景
最近在搞一个比较复杂的crontab 定时任务, 核心思路就是通过crontab触发一个bash脚本,调用docker内的一个指令,来实现docker积压日志的删除清理操作。
然后问题就来了:我通过命令行调用脚本,能够正常执行,但是通过crontab,脚本就死活调用不成功。
以下就是记录的排查crontab调用不成功的过程:
案发现场
crontab上配置定时任务,把该有的环境变量都配置上,脚本的开头也加上了source /etc/profile
为了方便排查,调度改为2分钟一次
*/2 * * * * /usr/bin/docker exec -it container-name bash -c 'sh /var/log/del_overdue_log.sh'
不出意外的话,定时任务调度失败了。
通过tail -f /var/log/cron
去cron调度日志里查看调用情况:
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,改成如下配置
inet_interfaces = all
# inet_interfaces = localhost
重启postfix
systemctl restart postfix
此时,我们就可以收到cron调度失败的系统通知邮件了。
tail -f /var/spool/mail/root
查看邮件信息:
From root@host.localdomain Fri Oct 13 12:56:01 2023
Return-Path: <root@host.localdomain>
X-Original-To: root
Delivered-To: root@host.localdomain
Received: 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.localdomain
Subject: 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-generated
Precedence: bulk
X-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 * * * /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
文档信息
- 本文作者:寒澈
- 本文链接:https://www.hancher.top/2023/10/13/crontab_not_work/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)