使用ansible在大规模(几百台以上规模比较容易遭遇)部署机器的时候,有时候会碰到ansible进程hang在某个点上,这个点不一定每次都一样。
研究了一个下午,发现hang住的时候,CI/CD的agent上并没有报错日志,被执行host上也没有ansible的日志输出,并且无论等待多长时间也不会退出。

期初怀疑是版本问题,因为之前线上一直用的2.7版本,最近刚切换到2.9,但是实际上这个部署的job在使用2.9版本的时候已经成功过不少次。然后检查CI/CD上部署job的VCS配置,发现虽然有更新,但是并没有什么很大的可能明显导致这种情况发生的changes。

启动google大法,发现ansible的github上有人提过一个issue,一个很老的2017年9月的issue,https://github.com/ansible/ansible/issues/30411
这个issue最后一个comment是提交了一个PR(竟然是20天前刚提交的),这个PR给task执行增加了一个硬超时,已经merge,虽然没有解释为什么会hang住,但是起码提供了一个解决方案。而且里面的comments提到了一个关键的复现方法,一个简单的"df"命令就可以让ansible进程hang住。
于是我找了两台机器,装了nfs,client端挂载了server端的一个目录,然后把server端的nfs进程stop掉,这时候在client中执行"df"命令,效果等同于进程hang住,原理也很简单,因为nfs链接断掉了,"df"在等待返回,等不到就“假死”在那里。
再然后,我配置了一个只有一条shell命令的ansible playbook,让多台机器(其中包括了这个nfs client节点)同时运行,结果果然是ansible在执行到这个nfs client节点的时候hang住再也不动了,并且如果我重新启动nfs server端,ansible立即就回正常执行完,说明ansible进程没有“死”,只是一直在“等待”。

现在在回过头来思考,结论应该是批量开机器的时候,因为我们用的是AWS,在规模很大的情况下,经常有实例会因为各种特例问题导致某些task挂起在机器上,而这个时候由于ansible没有超时机制,并且!最关键的来了,ansible没有收到任何错误值和返回值,于是就一直在“等待”,等到海枯石烂。
短期的解决方法,如果是云平台,干掉那些“不正常”的机器,重新run一次job,一般都会解决。
长期的话,超时是一个可行的方案,而且ansible还需要真正的解决是并行问题,不要让一个机器的问题导致整个job的失败(某一个实例有问题,那就提示出来,其他的继续跑)。