linux定时任务crontab不执行的问题排查过程

2023/10/13 编程人生 共 2248 字,约 7 分钟

背景

最近在搞一个比较复杂的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 

最终指令改了两点:

  1. -it改为-d, 解决crontab调度失败的问题。
  2. 在crontab命令上增加 >/dev/null 2>$1 配置,这是为了取消任务调度的系统日志产生,防止系统日志爆炸,也可以不加。

结论

授之以渔不如授之以渔, 出现问题不可怕,只要我们掌握了一些解决问题的方法论, 任何问题都能迎刃而解。

参考

‘crontab’ in Linux with Examples

crontab不执行排查

文档信息

Search

    Table of Contents