mongodump是MongoDB官方提供的指令工具,用來輸出MongoDB內儲存的資料;mongorestore也是MongoDB官方提供的指令工具,用來將mongodump所輸出的資料,輸入回MongoDB內。將mongodumpmongorestore搭配使用就可以備份與還原MongoDB。



安裝mongodumpmongorestore

可以參考官方文件來安裝mongodumpmongorestore,連結如下:

https://www.mongodb.com/docs/database-tools/installation/installation/

備份 MongoDB

以下的mongodump指令,可以將整個MongoDB備份為一個壓縮過後的檔案:

mongodump [--gzip] --archive=$(date +%F_%T).archive[.gz] [MONGODB_URI]

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」)。

mongodump

然後執行以下指令重啟MongoDB:

mongodump

第一次使用replica set,要進入mongo Shell,並執行以下指令來進行初始化:

rs.initiate()

mongodump

之後在使用mongodump指令時,就可以加上--oplog參數,在備份檔裡加入備份時的oplog。

mongodump

使用Zstd來壓縮備份檔

在Bash Shell中可以使用管線將mongodump指令的輸出導給zstd指令的輸入,使得輸出的備份能夠被Zstd壓縮。指令用法如下:

mongodump [--oplog] --archive [MONGODB_URI] | zstd > $(date +%F_%T).archive.zst

基於Debian的Linux發行版可以使用以下指令來安裝Zstd:

sudo apt install zstd
使用RAR來壓縮備份檔

在Bash Shell中可以使用管線將mongodump指令的輸出導給rar指令的輸入,使得輸出的備份能夠被RAR壓縮,也能夠製作修復RAR壓縮檔的還原記錄,降低硬碟出問題而使得備份檔損壞的機率。指令用法如下:

mongodump [--oplog] --archive [MONGODB_URI] | rar a [-m5] -si -rr1 $(date +%F_%T).archive.rar

rar指令的-m5參數可以讓RAR使用最大壓縮,壓縮時長並不會像Zstd這樣顯著增長,推薦使用。-si參數可以讓RAR從標準輸入(stdin)讀取資料,也可以接上一個路徑來設定這份資料在RAR內要存放在哪,但沒有必要。-rr1參數可以設定1%的復原記錄,如果想要安全一點也可以設為更高的數值。

RAR的安裝方法可以參考這篇文章:

https://magiclen.org/rar/
自動化備份腳本

搭配SSH Tunnel,我們可以在任意一台能夠SSH登入進MongoDB所在的主機的電腦上進行自動化備份。如果您不熟悉SSH Tunnel的話可以先參考這篇文章:

https://magiclen.org/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}"

上面的腳本可以透過設定環境變數而適應不同的需求,列表如下:

  1. SSH_SERVER:MongoDB所在的SSH伺服器,或是其它可以連接得到MongoDB的SSH伺服器。您應該要自行改掉腳本內的預設值。
  2. BACKUP_NAME:此備份的識別名稱,用來分類備份檔。預設值是mongodb-backup。每個備份應使用不同的名稱。
  3. DB_NAME:要備份的資料庫名稱。如果不設定的話就是全部的資料庫。
  4. USE_OPLOG:是否備份oplog。不設定或是設定為0表示不使用,其餘的值表示要使用。若要備份oplog就不能夠設定DB_NAME
  5. BASIC_BACKUP_DIRECTORY:備份檔存放的基礎目錄。預設是放在/var/backups/magic,如果執行這個腳本的使用者不是root的話,則可能會沒有權限建立出這個目錄,此時可以先手動建好這個目錄,並將擁有者和群組改為要執行這個腳本的使用者和群組。
  6. BACKUP_DIRECTORY:此備份要存放備份檔的目錄。預設是放在${BASIC_BACKUP_DIRECTORY}/${BACKUP_NAME}。不太需要去設定這個。
  7. SSH_SOCKET_PATH:SSH control master socket檔案的位置。預設的路徑放在/tmp/ssh-tunnel-${BACKUP_NAME}。不太需要去設定這個。
  8. OLD_BACKUP_AGE:設定備份成功後要刪除幾天之前的備份檔。預設是15
  9. CLIENT_SERVICE_IP:設定SSH Tunnel的客戶端IP。預設是127.0.0.1。不太需要去設定這個。
  10. CLIENT_SERVICE_PORT:設定SSH Tunnel的客戶端連接埠。預設是27017。建議替每個備份設定不同的連接埠,避免重疊。
  11. SERVER_SERVICE_IP:設定SSH Tunnel的伺服器端IP。預設是127.0.0.1
  12. SERVER_SERVICE_PORT:設定SSH Tunnel的伺服器端連接埠。預設是27017

由於腳本會使用Zstd做壓縮,所以腳本的執行環境要能夠使用zstd指令才行。如果您想要用其它的壓縮方式,就自行修改腳本吧!

因為筆者並沒有去設定MongoDB的使用者驗證機制的習慣,所以這個腳本就沒有考慮到這部份。如果您有設定的話,要自行去修改腳本中的mongodump指令。看是要加上MongoDB URI還是要加上-u-p參數。如果您是使用MongoDB的SSL驗證機制,應該就沒有必要使用到SSH Tunnel了。

此腳本假定執行這個腳本的使用者的SSH公鑰有被加至SSH伺服器的authorized_keys檔案中。如果您還沒做這樣的設定,請參考這篇文章來進行:

https://magiclen.org/ssh-pub-key/

筆者習慣將這個腳本放在Linux作業系統的/usr/local/bin目錄,並設定crontab讓電腦自動去備份。如下圖:

mongodump

還原 MongoDB

以下的mongorestore指令,可以利用mongodump指令產生出來的備份檔,來還原MongoDB:

mongorestore [--oplogReplay] [--drop] [--gzip] --archive=/path/to/backup-file [MONGODB_URI]

如果是使用含有oplog的備份檔,可以加上--oplogReplay參數來套用oplog記下的資料庫操作,以修復不一致的資料。加上--drop參數,可以在還原集合之前先刪除該集合才還原。--archive參數若沒有設定路徑,則會從標準輸入讀取備份。

mongodump

mongorestore指令還可以加上一個或多個--nsInclude--nsExclude參數來控制要還原的資料庫和集合。例如要還原a資料庫下的所有集合,就使用--nsInclude "a.*"

解壓縮Zstd備份檔

在Bash Shell中可以使用管線將zstd指令的輸出導給mongorestore指令的輸入,使得被Zstd壓縮的備份能夠再被Zstd解壓縮。指令用法如下:

zstd -d < /path/to/zst-backup-file | mongorestore [--oplogReplay] [--drop] --archive
解壓縮RAR備份檔

在Bash Shell中可以使用管線將rar指令的輸出導給mongorestore指令的輸入,使得被RAR壓縮的備份能夠再被RAR解壓縮。指令用法如下:

rar p /path/to/rar-backup-file | mongorestore [--oplogReplay] [--drop] --archive