何かの作業を行った後に、環境が壊れてしまい現在のサービスが動かなくなってしまった。どうにか作業前の状態に戻せないか?と考えたことはないでしょうか?そんな事態を回避したいならLVM (Logical Volume Manager 論理ボリュームマネージャ)を使ったスナップショットがお勧めです。LVMとは、複数のハードディスクやパーティションをまとめて、単一のLV(論理ボリューム)として扱うことのできるディスク管理機能です。通常ディスクを増やすと作成したパーティションをフォーマットして何処かのディレクトリにマウントしなければいけませんが、/ ルートディレクトリをLVM設定にしておくと/ルートディレクトリのディスク容量自体を増やしていけるのです。LVMには、ある一時点のファイル・ディレクトリの状態を保存するスナップショットという機能があります。通常バックアップを行う場合、毎回コピー元と同じディスク容量のディスクが必要となります。スナップショットは前回との差分を保存していくので、ディスク容量をおさえることができます。今回は、増設したディスク/dev/sdb 10G にLVを作成して定期的そのディスクのスナップショットを作成して、過去のデータに戻す方法を紹介したいと思います。
lvm のツールをインストールするには以下のコマンドを実行します。
$ sudo apt install lvm2
次にコマンドを何度も実行しますが、LVM のデータ領域を作成する手順をまとめると以下のようになります。
物理ボリューム作成(pvcreate) /dev/sdb
↓
ボリュームグループ作成(vgcreate) /dev/sdb に lvm_group というグループを作成する。
↓
論理ボリューム作成(lvcreate) lvm_group に 6Gの lvm_data という名前のデータ領域を作成する。残りの4Gをスナップ領域として使用する。
↓
論理ボリュームにファイルシステム作成 /dev/lvm_group/lvm_data を ext4 でフォーマット
↓
作成したファイルシステムのマウント
#物理ボリュームを作成する
$ sudo pvcreate /dev/sdb
Physical volume "/dev/sdb" successfully created.
#物理ボリュームを確認する
$ sudo pvdisplay
"/dev/sdb" is a new physical volume of "10.00 GiB"
--- NEW Physical volume ---
PV Name /dev/sdb
:
#任意の名前(例 lvm_group) のボリュームグループを作成する
$ sudo vgcreate lvm_group /dev/sdb
Volume group "lvm_group" successfully created
#ボリュームグループを確認する
$ sudo vgdisplay
--- Volume group ---
VG Name lvm_group
:
#任意の名前(例 lvm_data)の論理ボリュームを作成する
$ sudo lvcreate --size 6G --name lvm_data lvm_group
Logical volume "lvm_data" created.
#論理ボリュームを確認する
# /dev/ボリュームグループ名/論理ボリューム名 というデバイスが作成されたことがわかります。
$ sudo lvdisplay
--- Logical volume ---
LV Path /dev/lvm_group/lvm_data
LV Name lvm_data
VG Name lvm_group
:
#論理ボリュームをext4でフォーマットする
$ sudo mkfs.ext4 /dev/lvm_group/lvm_data
#マウント先のディレクトリを作成する
$ sudo mkdir /mnt/lvm_data/
#マウントする
$ sudo mount /dev/lvm_group/lvm_data /mnt/lvm_data/
#ディレクトリのオーナーを一般ユーザにする
$ sudo chown $USER:$USER /mnt/lvm_data/
#テストファイルを作成する
$ touch /mnt/lvm_data/sample0.txt /mnt/lvm_data/sample1.txt /mnt/lvm_data/sample2.txt
#ディレクトリの中身を確認する
$ ls /mnt/lvm_data/
lost+found sample0.txt sample1.txt sample2.txt
#1Gの領域を確保して任意の名前(例 snap)のスナップショットを作成する
$ sudo lvcreate -s -L 1G -n snap /dev/lvm_group/lvm_data
Logical volume "snap" created.
#論理ボリュームをスキャンする。
$ sudo lvscan
ACTIVE Original '/dev/lvm_group/lvm_data' [6.00 GiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap' [1.00 GiB] inherit <- データが溢れると ACTIVE が inactive と表示されます。
#ファイルsample1.txtを削除する。
$ rm /mnt/lvm_data/sample1.txt
#ディレクトリの中身を確認する。
$ ls /mnt/lvm_data/
lost+found sample0.txt sample2.txt
#アンマウントする。
$ sudo umount /mnt/lvm_data/
#スナップショットをデータ領域にマージする
$ sudo lvconvert --merge /dev/lvm_group/snap
Merging of volume lvm_group/snap started.
lvm_group/lvm_data: Merged: 100.00%
#論理ボリュームをスキャンすると利用したスナップショットが消えたことが分かる。
$ sudo lvscan
ACTIVE '/dev/lvm_group/lvm_data' [6.00 GiB] inherit
#マウントする
$ sudo mount /dev/lvm_group/lvm_data /mnt/lvm_data/
#削除したファイルsample1.txtが復元された事を確認する
$ ls /mnt/lvm_data/
lost+found sample0.txt sample1.txt sample2.txt
*スナップショットのサイズは元ディスクのサイズの10%〜20%必要と言われています。変更差分が保存されていくので、データ領域の変更量が多くなると、変更差分を保存しきれなくなり inactive となります。
#アンマウントする。
$ sudo umount /mnt/lvm_data/
#論理ボリュームを削除する。
$ sudo lvremove /dev/lvm_group/lvm_data
#ボリュームグループを削除する。
$ sudo vgremove lvm_group
#物理ボリュームを削除する。
$ sudo pvremove /dev/sdb
次に以下のように4世代スナップショットを保持するスクリプトを作成しました。
snap_cron.sh
#!/bin/bash
#保存するスナップショットの履歴数 4世代
MAXSNAP=4
#現在の日付を取得
SNAPTIME=`date +'%Y%m%d%H%M%S'`;
#現在のスナップショット数を取得
LV_SUM=`lvscan |wc |awk '{print $1}'`
# スナップショットが MAXSNAP だった場合、古いスナップショットを削除する
if [ ${LV_SUM} == $((${MAXSNAP} + 1)) ]; then
# lvscan を行い先頭のスナップショットリストから
# ACTIVE Snapshot '/dev/lvm_group/snap_20210506094148' [900.00 MiB] inherit
# から /dev/lvm_group/snap_20210506094148 を抽出
SNAPNAME=`lvscan |sed -n 2p|awk '{print $3}'|sed -e "s/'//g"`
# スナップショットを削除
lvremove -f ${SNAPNAME}
fi
#新規にスナップショットを作成する。
# 10G-6G は 4G (1Gx4世代 MAXSNAP)だが誤差があり1G一杯に指定出来ないため 900Mとしている
lvcreate -s -L 900M -n snap_${SNAPTIME} /dev/lvm_group/lvm_data
$ sudo lvscan
ACTIVE Original '/dev/lvm_group/lvm_data' [6.00 GiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506094148' [900.00 MiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506095128' [900.00 MiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506095132' [900.00 MiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506095140' [900.00 MiB] inherit
# 4世代以上になると古いスナップショットを削除して新規スナップショットを作成します。
$ sudo ./snap_cron.sh
Logical volume "snap_20210506094148" successfully removed
Logical volume "snap_20210506100119" created.
$ sudo lvscan
ACTIVE Original '/dev/lvm_group/lvm_data' [6.00 GiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506095128' [900.00 MiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506095132' [900.00 MiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506095140' [900.00 MiB] inherit
ACTIVE Snapshot '/dev/lvm_group/snap_20210506100119' [900.00 MiB] inherit
前に紹介した crontab で指定したスクリプトは一般ユーザ権限で動作します。スナップショットは sudo で実行しているようにroot権限で実行させる必要があるため、作成したスクリプトは、データ領域の更新頻度に応じてシステムクーロンが実行される /etc/cron.weekly/ /etc/cron.daily/ に置きます
$ sudo chmod 755 snap_cron.sh
$ sudo chown root:root snap_cron.sh
# 拡張子 .sh を付けていると動かない
$ sudo cp snap_cron.sh /etc/cron.daily/snap_cron