您好,登錄后才能下訂單哦!
摘要: 數據還是要備份的,萬一刪庫了呢?
今年8月,騰訊云竟然把客戶前沿數據的數據弄沒了,Fundebug在第一時間進行了一些簡單的技術分析:
一方面,騰訊云對這件事負有不可推卸的責任,他們剛開始說是什么硬盤固件版本bug(該聲明已刪),后來承認是人為操作失誤導致的。
另一方面,前沿數據沒有備份業務數據,也是一種非常不專業的行為,導致業務無法恢復,必須完全重新開始。
因此,所有的開發者都應該從這個事件吸取教訓,不要偷懶,嚴格備份業務數據,否則數據一旦出問題,損失將無法挽回。
我們還分享了Fundebug的數據備份方案,供大家參考:
備份方案 | 時間粒度 | 細節 |
---|---|---|
MongoDB復制集 | 實時 | 搭建3個節點(1個Primary和2個Secondary)的MongoDB復制集,實時同步數據。 |
阿里云磁盤快照 | 每天 | 每天凌晨自動快照所有磁盤,包括系統盤和備份數據盤。 |
mongodump導出核心數據 | 每天 | 每天凌晨將MongoDB核心數據導出到復制集之外的服務器磁盤(該磁盤會每天進行快照)。 |
阿里云對象存儲 | 每天 | 每天凌晨將mongodump導出的數據使用gpg非對稱加密之后,上傳到阿里云深圳數據中心的對象存儲,設置跨區域復制,自動同步到杭州數據中心,每份數據保留1個月。 |
本地硬盤備份 | 每周 | 每周六中午從阿里云對象存儲下載加密的備份數據,存儲到本地磁盤。 |
大概是因為我們沒有公布備份方案的技術細節,我們受到了質疑:
要么多重備份是假的
對于這種指責,我的原則是必須懟回去。那么,這篇博客我來詳細介紹一下我們數據備份方案吧~所有源代碼都在GitHub倉庫Fundebug/fundebug-mongodb-backup,歡迎star。
生產環境使用單節點的MongoDB數據庫,除非訪問量非常低或者不在乎服務可用性,否則基本上是不可能的,這輩子都不可能。單節點MongoDB存在單點故障(single point of failure),一旦掛了,整個應用就掛了。更糟糕的是,如果數據損壞,恢復將非常麻煩。
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累計處理了8億+錯誤事件,得到了Google、360、金山軟件等眾多知名用戶的認可。歡迎免費試用!
轉載時請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2018/09/27/how-does-fundebug-backup-data/
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。