摘要: 數據仍是要備份的,萬一刪庫了呢?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/