#前言#
crontab有一个邮件发送的机制,如果crontab执行脚本时,stdout有输出时,会以邮件的形式发送到crontab当前用户,使用mail
命令即可查看邮件。这些信息经常很有用,往往携带了错误信息,可以帮助管理员排错,因此管理员对于mail不可不关注。
注意:
想要看到crontab报警邮件需要有邮件服务器,在centos/RHEL,ubuntu/Debian等主流发行版都会有一个
mailutils
软件包,这个软件包就二选一依赖于邮件服务端软件postfix
/sendmail
和客户端软件(早一些的发行版,如centos/RHEL5 优先依赖的是sendmail,centos6,ubuntu12.04等新一些的发行版,优先依赖的是postfix)
安装
mailutils
之后,默认配置即可收到crontab的邮件,使用mail命令即可看到邮件详情。
但当服务器比较多时,一台台ssh上去敲mail
命令显然不合适,有没有方法能让crontab把邮件发送到管理员的邮箱呢?crontab已经有这种设定了。
研究了一天,google了一天老外的资料,眼睛都花了(希望自己动手解决的可以google一下各种关于crontab smtp之类的关键字,老外提到的基本集中在几个点),加上自己的各种尝试,总结方法如下:
#MAILTO变量#
首先值得关注的是MAILTO
这个变量,这个在crontab的man手册写的也很清楚:
In addition to LOGNAME, HOME, and SHELL, cron(8) will look at MAILTO if it has any reason to send mail as a result of running commands in ``this'' crontab. If MAILTO is defined (and non-empty),
mail is sent to the user so named. MAILTO may also be used to direct mail to multiple recipients by separating recipient users with a comma. If MAILTO is defined but empty (MAILTO=""), no mail
will be sent. Otherwise mail is sent to the owner of the crontab.
也就是说,定义了MAILTO
这个变量,那么crontab会将stdout的内容以邮件的形式发送到MAILTO定义的邮箱中。
测试一下,编辑/etc/crontab
,增加两行:
MAILTO=myuser@mydomain.com
* * * * * root echo "mail test"
如果没什么问题的话,__有可能__会在一分钟以后收到cron的邮件。
#smtp#
如果上面的方案能起作用的话,那么恭喜你,你省了很多麻烦。下面我要说的就是我折腾了一天后才搞定的玩意——smtp!
上面说有可能会收到邮件,是因为邮件发送机制决定的,以下表述可能不准确,是按照我自己的理解来说的,可能会误导。邮件的发送方和接收方需要有域名,可以被DNS正确解析的。如果你的服务器有域名,那么恭喜你,只需挂上自己的域名,配置一下postfix/sendmail允许向外发送邮件即可。如果没有域名,或者服务器在内网的话,这种方式就行不通了(这里可能有误,我有一台服务器在域名那里直接写的是主机IP,可以发送出去。但是另一台这样做就不行,猜测是否跟IP能反解析有关)
下面介绍的是一种更通用的形式——smtp协议!
首先需要装smtp客户端,老外推荐更多的是msmtp
这个smtp客户端,我的系统是ubuntu-server 12.04,以下命令全部都是ubuntu 12.04上可以用的命令,如果是其他发行版,做相应变更即可
使用smtp发送邮件的话,那么postfix/sendmail就不再需要了,可以卸载掉
apt-get purge postfix #彻底卸载postfix(如果之前使用的服务端是sendmail的话就改成sendmail)
apt-get purge mailutils #彻底卸载mailutils,mail都发送到外部邮箱去了,因此mail命令也没用了,如果还想在本地发送邮件的话,保留也可以
apt-get auto-remove #清理掉不再使用的依赖项
##安装msmtp## 之后安装msmtp,注意的是有两个软件包都要装:
apt-get install msmtp-mta #只装这一个就可以了,因为这个软件包依赖于msmtp,会自动安装。msmtp-mta这个软件包会将/usr/sbin/sendmail --> /usr/bin/msmtp做一个软连接,也就是会将原来使用sendmail发送邮件的程序改为使用msmtp发送。
如果此时安装mailutils
(可选),会发现由于存在/usr/sbin/sendmail,所以既不会安装sendmail也不会安装postfix。
##配置msmtp## msmtp安装完毕并不存在全局配置文件,需要时可以自己建立一个。具体的命令行语法就不多介绍了,看--help
帮助就可以了。
msmtp支持全局配置/etc/msmtprc
和用户个性配置~/.msmtprc
,需要所有用户使用同样的msmtp配置的话,只建立/etc/msmtprc
就可以了,以下是我的配置
vim /etc/msmtprc
defaults
logfile /var/log/msmtp.log #注意权限,普通用户可能没有写入这个文件的权限,可以管理员授权
syslog on #这个选项是记录到syslog的,可以去掉这一行,默认是off
aliases /etc/aliases #先看看这个文件是否存在,如果不存在就不要加这一行,这一行的作用后面再说
account default
host smtp.ym.163.com
from user@domain.com
user user@domain.com
password mypass
auth on
tls on
tls_certcheck off
我的配置,使用网易企业邮箱的smtp服务器发送邮件的,开启了tls。这里根据需要自己调整,如果使用QQ邮箱或是gmail等其他邮箱,请参考邮箱帮助文档配置smtp。
配置完毕之后测试一下
msmtp -Sd
看一下输出,检测一下变量,看看有没有问题,发送一封邮件试试echo -e "Subject: Test Mail\r\n\r\nThis is a test mail" |msmtp --debug -t youremail@domain
,看一下输出,如果能收到邮件的话,那么msmtp的配置就结束了。
##使用msmtp替换sendmail,成为crontab发送邮件的客户端##
只需安装msmtp-mta
这个软件包即可,会自动做软连接,上面提到过,如果没装的话,也可以手工软连接/usr/lib/sendmail
和/usr/sbin/sendmail
两个文件到/usr/bin/msmtp
,之后如果crontab发送邮件的时候,就会使用msmtp了。
##遇到的问题## 1:CRON[28742]: (root) MAIL (mailed 1 byte of output; but got status 0x004e, #012)
这是由于已经使用了smtp代替了sendmail/postfix,已经连接到了smtp服务器上,smtp服务器可不识别root、mail这样的用户名作为邮件地址,所以报错了。配合开始提到的
MAILTO
变量,在crontab中写入MAILTO
这个变量,强制指定将邮件发送到目标邮箱中即可。
2:中文乱码,效果如下:
������ english
ls: ������������/asldkfjasdf: ���������������������������
LANG=zh_CN.UTF-8
LANGUAGE=zh_CN:zh
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=zh_CN.UTF-8
记得邮件有
content type
这个属性吗?设置为text/plain; charset=utf-8
就可以搞定了。crontab有一个CONTENT_TYPE
变量(详见man 5 crontab
,搜索charset
这个关键字即可找到相关内容),在crontab中定义CONTENT_TYPE=text/plain; charset=utf-8
就可以了
#依旧没解决的问题#
##crontab全局变量问题##
试了很多次,发现所有的crontab文件的环境变量都是独立的,也就是说在/etc/crontab
中定义的MAILTO=yourmail@domain.com
并不会影响到crontab -e
!依然需要在crontab -e
中重新写上MAILTO=yourmail@domain.com
,再加上系统中有各种拆散的crontab(如/etc/cron.d/,/etc/cron.daily等等)。
但是对于MAILTO
这个变量问题,有一个变通的解决方案。
还记得在msmtp配置中提到的aliases /etc/aliases
这个配置段吗?根据msmtp的man手册,这个代表使用指定文件定义aliases,默认为空。
/etc/aliases
这个文件是sendmail的邮件别名,如果之前安装过postfix/sendmail的话,是存在这个文件的,指明了如果发到哪个用户的邮件,自动使用别名命名成别的地址。
这个文件的格式也很简单user: email_address
即可,如root: myemail@qq.com
,这样的话发到root的邮件就会自动被别名替换而发送到myemail@qq.com
。
我的配置: default: myemail@163.com
,default代表缺省,即不指定的话,所有发送到用户的邮件都将被这个别名替换,这样就不需要所有的crontab去指定MAILTO
这个变量了,缺省会发送到crontab用户中,然后被别名替换。
但是对于CONTENT_TYPE
这个变量我真的没有什么好办法了,希望有朋友支招,只能一个个写了……
所幸的是基本上只有自己写的脚本才有中文输出,系统命令大多是英文的,所以暂时只把这个变量加到了crontab -e
中,以后如果哪个出现中文乱码了再在哪个crontab中加吧。
google到老外的一种解决方案,就是给
cron
守护进程传递参数,让cron启动的时候初始化环境变量,就会成为crontab中全局变量,我看ubuntu的crontab的man手册,也提到了自定义变量写在/etc/default/corn中,但是打开这个文件,提示这个文件已经不再支持了,如果有自定义变量的需求,写入/etc/init/cron.conf
或/etc/init/cron.override
,然后我就不会搞了,也没google到别人怎么写cron.override的。希望有知道的朋友不吝赐教!
好了,就这么多了,困得不行了,睡觉先……
后记
2016-09-07: 感谢回复的朋友,其实理论上只要存在/usr/sbin/sendmail
即可,所以可以自己写一个shell脚本伪造成/usr/sbin/sendmail
即可,殊途同归。