もしもの時のためにファイルのバックアップをこまめに取るのは大切ですが、Webサービスの場合それに劣らず大切なのがデータベースのバックアップとなります。
例えば、WordPressでは記事本文はデータベースに格納されていますし、Nextcloudではアドレス帳などのデータがデータベースに格納されています。
このため、ファイルのバックアップを取っていても、データベースのバックアップが無ければこれらの情報が失われる恐れがあります。
今回は、MariaDBの論理バックアップの方法を説明したいと思います。
論理バックアップはデーターベースを元通りにするためのコマンドを保存するバックアップ方法で、容量を圧縮できずリストア(復旧)に時間がかかりやすい代わりに環境が違っていても復旧が出来るという長所があります。
対して物理バックアップという方法もあり、こちらはデータベースのファイルをそのまま圧縮して保管する形です。圧縮しやすく、リストアも早いですがファイル構造ごと保管するため、ファイル構造が違う環境ではリストア出来ないという難点があります。
この記事で行うこと
1. バックアップのコマンド
バックアップを取るためのコマンドは以下となります。
[root@localhost ~]# mysqldump --single-transaction [データベース名] > [保存ファイル名]
mysqldump
: SQLの論理バックアップを出力するためのコマンドです。--single-transaction
: データベースの一貫性を担保するためのオプションです。バックアップ実行中にデータベースに変更が加わったとしても、バックアップ開始当初のデータを取得します。(このオプションはInnoDBというDBエンジンを使っている必要がありますが、バージョン10.2以降のMariaDBはデフォルトでInnoDBとなります)[データベース名]
: 取得するデータベースの名前を取得します。もしデータベースの名称が分からない場合はmariadb
コマンドを実行してMariaDBのプロンプトを起動した後でshow databases;
を実行することにより存在するデータベースの一覧を見る事が出来ます。> [保存ファイル名]
:>
は左側のコマンドの実行結果を右側に書かれているファイルに保存するというコマンドです。今回はmysqldump
で出力されたバックアップを右に書いたファイルに保存することになります。
※上記のコマンドはUNIXソケット認証を使っている場合のものです。UNIXソケット認証を使わない場合は --single-transaction
の後に -u [MariaDBのユーザ名] -p [パスワード]
を追加で記載する必要があります。
なお、データベース名の代わりに --all-databases
と記載することで全てのデータベースのバックアップを取ることが出来ます。
また、複数のデータベースのバックアップを一度に取る場合は --databases
に続けてバックアップを取りたいデータベース名を半角スペースで区切って書きます。
ただ、個人的にはリストア時の小回りを考えると1バックアップ1データベースの方が扱いやすいとは思います。(個人サーバーで数十数百もデータベースを使うとは思えないですし)
2. リストアのコマンド
リストアは以下のコマンドとなります。
[root@localhost ~]# mariadb [データベース名] < [バックアップファイル名]
mariadb
: MariaDB起動時に使うコマンドですが、直接SQL文でデータベースを操作することも出来ます。[データベース名]
: 使用するデータベースを指定します。< [バックアップファイル名]
:<
は右側に書いてあるファイルを左側のコマンドの入力として使用するコマンドです。バックアップファイルにはSQL文が書かれているので、このSQL文を実行させて元のデータベースを復元するものとなります。
なお、複数のデータベースを一気にバックアップした場合は、以下のコマンドになります。
[root@localhost ~]# mariadb < [バックアップファイル名]
データベース名はバックアップしたデータベース名と同じ物が使用されます。この時、既に同じデータベース名が存在する場合は上書きされてしまいます。
このため、一部のデータベースだけが壊れたといった場合の復元が大変面倒なので、データベースは個別でバックアップする方が良いのではないかと考えます。
※これらのコマンドもUNIXソケット認証を使っている場合のものです。UNIXソケット認証を使わない場合は mariadb
の後に -u [MariaDBのユーザ名] -p [パスワード]
を追加で記載する必要があります。
3. バックアップのシェルスクリプト作成
上述の通りに1バックアップ1データベースと考えるとその分だけ実行するコマンドが増えてしまうので1回のコマンドでバックアップが出来るようにシェルスクリプトを作成します。
今回するデータベース名は testdb
としバックアップの保存先を /var/sqlbackup/
ディレクトリ内に保存し、ファイル名は TestDB-YYYYMMDD
となるようにします。(YYYYMMDDはバックアップ日の日付です)
また、1週間前のバックアップは削除するようにします。
ファイル名は何でも構いませんが通常は拡張子を sh
とします。
内容は以下の通りとなります。
#!/bin/sh
mysqldump --single-transaction testdb > /var/sqlbackup/TestDB-`date +%Y%m%d`
rm -f /var/sqldump/TestDB-`date +%Y%m%d -d "-1 week"`
#!/bin/sh
: このファイルをシェルスクリプトであるという事を示します。これが無いと、実行しようとしても何で出来たプログラムなのか解釈出来ずに実行できません。`date +%Y%m%d`
: `で括られた部分は先に実行されて実行結果が代入されます。この場合、date
コマンドが実行されてYYYYMMDD
の形式で表された日付が代入されます。なお、%Y
は大文字でないと、年が下2桁だけ入るようになります。rm -f
: ご存じの通りファイルを削除するコマンドです。シェルスクリプト実行中に削除するかの確認をされても困るので-f
オプションで確認をしないように設定しています。- `
date +%Y%m%d -d "-1 week"`
: こちらも先に実行されてYYYYMMDD
形式で日付が代入されます。こちらはdate
コマンドに-d "-1 week"
という引数を追加で記載しています。これは-1週間、つまり1週間前の日付を表示させる物です。同様に"2 day"
で2日後、-3 month
だと3ヶ月前といった具合になります。これによって1週間前の日付のファイル名を指定して削除する事ができます。(注意点として、これはあくまでも1週間前の日付のファイル名を削除するので、それより前のファイルが何かしらの理由で残った場合は手動で消さない限り残り続けることになります)
mysqldump
と rm
の行をデータベース毎に書いていくことで複数のデータベースのデータベスを一度に取ることが出来ます。
上記のシェルスクリプトではディレクトリの作成はされないので、先に /var/sqlbackup/
ディレクトリを作成します。
[root@localhost ~]# mkdir /var/sqlbackup/
次に、シェルスクリプトを実行する為に実行権限を付けます。
[root@localhost ~]# chmod 700 sqlbackup.sh
sqlbackup.sh
の部分は先ほど作成したファイルとします。なお、今回は所有者(今回はrootで作業しているのでroot)以外には読み書き実行全て出来ないように設定しています。
[root@localhost ~]# ./sqlbackup.sh
ファイル実行時には必ずパスを入れます。現在のディレクトリ内に実行したいファイルがあってもファイル名だけではダメで、相対パスを使って指定する必要があります。(同じディレクトリ内の場合は ./
で表記)
実行後に /var/sqlbackup/
ディレクトリを見ると、ファイルが生成されている事が分かります。
4. バックアップを自動化する
せっかくシェルスクリプトを作ったので、これを定期的に実行するようにしてバックアップを自動化しましょう。
今回はsystemdのtimerを使って定期実行を行います。
まず、systemdのサービスを定義します。以下のファイルを /etc/systemd/system/
に作成します。(ファイル名は何でも構いませんが、拡張子は .service
とします)
[Unit]
Description=SQL backup job
[Service]
User=root
ExecStart= /root/sqlbackup.sh
Description
: 説明文です。サービスの詳細確認などで表示されるので、自分が分かりやすいように記述します。User
: このサービスを実行するユーザです。今回はUNIXソケット認証を使ってrootにログインする前提なので、rootを実行ユーザとしています。ExecStart
: サービス起動時に実行されるコマンドです。作成したシェルスクリプトを相対パスを使わずに記載します。
次に、このサービスを定期的に実行する為のタイマーを作成します。
以下のファイルを /etc/systemd/system/
に作成します。(ファイル名はサービスと同じにすることが多いですが制約はありません。拡張子は .timer
とします)
[Unit]
Description=Run SQL backup once everyday
[Timer]
OnCalendar=daily
Unit=sqlbackup.service
[Install]
WantedBy=timers.target
OnCalendar
: 実行するタイミングを指定します。daily
だと毎日ですが、他にもweekly
やmonthly
も指定できます。Unit
: 実行するサービスを指定します。なお、サービスとタイマーが同じファイル名の場合はこの項目は不要です。WantedBy
: このタイマーの起動条件となります。Timersが起動している必要があるのでこのように設定しています。
また、rootでログインして直接バッチを実行するのとは違い、systemdで起動しようとするとSELinuxに引っ掛かってしまいます。
このため、SELinuxの対象外となるコンテキストを付けてSELinuxに引っ掛からないようにします。(本当はあまり良い方法では無いのですが、SELinux自体を切るよりかは……といったところです)
[root@localhost ~]# semanage fcontext --add --type bin_t "/root/sqlbackup.sh"
[root@localhost ~]# restorecon /root/sqlbackup.sh
あとは、 systemctl
を使ってタイマーを有効化すれば完了です。
[root@localhost ~]# systemctl enable sqlbackup.timer
sqlbackup.timer
の部分は作成したタイマーのファイル名にします。
まとめ
これで自動的にMariaDBのデータベースのバックアップを取るように設定することが出来ました。
最近はデータベースが壊れるという事はあまり聞かないですがサーバの停電など、壊れる可能性は常に存在していますし、壊れるときはあっけなく壊れるものです。
万が一の時のためにもデータベースはこまめにバックアップを取るようにしましょう。(バックアップはデータベースに限らず、ですけどね)
おまけ
日数やバックアップ先を変数とすることで変更を容易にしたバックアップ取得シェルスクリプトです。
バックアップしたいデータベース名は "
で括って半角スペース区切りで書いていくことで複数のデータベースを指定できます。
バックアップ先にデータベース毎のディレクトリを作ってその中にバックアップを行います。
※このスクリプトもエラー処理とかまでは書いてない簡易的な物ですので使用には注意してください。
#!/bin/sh
# バックアップを保管する日数
rotate=10
# バックアップ先
path=/path/to/directory/
# バックアップしたいデータベース名
dbname=("dbname1" "dbname2")
for db in "${dbname[@]}"
do
mkdir -p $path/$db/
mysqldump --single-transaction $db > $path/$db/$db-`date +%Y%m%d`
rm -f $path/$db/$db-`date +%Y%m%d -d "-$rotate day"`
done