mongodump
是MongoDB官方提供的指令工具,用來輸出MongoDB內儲存的資料;mongorestore
也是MongoDB官方提供的指令工具,用來將mongodump
所輸出的資料,輸入回MongoDB內。將mongodump
和mongorestore
搭配使用就可以備份與還原MongoDB。
安裝mongodump
和mongorestore
可以參考官方文件來安裝mongodump
和mongorestore
,連結如下:
備份 MongoDB
以下的mongodump
指令,可以將整個MongoDB備份為一個壓縮過後的檔案:
mongodump
指令的--archive
參數可以使備份出來的檔案變成單一檔案,會比較方便儲存,如果不設定檔案路徑的話,就會從標準輸出(stdout)來輸出備份;--gzip
參數可以用Gzip來壓縮資料庫輸出後的資料,節省硬碟空間,不過筆者會比較建議使用--archive
參數搭配管線(pipe)的方式將輸出導給zstd
指令或是rar
指令來做壓縮,稍候會說明要怎麼跟它們一起用。mongodump
指令使用--archive
所產生出來的檔案經常會以.archive
作為其副檔名,如果又用了--gzip
參數,就將它的副檔名設定為.archive.gz
。至於$(date +%F_%T)
Bash Shell的語法,可以取得當下的本地時間,例如:2022-07-05_18:00:00
。
MONGODB_URI
是可選的,預設值等同於mongodb://127.0.0.1:27017
。如果您的MongoDB有設定使用者驗證機制,或者不是在127.0.0.1:27017
的話,就要自行設定MONGODB_URI
。
如果只要備份單一個資料庫,可以替mongodump
指令加上-d
參數,來指定資料庫的名稱。如果只要備份單一個資料庫,可以替mongodump
指令加上-d
參數,來指定資料庫的名稱。
啟用Oplog
使用mongodump
指令跑備份程序的時候,如果此時MongoDB的內容也正在被改變,那備份檔可能會有資料不一致的問題。如果您的MongoDB是運行在「standalone」模式下,強烈建議將其改為「replica set」以啟用oplog(operations log)功能,來讓mongorestore
在進行還原的時候能夠去參考oplog,解決資料不一致的問題。
開啟MongoDB的設定檔/etc/mongod.conf
,將replication
欄位的內容進行如下的修改:
replication:
replSetName: rs0
藉由隨便指定一個replica set的名稱,可以讓MongoDB運行在replica set模式,即使只有一個節點也可以(即所謂的「single-node replica set」)。
然後執行以下指令重啟MongoDB:
第一次使用replica set,要進入mongo Shell,並執行以下指令來進行初始化:
之後在使用mongodump
指令時,就可以加上--oplog
參數,在備份檔裡加入備份時的oplog。
使用Zstd來壓縮備份檔
在Bash Shell中可以使用管線將mongodump
指令的輸出導給zstd
指令的輸入,使得輸出的備份能夠被Zstd壓縮。指令用法如下:
基於Debian的Linux發行版可以使用以下指令來安裝Zstd:
使用RAR來壓縮備份檔
在Bash Shell中可以使用管線將mongodump
指令的輸出導給rar
指令的輸入,使得輸出的備份能夠被RAR壓縮,也能夠製作修復RAR壓縮檔的還原記錄,降低硬碟出問題而使得備份檔損壞的機率。指令用法如下:
rar
指令的-m5
參數可以讓RAR使用最大壓縮,壓縮時長並不會像Zstd這樣顯著增長,推薦使用。-si
參數可以讓RAR從標準輸入(stdin)讀取資料,也可以接上一個路徑來設定這份資料在RAR內要存放在哪,但沒有必要。-rr1
參數可以設定1%的復原記錄,如果想要安全一點也可以設為更高的數值。
RAR的安裝方法可以參考這篇文章:
自動化備份腳本
搭配SSH Tunnel,我們可以在任意一台能夠SSH登入進MongoDB所在的主機的電腦上進行自動化備份。如果您不熟悉SSH Tunnel的話可以先參考這篇文章:
以下筆者撰寫的Bash腳本:
#!/bin/bash
set -o pipefail
SSH_SERVER=${SSH_SERVER:-"magiclen@192.168.56.101"}
BACKUP_NAME=${BACKUP_NAME:-"mongodb-backup"}
BACKUP_NAME="$(basename "${BACKUP_NAME}")"
DB_NAME=${DB_NAME:-""}
USE_OPLOG=${USE_OPLOG:-"0"}
BASIC_BACKUP_DIRECTORY=${BASIC_BACKUP_DIRECTORY:-"/var/backups/magic"}
BASIC_BACKUP_DIRECTORY="$(realpath -m "${BASIC_BACKUP_DIRECTORY}")"
BACKUP_DIRECTORY=${BACKUP_DIRECTORY:-"${BASIC_BACKUP_DIRECTORY}/${BACKUP_NAME}"}
BACKUP_DIRECTORY="$(realpath -m "${BACKUP_DIRECTORY}")"
SSH_SOCKET_PATH=${SSH_SOCKET_PATH:-"/tmp/ssh-tunnel-${BACKUP_NAME}"}
SSH_SOCKET_PATH="$(realpath -m "${SSH_SOCKET_PATH}")"
OLD_BACKUP_AGE=${OLD_BACKUP_AGE:-"15"} # days
if [[ ! "${OLD_BACKUP_AGE}" =~ ^[0-9]+$ ]]; then
echo "OLD_BACKUP_AGE should be a positive integer." >&2
exit 1
fi
CLIENT_SERVICE_IP=${CLIENT_SERVICE_IP:-"127.0.0.1"}
CLIENT_SERVICE_PORT=${CLIENT_SERVICE_PORT:-"27017"}
SERVER_SERVICE_IP=${SERVER_SERVICE_IP:-"127.0.0.1"}
SERVER_SERVICE_PORT=${SERVER_SERVICE_PORT:-"27017"}
if ss -tan | tr -s ' ' | cut -d " " -f 4 | grep -m1 "${CLIENT_SERVICE_IP}":"${CLIENT_SERVICE_PORT}" > /dev/null ; then
echo "${CLIENT_SERVICE_IP}:${CLIENT_SERVICE_PORT} is used." >&2
exit 2
fi
(rm -f "${SSH_SOCKET_PATH}" && ssh -fNM -S "${SSH_SOCKET_PATH}" -L "${CLIENT_SERVICE_IP}:${CLIENT_SERVICE_PORT}:${SERVER_SERVICE_IP}:${SERVER_SERVICE_PORT}" "${SSH_SERVER}") || (echo "Unable to create the SSH tunnel!" >&2 && exit 3)
if [ ! -d "${BACKUP_DIRECTORY}" ]; then
if [ -e "${BACKUP_DIRECTORY}" ]; then
echo "${BACKUP_DIRECTORY} exists and it is not a directory!" >&2
exit 4
fi
mkdir -p "${BACKUP_DIRECTORY}" || exit 5
echo "The directory, ${BACKUP_DIRECTORY} has been created."
fi
DB_PARAM=
if [ -n "${DB_NAME}" ]; then
if [[ "${USE_OPLOG}" != "0" ]]; then
echo "Oplog can not be used with a specific database name."
exit 6
fi
DB_PARAM="-d \"${DB_NAME}\""
fi
OPLOG_PARAM=
OPLOG_FILENAME=
if [[ "${USE_OPLOG}" != "0" ]]; then
OPLOG_PARAM="--oplog"
OPLOG_FILENAME="-oplog"
fi
FILE_NAME="$(date +%F_%T)${OPLOG_FILENAME}.archive.zst"
if mongodump ${OPLOG_PARAM} ${DB_PARAM} --archive --host="${CLIENT_SERVICE_IP}" --port="${CLIENT_SERVICE_PORT}" | zstd > "${BACKUP_DIRECTORY}/${FILE_NAME}"; then
echo "Backup successfully!"
echo "-----Delete the following old backup files-----"
find "${BACKUP_DIRECTORY}" -name "*.archive.*" -mtime "+${OLD_BACKUP_AGE}" -type f -delete -print
echo "----------"
fi
ssh -S "${SSH_SOCKET_PATH}" -O exit "${SSH_SERVER}"
上面的腳本可以透過設定環境變數而適應不同的需求,列表如下:
SSH_SERVER
:MongoDB所在的SSH伺服器,或是其它可以連接得到MongoDB的SSH伺服器。您應該要自行改掉腳本內的預設值。BACKUP_NAME
:此備份的識別名稱,用來分類備份檔。預設值是mongodb-backup
。每個備份應使用不同的名稱。DB_NAME
:要備份的資料庫名稱。如果不設定的話就是全部的資料庫。USE_OPLOG
:是否備份oplog。不設定或是設定為0
表示不使用,其餘的值表示要使用。若要備份oplog就不能夠設定DB_NAME
。BASIC_BACKUP_DIRECTORY
:備份檔存放的基礎目錄。預設是放在/var/backups/magic
,如果執行這個腳本的使用者不是root
的話,則可能會沒有權限建立出這個目錄,此時可以先手動建好這個目錄,並將擁有者和群組改為要執行這個腳本的使用者和群組。BACKUP_DIRECTORY
:此備份要存放備份檔的目錄。預設是放在${BASIC_BACKUP_DIRECTORY}/${BACKUP_NAME}
。不太需要去設定這個。SSH_SOCKET_PATH
:SSH control master socket檔案的位置。預設的路徑放在/tmp/ssh-tunnel-${BACKUP_NAME}
。不太需要去設定這個。OLD_BACKUP_AGE
:設定備份成功後要刪除幾天之前的備份檔。預設是15
。CLIENT_SERVICE_IP
:設定SSH Tunnel的客戶端IP。預設是127.0.0.1
。不太需要去設定這個。CLIENT_SERVICE_PORT
:設定SSH Tunnel的客戶端連接埠。預設是27017
。建議替每個備份設定不同的連接埠,避免重疊。SERVER_SERVICE_IP
:設定SSH Tunnel的伺服器端IP。預設是127.0.0.1
。SERVER_SERVICE_PORT
:設定SSH Tunnel的伺服器端連接埠。預設是27017
。
由於腳本會使用Zstd做壓縮,所以腳本的執行環境要能夠使用zstd
指令才行。如果您想要用其它的壓縮方式,就自行修改腳本吧!
因為筆者並沒有去設定MongoDB的使用者驗證機制的習慣,所以這個腳本就沒有考慮到這部份。如果您有設定的話,要自行去修改腳本中的mongodump
指令。看是要加上MongoDB URI還是要加上-u
和-p
參數。如果您是使用MongoDB的SSL驗證機制,應該就沒有必要使用到SSH Tunnel了。
此腳本假定執行這個腳本的使用者的SSH公鑰有被加至SSH伺服器的authorized_keys
檔案中。如果您還沒做這樣的設定,請參考這篇文章來進行:
筆者習慣將這個腳本放在Linux作業系統的/usr/local/bin
目錄,並設定crontab讓電腦自動去備份。如下圖:
還原 MongoDB
以下的mongorestore
指令,可以利用mongodump
指令產生出來的備份檔,來還原MongoDB:
如果是使用含有oplog的備份檔,可以加上--oplogReplay
參數來套用oplog記下的資料庫操作,以修復不一致的資料。加上--drop
參數,可以在還原集合之前先刪除該集合才還原。--archive
參數若沒有設定路徑,則會從標準輸入讀取備份。
mongorestore
指令還可以加上一個或多個--nsInclude
和--nsExclude
參數來控制要還原的資料庫和集合。例如要還原a
資料庫下的所有集合,就使用--nsInclude "a.*"
。
解壓縮Zstd備份檔
在Bash Shell中可以使用管線將zstd
指令的輸出導給mongorestore
指令的輸入,使得被Zstd壓縮的備份能夠再被Zstd解壓縮。指令用法如下:
解壓縮RAR備份檔
在Bash Shell中可以使用管線將rar
指令的輸出導給mongorestore
指令的輸入,使得被RAR壓縮的備份能夠再被RAR解壓縮。指令用法如下: