Docker内のmysqlをバックアップしてリストア
はじめに
Dockerでmysqlを動かしていて、本番運用するにあたってバックアップとリストアの一連の手順をいろいろと勉強したので忘れないように残しておきます。
検証環境
ホストサーバーでバックアップを取って、バックアップサーバーからrsyncでとってくる…をやります。
- ホストサーバー 192.168.2.21
- バックアップサーバー 192.168.2.22
バージョンは以下のとおり
- ubuntu 24.04.1
- docker 27.3.1
- mysql 8.4
物理バックアップと論理バックアップ
本題に入る前にちょっとお勉強。
mysql公式が丁寧にまとめておいてくれたので一部抜粋します。
物理バックアップ
- 物理バックアップ方法は、変換なしでファイルのコピーのみを行うため、論理バックアップ方法よりも高速です。
- 出力は論理バックアップの場合よりもコンパクトになります。
- バックアップは、同一または類似のハードウェア特性を持つ他のマシンにのみ移植可能です。
InnoDBで動かしているのであれば.idbファイルのこと。
論理バックアップ
- 論理バックアップは、MySQL サーバーにクエリを実行してデータベースの構造とコンテンツ情報を取得することによって実行されます。
- 論理バックアップは、サーバーがデータベース情報にアクセスしてそれを論理形式に変換する必要があるため、物理バックアップよりも遅くなります。
- 論理形式で保存されたバックアップはマシンに依存せず、移植性も高くなります。
mysqldumpなどで生成されるファイルのこと。
どっちもとっておけば安心でしょう。ということでどっちもとっていきます。
mysqldumpで論理バックアップを取得
mysqlpumpとかmysqlshとかもっと新しくてイケてるコマンドがあるようなんだけど、それらのコマンドを使用した記事は私の理解力を超えており扱い切れないと感じたので、mysqldumpコマンドを使用してバックアップを取っていきます。
my.cnfの編集
mysqldumpの実行に際して設定をいじくっておきます。
[mysqldump]
single-transaction # データの整合性を保つ
events # DBのイベントスケジューライベントを出力に含めます。
routines # DBのストアドルーチン (プロシージャーおよび関数) を出力に含めます。
triggers # DBの各テーブルのトリガーを出力に含めます。
skip-disable-keys # キーを無効にしない
mysqldumpを実行するシェルスクリプトを作成
#!/bin/bash
# MYSQL_USERNAMEなどは別ファイルから取得
source .env
# 年、月、タイムスタンプをそれぞれ取得
YEAR=`date '+%Y'`
MONTH=`date '+%m'`
TIMESTAMP=`date '+%Y%m%d%H%M'`
# バックアップファイルの保存場所
BACKUP_DIR=/opt/backup/mysqldump
# バックアップファイルを保存するためのディレクトリを作成
mkdir -p ${BACKUP_DIR}/${YEAR}/${MONTH}
# コンテナID取得
CONTAINER_ID=$(docker ps -q --filter name=mysql)
# Docker内のmysqlに接続してバックアップをする部分(zstdでファイルの圧縮もしている
docker exec ${CONTAINER_ID} mysqldump -u${MYSQL_USERNAME} -p${MYSQL_PASSWORD} ${MYSQL_DATABASE} --all-databases | zstd > ${BACKUP_DIR}/${YEAR}/${MONTH}/${TIMESTAMP}.sql.zst
BACKUP_DIRなどはお好きな場所に変えてください。
バックアップファイルの保存場所やこのシェルスクリプトに実行ユーザーの権限を与えることも忘れずに。
mysqldumpのシェルスクリプトをcronで実行
0,30 * * * * /bin/bash /opt/scripts/mysqldump.sh
0分と30分ごとに実行する場合は上記のとおり。
うまくいっている場合は以下のようなファイルが生成されています。
$ ls /opt/backup/mysqldump/2024/11
202411291030.sql.zst # 10:30のデータ
202411291100.sql.zst # 11:00のデータ
rsyncを実行するシェルスクリプトを作成
rsyncを使ってバックアップサーバー(192.168.2.22)からホストサーバー(192.168.2.21)に接続⇒コピーします。
rsync実行時にホストサーバーのパスワードを入力しなければならないため、expectをインストールします。
ここからの作業はすべてバックアップサーバー(192.168.2.22)で行います。
sudo apt install expect
インストールが完了したら、シェルスクリプトを作成しましょう。
#!/bin/bash
# PASSWORDは別ファイルから取得
source .env
expect -c "
set timeout -1
spawn rsync -av ubuntu@192.168.2.21:/opt/backup /home/ubuntu/
expect \"password:\"
sleep 1
send \"${PASSWORD}\r\"
expect eof
exit
"
rsync -av ubuntu@192.168.2.21:/opt/backup /home/ubuntu/
をご自身の値に変えてください。
なお、上記コマンドだと/opt/backup以下の作成と更新は反映されますが、削除は反映されません。
また、rsyncは873番ポートを使用するため開けておいてください。
rsyncのシェルスクリプトをcronで実行
バックアップサーバーに以下のcronを登録します。
5,35 * * * * /bin/bash /home/ubuntu/rsync.sh
ホストサーバで0分と30分に実行されるので、バックアップサーバーでは5分遅れで実行するようにします。
正常に作動している場合、バックアップサーバーに以下のファイルがコピーされています。
$ ls backup/mysqldump/2024/11
202411291030.sql.zst # 10:30のデータ
202411291100.sql.zst # 11:00のデータ
mysqldumpからリストア
生成されたmysqldumpからDBリストアするコマンドは以下のとおり。
zstdcat <ファイルパス>/<ファイル名>.sql.zst | docker exec -i コンテナ名 mysql -uDBユーザ名 -pDBパスワード
.idbで物理バックアップを取得
リストアにあたって必要になるのはコンテナ内の以下のファイルです。
- /var/lib/mysql/some_database/ 復元対象のデータベース
- /var/lib/mysql/ibdata1
/var/lib/mysql以下をボリュームとしてホストサーバーに保存します。
services:
...
mysql:
...
volumes:
- mysql_data:/var/lib/mysql
...
volumes:
mysql_data:
mysql_dataがホストサーバーのどこにあるかはdocker volume inspect
コマンドでわかります。
[
{
"CreatedAt": "2024-11-28T10:06:00+09:00",
"Driver": "local",
"Labels": {
"com.docker.stack.namespace": "someapp"
},
"Mountpoint": "/var/lib/docker/volumes/someapp_mysql_data/_data", # ここにある
"Name": "someapp_mysql_data",
"Options": null,
"Scope": "local"
}
]
mysqlデータの圧縮を実行するシェルスクリプトを作成
#!/bin/bash
# ROOT_PASSWORDなどは別ファイルから取得
source .env
# Mountpointを指定
VOLUME_PATH=/var/lib/docker/volumes/someapp_mysql_data/_data
# 年、月、タイムスタンプをそれぞれ取得
YEAR=`date '+%Y'`
MONTH=`date '+%m'`
TIMESTAMP=`date '+%Y%m%d%H%M'`
# バックアップファイルの保存場所
BACKUP_DIR=/opt/backup/mysqldata
# バックアップファイルを保存するためのディレクトリを作成
mkdir -p ${BACKUP_DIR}/${YEAR}/${MONTH}
# 圧縮して保存
echo ${ROOT_PASSWORD} | sudo -S tar -acf ${BACKUP_DIR}/${YEAR}/${MONTH}/${TIMESTAMP}.tar.zst ${VOLUME_PATH}
なお実行時にroot権限が必要であるため標準入力からパスワードを渡しています。
以降は論理バックアップと同様の流れになります。
シェルスクリプトをcronで実行
0,30 * * * * /bin/bash /opt/scripts/mysqldata.sh
0分と30分ごとに実行する場合は上記のとおり。
うまくいっている場合は以下のようなファイルが生成されています。
$ ls /opt/backup/mysqldata/2024/11
202411291030.sql.zst # 10:30のデータ
202411291100.sql.zst # 11:00のデータ
rsyncを実行するシェルスクリプトを作成
/opt/backup配下に置いたため、自動でバックアップが取得されます。
正常に作動している場合、バックアップサーバーに以下のファイルがコピーされています。
$ ls backup/mysqldata/2024/11
202411291030.sql.zst # 10:30のデータ
202411291100.sql.zst # 11:00のデータ
.idbからリストア
前述の/var/lib/docker/volumes/someapp_mysql_data/_data
にバックアップで保存したデータを展開する
参考
参考にさせていただきました。ありがとうございました。
Discussion