最近发现VPS上的系统自动更新cron似乎没有成功执行,排查解决问题之后,梳理总结了最近学习的关于确保cron顺利执行的一些关键点。
问题的由来、排查和解决
原来设置的cron如下,即每天9点更新系统:
00 09 * * * /usr/bin/apt-get update && /usr/bin/apt-get upgrade -q -y > /dev/null 2>&1
其未能顺利执行的原因是环境变量不满足程序要求。解决办法是,新建shell脚本如下:
#! /bin/bash
PATH=$PATH:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
/usr/bin/apt-get update && /usr/bin/apt-get upgrade -q -y > /root/app/system_upgrade.log 2>&1
给脚本赋予可执行权限:
chomod a+x /root/app/upgrade_system.sh
然后将相应的cron改为:
* * * * * /root/app/upgrade_system.sh > /root/app/upgrade_system.log 2>&1
就顺利执行了。
我是怎么确定PATH环境变量导致cron不能按预期执行的呢?我们可以通过cron打印错误日志看看。将原来的任务设为每分钟执行,并将stdout、stderror重定向到/tmp/debug.log,然后等一会儿看一下输出日志。在这里调试的是系统更新,我们设置这条cron:
* * * * * /usr/bin/apt-get update && /usr/bin/apt-get upgrade -q -y > /root/apt_upgrade.log 2>&1
等一会儿看一下日志内容如下:
Reading package lists... Building dependency tree... Reading state information... Calculating upgrade... The following packages will be upgraded: curl libcurl3 libcurl3-gnutls debconf: unable to initialize frontend: Dialog debconf: (TERM is not set, so the dialog frontend is not usable.) debconf: falling back to frontend: Readline debconf: unable to initialize frontend: Readline debconf: (This frontend requires a controlling tty.) debconf: falling back to frontend: Teletype dpkg-preconfigure: unable to re-open stdin: 3 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 0 B/509 kB of archives. After this operation, 0 B of additional disk space will be used. dpkg: warning: 'ldconfig' not found in PATH or not executable dpkg: warning: 'start-stop-daemon' not found in PATH or not executable dpkg: error: 2 expected programs not found in PATH or not executable Note: root's PATH should usually contain /usr/local/sbin, /usr/sbin and /sbin FATAL -> Failed to fork.
很明显是因为PATH中没有/sbin、/usr/sbin、/usr/local/sbin,执行过程中没有找到相应的依赖组件造成的。所以才有了上面的解决方案。
cron未按预期执行的常见原因
这里总结cron未能按预期执行的常见原因:
1、cron服务未运行
可以通过pgrep cron
查看,如果无进程号输出,则用service cron start
将其启动。
2、输出定向失败
如果程序、脚本执行过程中有stdout、stderror输出,系统应能将其顺利重定向,默认是通过mta给用户发信,如果没有安装mta,就会执行失败,可以安装postfix解决。也可以将cron的stdout、stderror重定向到/dev/null或者日志文件。
# 安装mta sudo apt-get install postfix # 或者按如下格式将stdout、stderror重定向,/dev/null可以替换为文件路径 your_command >/dev/null 2>&1
3、误解了cron任务规则
cron中的定时数字是“绝对数字”,也就是60分钟的第几分钟,24小时的第几小时,而非每几分钟、几小时。所以cron每分钟执行是* * * * *
,每三分钟执行是*/3 * * * *
,调试时可以使用每分钟执行。
4、PATH环境变量不合要求
环境变量尤其是PATH是否正确,要执行的脚本、程序是否在路径中,默认的PATH中只有/usr/bin和/bin,可以在shell脚本开头加入以下语句:
PATH=$PATH:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
5、程序要求的其它特殊环境变量不合要求
例如apt更新系统可能要求DEBIAN_FRONTEND=noninteractive
。
其它
考虑替换方案
有些任务不能简单通过在cron中写一条命令完成,或者写出来也很繁琐,或者嫌写脚本麻烦、移植性不强,可以考虑使用现成的方案,尤其一些常见的定时任务,可能都小程序可以替代执行。例如定时更新系统,可以使用unattended-upgrades
。
深入了解ssh和cron的环境变量差异
一些你ssh登陆后能顺利执行的程序,放到cron后执行就失败,很可能是环境变量导致的,可以详细了解下cron的env和ssh登陆后的env的差异,避免以后碰到类似的麻烦。这里提供我测试输出的两个环境的变量,供大家参考了解:
cron执行环境的env输出:
HOME=/root LOGNAME=root PATH=/usr/bin:/bin LANG=en_US.UTF-8 SHELL=/bin/sh PWD=/root
ssh执行环境的env输出,已将个别隐私信息涂抹:
XDG_SESSION_ID=9062 TERM=cygwin SHELL=/bin/bash SSH_CLIENT=x.x.x.x 8703 22 SSH_TTY=/dev/pts/0 USER=root LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: MAIL=/var/mail/root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin PWD=/root LANG=en_US.UTF-8 SHLVL=1 HOME=/root LOGNAME=root XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop SSH_CONNECTION=x.x.x.x 8703 x.x.x.x 22 LESSOPEN=| /usr/bin/lesspipe %s XDG_RUNTIME_DIR=/run/user/0 LESSCLOSE=/usr/bin/lesspipe %s %s _=/usr/bin/env
参考资料
- Why crontab scripts are not working?
- “(CRON) info (No MTA installed, discarding output)” error in the syslog
- apt-get upgrade not installing upgrades through crontab job
- apt-get upgrade not installing upgrades through crontab job
-- EOF --
本文最后修改于6年前 (2018-09-18)