复盘 - 手机app上一行代码导致的生产事故

复盘 - 手机app上一行代码导致的生产事故

December 10, 2019

过程

由于一些历史原因,以及与原先代码的兼容性,临时文件存储在minio中。

某天,生产环境突发事故,minio集群的CPU与内存爆了,只能重启集群。过了十来分钟,很多服务都不可用,健康检查都不通过。日志中的错误信息显示,这些服务都是数据库拿不到连接。需要事先处理数据库连接的问题,于是连接到数据库查死锁,终止导致死锁的sql进程,再重启来恢复这些服务。

由于前不久minio集群挂掉,很容易就想到是由于minio的原因。其他服务通过RPC调用一个文件存储服务来操作minio中的数据,当minio集群挂掉的时候,文件存储服务访问minio一直想访问minio直到超时。在这个期间,其他服务如果有事务调用文件存储服务来操作minio中文件,那么这个事务就不会被提交,一会占着数据库连接。

minio集群的硬件资源如果不升级,迟早CPU与内存还会再次爆掉。幸运的是,文件存储服务,当初做了兼容,支持两种临时文件存储方式,一个是本地磁盘,另外一个是minio,修改配置文件可以方便的切换。于是先切换成本地磁盘方式,然后升级minio集群的CPU与内存配置,重启minio集群。接着,修改文件存储服务的配置文件,切换回minio的方式。至此,所有的服务都慢慢恢复正常了。

接着,细究这个事故的根源,联想到几天前发现app上面的bug,会一直在后台不断地上传图片。当时由于app出了紧急修复的版本,就没有当回事了。没想到过了几天,问题彻底暴露出来了。未升级app到最新版本的用户,在这几天内依然不停地上传文件。访问量的增大,minio集群原先的配置已经扛不住了。幸运的是,之前app实现了一个版本强制升级更新的机制,可以直接在后台修改当前所支持的最小版本,这样就让用户强制更新到最新的版本,避免了继续不停地传文件。

这段时间大概几十万个文件,约200多G的大小。这些文件都上传到同一个bucket中,由于minio的内部实现是一个bucket采用文件夹形式保存,当一个bucket文件夹中的文件数量过多,已经无法列出某个文件夹下面的文件,总是超时。文件存储服务中的归档删除机制失效,导致空间无法释放,逐渐变小。如果没空间,又会导致服务不可用。幸运的是,有办法获取到具体的文件名,只能手动写脚本直接处理文件,释放空间。

时间线

  1. 网页上面发现关联了几千个图片,后来发现是app的bug,会不断地上传图片,修复app
  2. 过了几天,先是minio集群爆了,十几分钟后其他服务挂了
  3. 释放数据库死锁,其他服务恢复
  4. 升级minio集群配置,文件存储服务恢复
  5. 强制让app客户端升级到最新版本,文件存储服务压力减小
  6. 手动写脚本直接处理minio中的文件,释放minio空间。

总结

做的对的地方:

  1. 文件存储服务做了兼容,支持多种方式,出了问题,有个备选方案
  2. app版本可以控制最低支持的版本,有强制升级机制

需要改进的地方:

  1. 发现app上面bug的时候,没有考虑周全,以为只是小问题,太疏忽
  2. 服务之间耦合性有点高,容错性不够。一个服务挂了,可能导致雪崩
  3. minio存储的时候,需要指定子路径。不能放在同一个文件夹下面,要分散到多个子文件夹中。
  4. 没有服务降级机制,如果minio挂了,自动启用本地磁盘,不需要重启服务器
  5. minio 当初建机器的时候,没有使用lv等方式,不方便扩展磁盘,如果存在磁盘不足的情况,那么扩容就不方便
  6. 配置文件的修改,需要重启服务,造成短时间内不可用
最后更新于