【问题追查】Linux随机数发生器导致Apache进程全部被block
故障发生
今天上午,运营同事报告一个严重故障,现象是网站页面速度非常慢,基本不可用。工程师开始追查问题。
系统架构:前端 Apache,中间PHP,后端MySQL,经典的LAMP架构。
第一反应,怀疑数据库数据量太大。我们一直定期清理数据库,保证单表数据量在一定范围内。
而这段时间一直没有清理,数据量可能过大。立刻执行delete语句,单表减少100W条记录。但是,问题依旧。
后来,DBA同学发现慢查询,存在filesort。果断增加索引,慢查询消失。但是,问题依旧。
无奈之下,怀疑机房网络问题。但是其他产品线都没有问题,网络组也没有故障通报。因此,否决这种可能。
时间已经到了下午5点,决定重新梳理思路,反复观察故障现象。总结几点:
由于没有权限看线上日志(其实代码中也没有打任何日志),无法从日志中获取有效信息。
为了方便调试,我们决定在线下环境部署代码并尝试复现故障。
结果,线下环境中,故障基本必现。可以肯定,与网络环境没有关系,与线上的memcahced/mysql的运行状况也没有关系。
我们在PHP代码的不同位置插入调试代码 echo ‘xxx’;exit; , 试图确定何处代码在堵塞。
我们惊奇的发现两个现象:
我们推断,Aapache进程全部被block住了!
为了搞清楚Apache进程block在什么地方,我们使用strace工具观察Apache进程的系统调用。
先找出Apache的进程号。
在这个实现中,发生器保存了来自熵池中噪声的数据位数的估计值,而随机数是从该熵池中创建的。在读取时,/dev/random设备只会返回熵池中噪声数据中的随机字节。/dev/random应当可以适用于要求非常高质量随机性的应用,例如产生公钥或一次性密码本。若熵池空了,对/dev/random的读操作将会被阻塞,直到收集到了足够的环境噪声为止[3]。这样的设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵,建议用于产生保护高价值或长保护周期的密钥。/dev/random的一个副本是/dev/urandom ("unlocked",非阻塞的随机数发生器[4]),它会重用内部池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。该设备文件是设计用于密码学安全的伪随机数发生器的,可以用于安全性较低的应用。
/dev/urandom则不会堵塞。有得必有失,urandom的随机性弱于random。
熵池本质上是若干字节。/proc/sys/kernel/random/entropy_avail中存储了熵池现在的大小,/proc/sys/kernel/random/poolsize是熵池的最大容量,单位都是bit。
如果entropy_avail的值小于要产生的随机数bit数,那么/dev/random就会堵塞。
那么,为什么熵池不够用呢?
google了一下资料,熵池实际上是从各种noice source中获取数据,noice source可能是 键盘事件、鼠标事件、设备时钟中等。linux内核从2.4升级到2.6时,处于安全性的考虑,废弃了一些source。source减少了,熵池补给的速度当然也变慢,进而不够用。
其实,通过消耗熵池,可以构造DOS攻击。原理很简单,熵池空了,依赖随机数的业务(SSL,加密等)就不能正常进行。
Linux服务器在运行时,既没有键盘事件,也没有鼠标事件,如何快速积累熵池呢?
google了一下资料,发现有一些程序可以自动补充熵池,例如rngd或rng-tools。
我在Linode VPS上尝试了一下rngd,效果非常明显。
先观察rngd启动前的熵池大小: watch cat /proc/sys/kernel/random/entropy_avail ,在100~200之间。
然后启动rngd:sudo rngd -r /dev/urandom -o /dev/random -f -t 1
熵池立刻飙升到3712,接近4096的上限。
先吐槽:没有日志的系统太扯淡了,追查问题只能靠推测或猜测,耽误大量时间。
后总结:如果业务依赖随机数,那么最好使用工具主动补充熵池。
再吐槽:熵池一直够用,今天才出现不够用的情况。究竟是神马原因,还搞不清楚。码农真苦!