ASR项目实战-交付过程中遇到的内核崩溃问题

当前参与交付的语音识别产品服务,算法模块基于经典的Kaldi,算法中的一部分运行在GPU之上。

算法团队采用的是声学模型+语言模型的1-pass方案。这个方案的特点在于,语言模型数据文件(HCLG文件)的大小,和训练语料的丰富程度正相关,即语言文本的语料越多,经过训练、转换后得到的语言模型文件越大。

经过数据团队一段时间的努力,当前项目使用的语言模型,已经达到了 XX GB的规模。

这对我负责交付的算法服务组件带来了全方位的挑战:

  • 版本构建,耗时接近30分钟。
  • 安全扫描,耗时接近30分钟,运气不好的话,可能会上升至1小时+。
  • 版本上传软件仓库,耗时至少10分钟。
  • 版本部署,耗时约30分钟。
  • 服务启动,耗时5分钟,其中主要时间都用来解析、加载语言模型文件。

平时在开发的部署环境上远程调试业务时,需要反复给服务打补丁、重启,由于启动时间耗时在5分钟,因此调试效率很低。受限于硬件规格,调试效率低的问题一直没有很好的解决方法,于是开发小伙伴们只好忍着。

但终于有一天的晨会上,开发小伙伴向我抱怨,现在调试算法服务时效率非常低,启动程序时,至少需要两次,等待10分钟,才能将程序运行起来。小伙伴们反复验证,发现打补丁之后,第一次启动算法服务时一定会启动失败。小伙伴反馈的这个信息,一开始我并没有放在心上,但事后证明这是一个大的疏忽。

后来,我交付的一个需求,代码在本地使用UT用例验证完毕,进入在环境上做功能验证的环节。这时由于需要反复的对环境打补丁、重启服务,修复远程调试过程中发现的问题,同样的,我也遇到了小伙伴反馈的现象。不过这个现象被我误判断为网络问题,因为那段时间网络特别差,经常断网,导致控制台程序随机断开,这也会导致服务启动不成功。

网络的问题很好修复,并且持续的时间比较短。而我自己UT做的相对比较充分,因而花在远程调试的时间比较短,因此业务需多次启动的问题,对我的困扰不大。于是,这个多次启动的问题被我轻易放过了。

版本转测试之后,测试同事反馈两个现象:

  • 在配置中心修改配置后,无法自动生效。
  • 测试同事部署新版本之后,需要至少需要启动两次,才能把服务正常运行起来。

这两个现象都很奇怪。

对于第一个现象,我们基于基础平台来交付业务,很多产品的服务都使用这套框架,而我们团队内部其它的服务也使用了这套框架,都没有遇到过在配置中心修改配置后不生效的现象。

对于第二个现象,正常情况下,使用部署工具完成算法服务的部署之后,算法服务本身是应该处于运行状态的。但测试同学发现使用最近的版本安装完成之后,算法服务经常处于启动失败的状态。而手工启动时,至少需要两次启动,才能启动成功。

测试同事是在做性能压力测试的准备工作时观察到的前述现象,虽然暂时不会阻塞性能测试,但本着怀疑一切、不能放过问题的精神,测试同事提交了问题单,要求尽快解决。于是,现在不能当成偶然事件放过了,需要认真对待,否则必然影响版本发布。

对于测试同事反馈的第一个问题,为便于后续的说明,这里先介绍一下配置管理的实现方案。

配置管理服务,包括配置中心和cnfg_agent。

  • 配置中心集中部署,提供Web界面供用户管理、维护应用服务的配置项和值。
  • cnfg_agent和业务服务组件同机部署,使用相同的运行账号,定时从配置中心服务读取配置项和值,并写入本地文件,由业务服务组件读取。

经过观察测试同事的环境,有如下发现:

  • 环境上的cnfg_agent进程没有处于运行状态。手工启动cnfg_agent后,在配置中心修改的配置值,可以被cnfg_agent正常同步至本地。由此可以确认cnfg_agent自身的业务功能是正常的。
  • 两次启动算法服务后,发现cnfg_agent进程消失了,因此需要确认cnfg_agent消失的原因。

联系配置管理服务的技术支持同学,了解到cnfg_agent的工作原理。

  • cnfg_agent安装过程中,安装脚本会自动定义一个cron任务。
  • 在cron任务的脚本代码中,会主动检查cnfg_agent进程是否存在,如不存在,则拉起cnfg_agent进程。

因此,即便是cnfg_agent由于自身的原因退出运行,理论上讲,应该很快被定时运行的cron任务检测到异常,并自动启动,不应该出现cnfg_agent长时间处于离线状态的现象。

进一步检查测试同事提供的环境,发现一个新的现象。如前所述,cnfg_agent和业务组件同机部署,使用了相同的操作系统的账号。检查测试环境的主机,发现业务账号恰好没有创建cron任务的权限,这意味着安装cron定时任务的操作一定失败。因此当cnfg_agent进程退出后,没有定时任务来检测、并重新拉起cnfg_agent进程。这可以解释为什么cnfg_agent消失后不能自动恢复运行。

于是,在测试环境上,手工给业务账号增加创建、执行cron任务的权限,并增加cnfg_agent的cron任务。之后尝试多次重启算法服务,发现cnfg_agent虽然会退出运行,但很快就会被cron任务拉起。

因此cnfg_agent进程退出后无法恢复运行的问题,可以使用本方法临时规避。

继续分析。

检查cnfg_agent的日志文件,在启动算法服务的时间点,找到了cnfg_agent退出运行的日志记录。

对于我们提供的信息,配置管理服务的技术支持同学给出的答案是,cnfg_agent退出运行,通常只在操作系统重启时才有可能会遇到。

按照这个答复,检查/var/log/messages,居然真的找到了操作系统重启的日志记录,同时观察到了内核crash的记录。检查时间点,发现启动算法服务、cnfg_agent退出运行,这三件事的时间点相同。

这时,基本可以把算法服务第一次启动失败、cnfg_agent退出运行、操作系统重启,这三件事情关联在一起分析。

联系操作系统专家,在其协助下,顺利的找到了操作系统重启的原因。原来是出现了内核crash现象,并且在出现问题的一台主机上提取到了内核crash的相关记录。

我们当前部署算法服务的主机,基于CentOS定制,安装了kdump工具。操作系统专家依据kdump生成的crash信息,找到两类原因:

  • 主机的内存不足,触发内核crash。
  • GPU驱动的代码中存在一处bug,申请内存后,没有对指针判空,当申请内存失败时,C库返回的是空指针,这时GPU驱动的代码直接操作这个指针,触发了空指针异常,进而导致内核crash。

这两个原因都和内存相关。

和操作系统专家、基础设施专家交流后,依据他们的建议,选择一台存在问题的主机,提交扩充规格的申请,将内存放大一倍。再复现操作,发现算法服务只需一次即可正常启动。

经反复重试后,确认:

  • 内核不再crash。
  • cnfg_agent不会出现重启现象。
  • 算法服务只需一次即可启动。由于扩充规格后,CPU数量蛮多,现在启动算法服务的时间也缩短了。

因而有理由推断:

  • cnfg_agent的意外重启现象,和cnfg_agent自身的质量无关,和操作系统重启强相关。
  • 操作系统的重启现象,主要由内核crash导致。
  • 而内核crash现象,目前看和算法服务所在主机的硬件规格过低相关。
  • 扩充规格后,一切问题现象全部消失。

将前述定位的信息同步给测试同事,经过讨论之后,测试同事基本认可分析结论。

大家达成一致结论:

  • 对于硬件规格不足的问题,同意调整测试环境中主机的规格,继续执行其余的验证。
  • 对于GPU驱动中存在的问题,需要算法同事介入,验证高版本的GPU驱动是否解决了相关的问题。
  • 假如高版本的GPU驱动解决了问题,涉及到的版本升级、版本部署包、安装指导等的更新,暂时先放在任务清单。

后记:

升级硬件规格之后,操作系统内核crash的现象不再出现。但前述描述的原因和现象之间的关系,个人感觉还是有点牵强。比如在重启应用时,这时系统其实处于空载状态,内存占用率、显卡的内存占用率,实际上比较低,但内核仍然会出现crash事件,比较怪异。由于不具备分析内核、驱动类故障的技能和经验,并且无法获得操作系统专家进一步的协助,因此本事件暂时放一放。

如下是kdump相关的帖子: