摘要: 数据仍是要备份的,万一删库了呢?javascript
今年8月,腾讯云居然把客户前沿数据的数据弄没了,Fundebug在第一时间进行了一些简单的技术分析:html
一方面,腾讯云对这件事负有不可推卸的责任,他们刚开始说是什么硬盘固件版本bug(该声明已删),后来认可是人为操做失误致使的。 另外一方面,前沿数据没有备份业务数据,也是一种很是不专业的行为,致使业务没法恢复,必须彻底从新开始。java
所以,全部的开发者都应该从这个事件吸收教训,不要偷懒,严格备份业务数据,不然数据一旦出问题,损失将没法挽回。git
咱们还分享了Fundebug的数据备份方案,供你们参考:github
备份方案 | 时间粒度 | 细节 |
---|---|---|
MongoDB复制集 | 实时 | 搭建3个节点(1个Primary和2个Secondary)的MongoDB复制集,实时同步数据。 |
阿里云磁盘快照 | 天天 | 天天凌晨自动快照全部磁盘,包括系统盘和备份数据盘。 |
mongodump导出核心数据 | 天天 | 天天凌晨将MongoDB核心数据导出到复制集以外的服务器磁盘(该磁盘会天天进行快照)。 |
阿里云对象存储 | 天天 | 天天凌晨将mongodump导出的数据使用gpg非对称加密以后,上传到阿里云深圳数据中心的对象存储,设置跨区域复制,自动同步到杭州数据中心,每份数据保留1个月。 |
本地硬盘备份 | 每周 | 每周六中午从阿里云对象存储下载加密的备份数据,存储到本地磁盘。 |
大概是由于咱们没有公布备份方案的技术细节,咱们受到了质疑:mongodb
要么多重备份是假的docker
对于这种指责,个人原则是必须怼回去。那么,这篇博客我来详细介绍一下咱们数据备份方案吧~全部源代码都在GitHub仓库Fundebug/fundebug-mongodb-backup,欢迎star。数据库
生产环境使用单节点的MongoDB数据库,除非访问量很是低或者不在意服务可用性,不然基本上是不可能的,这辈子都不可能。单节点MongoDB存在单点故障(single point of failure),一旦挂了,整个应用就挂了。更糟糕的是,若是数据损坏,恢复将很是麻烦。json
MongoDB有多种可能性会挂掉,最多见的就是高峰期内存使用量飙升,致使Linux的Out of Memory (OOM) killer将mongod进程杀死,这种状况Fundebug碰见过很多次,那咱们是如何安全渡过的呢?答案是**复制集(replica set)**。小程序
复制集由多个MongoDB节点构成,它们的数据是实时同步的,所以数据几乎彻底相同。当某个节点挂掉时,应用能够自动切换到其余节点,这样保证了服务的可用性。
Fundebug的MongoDB都运行在Docker容器中,其Docker Compose配置文件以下:
version: '2.2' services: mongo: image: mongo:3.2 network_mode: "host" restart: always cpus: 7 mem_limit: 30g command: --replSet rs0 --oplogSize 25600 volumes: - /mongodb/data:/data/db logging: driver: "json-file" options: max-size: "5g"
复制集一个很是重要的参数是oplog的大小,使用**--oplogSize**选项能够指定。咱们设定的值是25600MB,即25GB。oplog(operation log)是复制集节点同步数据的关键,Primary节点将数据库写操做记录到oplog中,Secondary节点从Primary节点处复制oplog并应用到本地数据库中。所以,oplog大小决定了Primary和Secondary节点能够接受的数据最大"时间差"。使用rs.printReplicationInfo()能够查看oplog信息:
rs.printReplicationInfo() configured oplog size: 25600MB log length start to end: 11409secs (3.17hrs) oplog first event time: Sat Sep 22 2018 12:02:04 GMT+0800 (CST) oplog last event time: Sat Sep 22 2018 15:12:13 GMT+0800 (CST) now: Sat Sep 22 2018 15:12:13 GMT+0800 (CST)
可知oplog中记录了最近3.17小时的数据库写操做,假设复制集中某个节点因为宕机有4个小时没有同步数据,则重启该节点也没法与其余节点同步了!这时会出现"too stale to catch up -- entering maintenance mode"的错误,只能手动同步数据。
所以,咱们建议oplog的值应该尽可能设大一些,不然之后修改oplog的步骤挺麻烦的。事实上,25GB的oplog大小对于Fundebug的MongoDB复制集来讲已经不够了,咱们须要修改。
Fundebug的MongoDB复制集由1个Primary节点和2个Secondary节点构成,为保证咱们服务可用性发挥了很是关键的做用!我以后所介绍的备份方案都是冗余措施,咱们历来没有真正使用过那些备份数据,而复制集**"拯救"**了咱们很多次,强烈建议你们都配置一下。
关于MongoDB复制集的更多技术细节,之后我再单独详述,欢迎关注Fundebug公众号。
快照可以保留某一时间点的磁盘数据状态,所以能够做为一种数据备份方式。很简单,配置一下自动快照策略就行了:
我备份了系统盘,万一数据丢失好比被删库,至少还能回滚磁盘。每周快照1次,保存7天。由于服务所有运行在Docker里面,服务器自己基本上没有什么配置,备份的需求不大,实际上咱们也历来没有回滚过磁盘。
另外,我没有对MongoDB数据盘直接进行快照,由于发现快照后的数据没法恢复(这一点有待进一步确认)。
我只是将mongodump导出的核心数据所在磁盘进行了快照。天天快照1次,保存两天。这样作能够确保核心数据的安全性。
使用mongodump命令,能够全量导出MongoDB数据。对应的,以后可使用mongorestore命令将备份数据导入MongoDB。
导出数据的脚本dump-data.sh以下:
#!/bin/sh # 删除前一天导出的数据 rm -rf /data/mongodb_backup DIR=`date +%Y%m%d%H%M` OUT=/data/mongodb_backup/$DIR mkdir -p $DEST # 全量导出MongoDB数据(排除部分集合) mongodump --host "rs0/192.168.59.11:27017,192.168.59.12:27017,192.168.59.13:27017" \ --db fundebug-production \ --excludeCollection events \ --out $OUT
使用**--excludeCollection选项,能够排除部分不须要备份的集合。例如,Fundebug累计处理了6亿+**的错误事件,存在event集合中,由于咱们已经聚合过了,因此没有必要备份,并且数据量太大,备份也不现实。
使用crontab脚本按期执行dump-data.sh脚本:
# 天天凌晨4点导出数据 0 4 * * * /root/fundebug-mongodb-backup/dump-data.sh
使用mongodump导出的数据保存在测试服务器的数据磁盘上,从地域层面上来讲都在同一个地方,即阿里云深圳数据中心。若是要作到异地备份,能够借助阿里云的对象存储服务的跨区域复制功能,将备份数据自动同步到阿里云杭州数据中心。
在上传备份数据以前,使用gpg命令进行非对称加密,能够保证数据安全性。加密导出数据的脚本encrypt-data.sh脚本以下:
#!/bin/bash DIR=`find /data/mongodb_backup/ -maxdepth 1 -type d ! -path /data/mongodb_backup/` source=$DIR/fundebug-production cd $source # 将导出数据加密 for file in * ; do gpg --batch --yes -v -e -r fundebug --output $source/$file.gpg --always-trust $file done ;
除了加密,gpg还有必定的压缩效果,这样能够减小备份数据量,一箭双雕。关于gpg命令的细节,能够查看参考博客。
使用阿里云提供的Node.js客户端ali-oss,能够将加密以后的.gpg文件上传到阿里云的对象存储服务中。使用multipartUpload方法便可,upload.js部分代码以下:
// 上传单个文件 async function uploadFile(fileName, filePath) { try { const result = await store.multipartUpload(fileName, filePath, { parallel: 4, partSize: 1024 * 1024, progress: function(p) { logger.info("Progress: " + p); } }); if (result.res.statusCode === 200) { logger.info(`upload file success! ${fileName}`); } else { const message = `upload file fail! ${fileName}`; logger.error(message); logger.error(result); fundebug.notifyError(new Error(message), { metaData: { message: message, result: result } }); } } catch (error) { const message = `upload file fail! ${fileName}`; logger.error(message); logger.error(error); fundebug.notifyError(error, { metaData: { message: message, error: error } }); } }
代码运行在Docker容器中,使用curl命令访问HTTP接口/upload便可触发执行上传操做,使用crontab按期执行:
# 天天凌晨4点备份数据 0 4 * * * /root/mongodb-backup/dump-data.sh && /root/mongodb-backup/encrypt-data.sh && docker restart mongodb-backup && sleep 1m && curl http://127.0.0.1:9160/upload
备份数据经过数据卷(volume)映射到容器中,天天须要重启容器,才能访问天天导出的新数据。
在阿里云上为备份数据的存储空间配置跨区域复制,便可实现自动异地备份,很是方便。其余对象存储云服务应该也支持这种功能吧。
前文提到的备份方式,其实都是在阿里云内部COPY数据。那么问题来了,阿里云挂了怎么办?这种事情固然基本上不可能发生,毕竟咱们有多处备份,甚至实现了异地备份。
既然备份数据都上传到阿里云对象存储了,下载到本地也不是什么难事。使用ali-oss的list和get方法便可实现,download.js部分代码以下:
// 获取当天上传到阿里OSS的文件列表 async function listFilesToDownload(day) { const result = await store.list({ prefix: day }); return result.objects; } // 将阿里云OSS中的文件下载到本地 async function downloadFile(fileName, path) { try { const file = fileName.split("/")[1]; const filepath = `${path}/${file}`; await store.get(fileName, filepath); } catch (error) { const message = `download file fail! ${fileName}`; logger.error(message); logger.error(error); fundebug.notifyError(error, { metaData: { error: error, message: message } }); } }
代码运行在Docker容器中,部署在本地机器,使用curl命令访问HTTP接口/download便可触发执行下载操做,使用crontab按期执行:
# 每周六中午从阿里云下载备份数据 0 12 * * 6 curl http://127.0.0.1:9160/download
本文提到的全部的数据备份方式彻底自动化执行,没有什么技术难度,成本也不高,能够极大提升数据安全性。
Fundebug专一于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了6亿+错误事件,获得了Google、360、金山软件等众多知名用户的承认。欢迎免费试用!
转载时请注明做者Fundebug以及本文地址: https://blog.fundebug.com/2018/09/27/how-does-fundebug-backup-data/