让crontab使用smtp服务器向管理员发送报错邮件

为什么80%的码农都做不了架构师?>>>   hot3.png

#前言#


crontab有一个邮件发送的机制,如果crontab执行脚本时,stdout有输出时,会以邮件的形式发送到crontab当前用户,使用mail命令即可查看邮件。这些信息经常很有用,往往携带了错误信息,可以帮助管理员排错,因此管理员对于mail不可不关注。

注意:

想要看到crontab报警邮件需要有邮件服务器,在centos/RHEL,ubuntu/Debian等主流发行版都会有一个mailutils软件包,这个软件包就二选一依赖于邮件服务端软件postfix/sendmail和客户端软件mail

(早一些的发行版,如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,增加两行:

[email protected]
* * * * * 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 [email protected]
user [email protected]
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中定义的[email protected]并不会影响到crontab -e!依然需要在crontab -e中重新写上[email protected],再加上系统中有各种拆散的crontab(如/etc/cron.d/,/etc/cron.daily等等)。

但是对于MAILTO这个变量问题,有一个变通的解决方案。

还记得在msmtp配置中提到的aliases /etc/aliases这个配置段吗?根据msmtp的man手册,这个代表使用指定文件定义aliases,默认为空。

/etc/aliases这个文件是sendmail的邮件别名,如果之前安装过postfix/sendmail的话,是存在这个文件的,指明了如果发到哪个用户的邮件,自动使用别名命名成别的地址。

这个文件的格式也很简单user: email_address即可,如root: [email protected],这样的话发到root的邮件就会自动被别名替换而发送到[email protected]

我的配置: default: [email protected],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即可,殊途同归。

转载于:https://my.oschina.net/abcfy2/blog/200351

你可能感兴趣的:(shell,开发工具,运维)