img

数据库突然变成空白一片?还好有备份,不然就出大事了…

发现故障

某天下午,服务器突然出现大量错误报警,应用大部分功能无法正常使用,后端服务基本处于不可用状态。得知这个情况后,第一时间查看后端日志,发现大量数据库连接错误,初步分析是 MongoDB 挂了。

初因定位

尝试重启后端,手动连接 MongoDB,都出现了错误。重新启动 MongoDB,依然没有解决,此时报错提示认证失败。

img

于是使用初始化的账号连接,连接成功但是发现此时数据库中所有数据都已消失不见。

img

难道是有人删库跑路了?查看 MongoDB 挂载的/data/db目录,发现一个文件都没有,数据文件似乎真的消失了。

故障止损

在经过一系列找回文件的操作以后,我们最终遗憾的宣布,数据大概率是找不回来了。幸运的是服务器上还保留了数天前的备份文件,虽然有点久远,但也比没有的强。重新导入数据后,服务恢复正常。

根因定位

查看 containerd 日志,发现故障发生的时候 MongoDB 容器出现了 OOM(Out Of Memory)。

img

难道是 k8s 帮我删库了?继续分析,发现 MongoDB 正常运行时,挂载的目录在宿主机上依然是一个数据文件也没有。推测可能是文件挂载出了问题,下面是当时的配置文件(部分):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo
  namespace: db
spec:
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      labels:
        app: mongo
    spec:
      containers:
        - name: mongo
          image: mongo:5.0
          ports:
            - name: db
              containerPort: 27017
              protocol: TCP
          volumeMounts:
            - mountPath: /data
              name: mongodb
          livenessProbe:
            initialDelaySeconds: 10
            tcpSocket:
              port: 27017
      volumes:
        - name: mongodb
          persistentVolumeClaim:
            claimName: mongo

MongoDB 官方文档中挂载的是/data/db,而我们挂载了/data,乍一看似乎也没什么问题,毕竟/data/db也包含在/data里,但就是这一个小小的不同,导致了数据的丢失。

查看 MongoDB Docker 镜像的构建过程,我们会发现有这样一行:VOLUME [/data/db /data/configdb]。也就是说,尽管我们挂载了/data,但对于/data/db/data/configdb,容器运行时会为它们挂载新的 Volume。

img

但是,对于这两个 Volume,他们的生命周期是跟随 Pod 的。因此,当容器出现 OOM 或是其他原因导致 Pod 重建时,这些资源都会被释放掉,也就出现了本文最开始提到的故障。

优化改进

  • 挂载正确的目录,/data/db而不是/data。定期检查目录内容,看看文件是否真的存到了正确的位置
  • 定期进行数据备份,定期进行数据备份,定期进行数据备份!

总结

数据千万条,备份第一条

数据不备份,亲人两行泪

附:使用 mongodump 备份

mongodump -u $MONGO_USERNAME -p $MONGO_PASSWORD --gzip --archive=/data/db/backups/mongo-backup.gz -h $MONGO_HOST