事情的起因

同事告诉我我们线上一组游戏服进不去了,我查了一下错误日志,看到 Redis 服务报了以下错误

MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. 
Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

以及 Redis 错误日志中的

1
2
3
4
5
6
7
8
[root@iZ232dbhg3uZ t2]# tail /data/data/t2/run.log 
[18727] 22 Dec 11:22:12.193 # Background saving error
[22817] 22 Dec 11:22:18.016 # Failed opening .rdb for saving: No space left on device
[18727] 22 Dec 11:22:18.107 # Background saving error
[22823] 22 Dec 11:22:24.029 # Failed opening .rdb for saving: No space left on device
[18727] 22 Dec 11:22:24.121 # Background saving error
[22828] 22 Dec 11:22:30.047 # Failed opening .rdb for saving: No space left on device
[18727] 22 Dec 11:22:30.134 # Background saving error

顺着这两个错误信息,我找到几个解决思路。 先确认自己的配置地址有没有问题,查到目录后在 Linux ls -l 确认确实存在。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[root@iZ232dbhg3uZ t2]# sh /data/www/t2/tools/loginRedis.sh 
127.0.0.1:8201> config get dir
1) "dir"
2) "/data/data/t2"
127.0.0.1:8201> config get dbfilename
1) "dbfilename"
2) "dump.rdb"
127.0.0.1:8201> config get stop-writes-on-bgsave-error
1) "stop-writes-on-bgsave-error"
2) "yes"

https://stackoverflow.com/questions/19581059/misconf-redis-is-configured-to-save-rdb-snapshots 所说修改 dbfilename 为 tmp.rdb 尝试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
127.0.0.1:8201> config set dbfilename tmp.rdb
OK
127.0.0.1:8201> config set stop-writes-on-bgsave-error no
OK
127.0.0.1:8201> BGSAVE
Background saving started

tail /data/data/t2/run.log 
[22833] 22 Dec 11:22:36.057 # Failed opening .rdb for saving: No space left on device
[18727] 22 Dec 11:22:36.148 # Background saving error

结果很不理解依然报错。。

查看一下进程吧,然后突然觉得可能发现问题了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[root@iZ232dbhg3uZ t2]# ps afx
21326 ?        S      0:00  \_ CROND
21370 ?        S      0:00  |   \_ /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root
21373 ?        S      0:00  |       \_ /usr/sbin/postdrop -r
21327 ?        S      0:00  \_ CROND
21409 ?        S      0:00  |   \_ /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root
21423 ?        S      0:00  |       \_ /usr/sbin/postdrop -r
21328 ?        S      0:00  \_ CROND
21406 ?        S      0:00  |   \_ /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root
21425 ?        S      0:00  |       \_ /usr/sbin/postdrop -r
21479 ?        S      0:00  \_ CROND
21565 ?        S      0:00  |   \_ /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root
21580 ?        S      0:00  |       \_ /usr/sbin/postdrop -r
21480 ?        S      0:00  \_ CROND
21564 ?        S      0:00  |   \_ /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root
21579 ?        S      0:00  |       \_ /usr/sbin/postdrop -r
21481 ?        S      0:00  \_ CROND
21561 ?        S      0:00  |   \_ /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root

怎么会有这么多的sendmail和postdrop呢?继续查看 mail 日志吧

1
2
3
4
5
6
7
[root@iZ232dbhg3uZ postfix]# tail /var/log/maillog
Dec 22 11:22:33 iZ232dbhg3uZ postfix/postdrop[18902]: warning: mail_queue_enter: create file maildrop/712248.18902: No space left on device
Dec 22 11:22:33 iZ232dbhg3uZ postfix/postdrop[18900]: warning: mail_queue_enter: create file maildrop/740438.18900: No space left on device
Dec 22 11:22:33 iZ232dbhg3uZ postfix/postdrop[18898]: warning: mail_queue_enter: create file maildrop/772643.18898: No space left on device
Dec 22 11:22:33 iZ232dbhg3uZ postfix/postdrop[18669]: warning: mail_queue_enter: create file maildrop/788042.18669: No space left on device
Dec 22 11:22:33 iZ232dbhg3uZ postfix/postdrop[18715]: warning: mail_queue_enter: create file maildrop/799093.18715: No space left on device
Dec 22 11:22:33 iZ232dbhg3uZ postfix/postdrop[18717]: warning: mail_queue_enter: create file maildrop/806610.18717: No space left on device

终于找到问题的真凶了,sendmail自己一旦邮件发送不成功,就持续重新发送而导致持续启动postdrop,而postdrop总是执行失败,导致持续写入日志到日志文件。日志文件增大的速率超过了logrotate的删除频率,所以占据了100%的磁盘空间。并且线程数也不断的增加。https://segmentfault.com/a/1190000000800723

先看看搞了多少个垃圾文件吧。

1
2
[root@iZ232dbhg3uZ t2]# ls -l /var/spool/postfix/maildrop/ | wc -l
1139685

shit 居然这么多,赶紧干掉。

1
2
[root@iZ232dbhg3uZ t2]# rm -rf /var/spool/postfix/maildrop/*
-bash: /bin/rm: Argument list too long

由于文件太多居然无法删除。。。1234换个姿势再来一次。。

1
2
[root@iZ232dbhg3uZ postfix]# rm -rf maildrop
rm: cannot remove `maildrop': Directory not empty

这里我没有想明白为什么我带了 -rf 依然删不掉这个目录,不过没关系,1234换个姿势再来一次。。

1
[root@iZ232dbhg3uZ postfix]# find ./maildrop -type f | xargs rm

终于把所有的文件都清理干净了,再 kill 掉 sendmail 和 postdrop 进程

1
2
killall -9 sendmail
killall -9 postdrop

再回来我们的 Redis 服务 执行 bgsave 果然也不报错了。

但是问题的根本还是没有解决,在 Linux 执行 cron 时,会将 cron 脚本中的 output 和 warning 信息都会以邮件的形式发给给 cron 的所有者。

但是环境中又没有 sendmail 或 postfix,导致发送邮件失败,所以会在 maildrop 目录生成临时文件,如果不定时清理就会积少成多,最终导致生产环境出现莫名其妙的问题甚至整个系统crash掉。

1
2
3
[root@iZ232dbhg3uZ maildrop]# grep -inR "MAILTO" /etc/cron*
/etc/cron.d/0hourly:3:MAILTO=root
/etc/crontab:3:MAILTO=root

将上述的 MAILTO=root 改为MAILTO=””

1
2
3
[root@iZ232dbhg3uZ maildrop]# service crond restart
Stopping crond:                                            [  OK  ]
Starting crond:                                              [  OK  ]

还有一个方法是使用重定向到空

1
*/1 * * * * nginx sh youbash.sh 2>&1 > /dev/null

这两种都方法都可以让 cron 执行后不发送邮件