ubuntu+kvm+gluster+lvmでlive migrationとスナップショット

# 本文書の目的
ミニPC3台で構成したubuntu環境において、live migrationとスナップショットが使えるkvm環境を構築した。その構築手順と使用手順を簡単に記述する。
# 環境の説明
## ハードウエア
- Shuttle NC10U5×2台
    - CPU Intel Core i5-8265U
    - メモリ 32Gバイト
    - ディスク 1TB SSD×2
    - OS ubuntu server 24.04
    - LAN 1Gbs
- Shuttle DS77U5×1台
    - CPU Intel Core i5-7200U
    - メモリ 32Gバイト
    - ディスク 1TB SSD×2
    - OS ubuntu server 24.04
    - LAN 1Gbs
## OS環境
### ディスク構成
<dl>
<dt>
sda1 sdb1
</dt>
<dd>
EFI区画
</dd>
<dt>
md0←sda2+sdb2
</dt>
<dd>
RAID1ディスク
</dd>
<dt>
vg0←md0
</dt>
<dd>
ボリュームグループ
</dd>
<dt>
vg0/lv0→/
</dt>
<dd>
論理ボリューム
</dd>
</dl>

## ホスト一覧
- host01
- host02
- host03
## kvm環境の構築
### インストール
全ノードで実施する。
~~~
apt install qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager
~~~
### 構成
kvm環境の構築と仮想マシンの作成については省略する。
# gluster
## インストール
各ノードで実施する。
~~~
apt install glusterfs-server glusterfs-client
~~~
## シンボリュームの作成
各ノードで実施する。
### 1. シンプールの作成
シンプールを作成する。例ではlvthinpool0という名前で容量40Gバイトのシンプールをボリュームグループvg0に作成している。
~~~
lvcreate -L 40G --thinpool lvthinpool0 vg0
~~~
### 2.シンボリュームの作成
例ではlvgfs1という名前で容量40Gバイトのシンボリュームをシンプールlvthinpool0に作成している。シンボリュームはシンプロビジョニングされているので容量をシンプールより大きくすることも可能である。作成したシンボリュームはext4でフォーマットし、/gfs1にマウントする。
~~~
lvcreate -V 40G -nlvgfs1 vg0/lvthinpool0 --thin
/gfs1
mkfs /dev/vg0/lvgfs1
mkdir /gfs1
echo /dev/vg0/lvgfs1 /gfs1 ext4 defaults 0 0 >>/etc/fstab
systemctl daemon-reload
mount /gfs1
~~~
## クラスターボリューム
ホストのうちの1台で実施する。例ではhost01で実施している。
### 作成
クラスターボリュームをレプリカボリュームとして作成する。例ではgfsv1という名前で作成している。3台にコピーが作られるので、クラスターボリュームの実効容量は各ホストのシンボリュームの合計サイズの3分の1になる。
~~~
> gluster peer probe host02
> gluster peer probe host03
> gluster peer status

Number of Peers: 2

Hostname: host02
Uuid: af5b7a4a-c79b-4141-8d04-4eec1bf1956e
State: Peer in Cluster (Connected)

Hostname: host03
Uuid: 61615793-a3c8-41ba-bacc-95b70b3f39a9
State: Peer in Cluster (Connected)

> gluster volume create gfsv1 replica 3 arbiter 1 transport tcp host01:/gfs1/gfsv1 host02:/gfs1/gfsv1 host03:/gfs1/gfsv1 force
> gluster volume start gfsv1
~~~
### ボリュームステータスの表示
どのホストで実施してもよい。
~~~
> gluster volume heal gfsv1 info

Brick host01:/gfs1/gfsv1
Status: Connected
Number of entries: 0

Brick host02:/gfs1/gfsv1
Status: Connected
Number of entries: 0

Brick host03:/gfs1/gfsv1
Status: Connected
Number of entries: 0

> gluster volume status gfsv1

Gluster process                             TCP Port  RDMA Port  Online  Pid
------------------------------------------------------------------------------
Brick host01:/gfs1/gfsv1                    58430     0          Y       2289
Brick host02:/gfs1/gfsv1                    49567     0          Y       2225
Brick host03:/gfs1/gfsv1                    60344     0          Y       2676
Self-heal Daemon on localhost               N/A       N/A        Y       2380
Self-heal Daemon on host02                  N/A       N/A        Y       2263
Self-heal Daemon on host03                  N/A       N/A        Y       2694

Task Status of Volume gfsv1
------------------------------------------------------------------------------
There are no active volume tasks

> gluster volume status gfsv1 detail

Status of volume: gfsv1
------------------------------------------------------------------------------
Brick                : Brick host01:/gfs1/gfsv1
TCP Port             : 58430
RDMA Port            : 0
Online               : Y
Pid                  : 2289
File System          : ext4
Device               : /dev/mapper/vg0-lvgfs1
Mount Options        : rw,relatime,stripe=16
Inode Size           : 256
Disk Space Free      : 11.5GB
Total Disk Space     : 44.2GB
Inode Count          : 2949120
Free Inodes          : 2948816
------------------------------------------------------------------------------
Brick                : Brick host02:/gfs1/gfsv1
TCP Port             : 49567
RDMA Port            : 0
Online               : Y
Pid                  : 2225
File System          : ext4
Device               : /dev/mapper/vg0-lvgfs1
Mount Options        : rw,relatime,stripe=16
Inode Size           : 256
Disk Space Free      : 11.5GB
Total Disk Space     : 44.2GB
Inode Count          : 2949120
Free Inodes          : 2948816
------------------------------------------------------------------------------
Brick                : Brick kost03:/gfs1/gfsv1
TCP Port             : 60344
RDMA Port            : 0
Online               : Y
Pid                  : 2676
File System          : ext4
Device               : /dev/mapper/vg0-lvgfs1
Mount Options        : rw,relatime,stripe=16
Inode Size           : 256
Disk Space Free      : 44.2GB
Total Disk Space     : 44.2GB
Inode Count          : 2949120
Free Inodes          : 2948817
~~~
## クライアント操作
### マウント
各ホストで実施する。例ではhost01での例となる。glusterボリュームを/var/lib/libvirt/gfs1にマウントしている。
~~~
echo host01:/gfsv1 /var/lib/libvirt/gfs1 glusterfs noauto 0 0 >>/etc/fstab
systemctl daemon-reload
mount /var/lib/libvirt/gfs1
~~~
crontabに以下の行を加えることで、起動時にマウントすることが出来る。
~~~
@reboot mount /var/lib/libvirt/gfs1
~~~

## kvm仮想マシンの設定
仮想マシンを作成したホストで実施する。仮想マシンはシャットダウンしておく。
virsh editで\<driver name='qemu' type='qcow2'…の部分を探し出し、cache='none'を追加して\<driver name='qemu' type='qcow2' cache='none'/>とする。
仮想マシンの仮想ディスクファイルは/var/lib/libvirt/gfs1にコピーし、仮想マシンの構成もここを指すようにする。
## 仮想マシンの移動
### オンライン
host01上で動作しているkvm01をhost02に稼働したまま移動するには以下のコマンドを実行する。
~~~
virsh migrate --live kvm01 qemu+ssh://host02/system
~~~
### オフライン
host01上でシャットダウン状態のkvm01をhost02に移動するには以下のコマンドを実行する。
~~~
virsh migrate --offline --persistent kvm01 qemu+ssh://host02/system
~~~
## ボリュームの拡張
### シンプールの拡張
シンプールの拡張は各ホストで実施する。例ではボリュームグループvg0にあるシンプールlvthinpool0のサイズを10Gバイト拡張している。
~~~
lvextend -L +10G /dev/vg0/lvthinpool0
~~~
### シンボリュームの拡張
シンボリュームの拡張は各ホストで実施する。例ではボリュームグループvg0にあるシンボリュームlvgfs1のサイズを5Gバイト拡張している。
~~~
lvextend -L +5G /dev/vg0/lvgfs1
resize2fs /dev/vg0/lvgfs1
~~~
## スナップショット
#### 作成
シンボリュームで構築したglusterボリュームはスナップショットを取得することができる。例ではglusterボリュームgfsv1にgfsv1snapという名前でスナップショットを作成している。実際のスナップショットの名前にはタイムスタンプが付加される。
~~~
gluster snapshot create gfsv1snap gfsv1
~~~
#### スナップショットの表示
glusterボリュームにあるスナップショットの一覧は以下のコマンで表示できる。
~~~
> gluster snapshot list
gfsv1snap_GMT-2025.04.30-16.00.26
gfsv1snap_GMT-2025.05.01-16.00.35
~~~
個々のスナップショットの情報は以下のコマンドで表示できる。
~~~
> gluster snapshot info gfsv1snap_GMT-2025.04.30-16.00.26

Snapshot                  : gfsv1snap_GMT-2025.04.30-16.00.26
Snap UUID                 : 5f91fb90-7424-4b68-80a9-d8f61e21c8a5
Created                   : 2025-04-30 16:00:26 +0000
Snap Volumes:

        Snap Volume Name          : 522721b0fe3d4777a9a800fc34c3a903
        Origin Volume name        : gfsv1
        Snaps taken for gfsv1      : 2
        Snaps available for gfsv1  : 8
        Status                    : Stopped
~~~
glusterボリュームのスナップショットがどのような形で個々のホストに存在するかは以下のコマンで表示する。
~~~
> gluster snapshot status

Snap Name : gfsv1snap_GMT-2025.04.30-16.00.26
Snap UUID : 5f91fb90-7424-4b68-80a9-d8f61e21c8a5

        Brick Path        :   host01:/run/gluster/snaps/522721b0fe3d4777a9a800fc34c3a903/brick1/gfsv1
        Volume Group      :   N/A (Deactivated Snapshot)
        Brick Running     :   No
        Brick PID         :   N/A
        Data Percentage   :   N/A
        LV Size           :   N/A

        Brick Path        :   host02:/run/gluster/snaps/522721b0fe3d4777a9a800fc34c3a903/brick2/gfsv1
        Volume Group      :   N/A (Deactivated Snapshot)
        Brick Running     :   No
        Brick PID         :   N/A
        Data Percentage   :   N/A
        LV Size           :   N/A

        Brick Path        :   host03:/run/gluster/snaps/522721b0fe3d4777a9a800fc34c3a903/brick3/gfsv1
        Volume Group      :   N/A (Deactivated Snapshot)
        Brick Running     :   No
        Brick PID         :   N/A
        Data Percentage   :   N/A
        LV Size           :   N/A

Snap Name : gfsv1snap_GMT-2025.05.01-16.00.35
Snap UUID : 05f6561c-25b5-495c-a288-193c2583d974

        Brick Path        :   host01:/run/gluster/snaps/c0a28a02552747c39d05edb84f46bf8d/brick1/gfsv1
        Volume Group      :   N/A (Deactivated Snapshot)
        Brick Running     :   No
        Brick PID         :   N/A
        Data Percentage   :   N/A
        LV Size           :   N/A

        Brick Path        :   host02:/run/gluster/snaps/c0a28a02552747c39d05edb84f46bf8d/brick2/gfsv1
        Volume Group      :   N/A (Deactivated Snapshot)
        Brick Running     :   No
        Brick PID         :   N/A
        Data Percentage   :   N/A
        LV Size           :   N/A

        Brick Path        :   host03:/run/gluster/snaps/c0a28a02552747c39d05edb84f46bf8d/brick3/gfsv1
        Volume Group      :   N/A (Deactivated Snapshot)
        Brick Running     :   No
        Brick PID         :   N/A
        Data Percentage   :   N/A
        LV Size           :   N/A
~~~
#### 削除
スナップショットの削除の例を示す。一番目は個々のスナップショットを削除する、2番目はglusterボリュームgfsv1に存在するスナップショットをすべて、3番目はglusterボリュームに存在するスナップショットをすべて削除する例となる。
~~~
gluster snapshot delete gfsv1snap_GMT-20.27-22.21.22
gluster snapshot delete volume gfsv1
gluster snapshot delete all
~~~
#### 仮想マシンの復元
スナップショットからスナップショット取得時の仮想ディスクファイルを取り出して上書きコピーすることで、仮想マシンの状態を復元できる。復元するためには、仮想マシンは予めシャットダウンしておく必要がある。
以下の手順でスナップショットをマウントすることができる。例ではスナップショットを/mntにマウントしている。
~~~
gluster snapshot activate gfsv1snap_GMT-20.27-22.21.22
mount -t glusterfs haguro:/snaps/gfsv1snap_GMT-2025.04.27-22.21.22/gfsv1 /mnt
~~~
スナップショットの中身を取り出したら、スナップショットを元の状態に戻す。
~~~
umount /mnt
gluster snapshot deactivate gfsv1snap_GMT-20.27-22.21.22
~~~
#### 自動削除
スナップショットを自動削除とすることで、cronなどで定期的にスナップショットを作成し、古いものから自動で削除するよう設定できる。以下では、ハードリミットを10、ソフトリミットを10の80%の8に設定している。8を超えるスナップショットは自動的に削除される。
~~~
> gluster snapshot config snap-max-hard-limit 10
> gluster snapshot config snap-max-soft-limit 80
> gluster snapshot config auto-delete enable
> gluster snapshot config

Snapshot System Configuration:
snap-max-hard-limit : 10
snap-max-soft-limit : 80%
auto-delete : enable
activate-on-create : disable

Snapshot Volume Configuration:

Volume : gfsv1
snap-max-hard-limit : 256
Effective snap-max-hard-limit : 10
Effective snap-max-soft-limit : 8 (80%)
~~~
## drbdとの違い
drbdを利用して、一つの仮想マシンの仮想ディスクファイルを二つのホストで共用することができる。glusterではvirsh migrateの一コマンドで仮想マシンが移動できるがdrbdに場合は、移行先と移行元の両方での操作が必要になる。
1. 移行元で仮想マシンをシャットダウン
1. 移行元のdrbdボリュームをアンマウント
1. 移行元のdrbdボリュームをセカンダリに変更
1. 移行先のdrbdボリュームをプライマリに変更
1. 移行先のrbdボリュームをマウント
1. 移行先の仮想マシンをスタート

# 参考
## kvmのスナップショット操作
### スナップショットの作成
仮想マシンkvm01のスナップショットをkvm01-snap01という名前で作成する。
~~~
virsh snapshot-create-as kvm01 kvm01-snap01 --atomic --disk-only
~~~
作成したナップショットが外部スナップショットであることを確認する。
~~~
virsh snapshot-dumpxml kvm01 kvm01-snap01 | grep external
~~~
### スナップショットの一覧
仮想マシンkvm01のスナップショットの一覧を表示する。
~~~
virsh snapshot-list kvm01
~~~
仮想マシンkvm01のスナップショットの一覧をツリー表示する。
~~~
virsh snapshot-list kvm01 --tree
~~~
### 仮想マシンをスナップショットから復元
仮想マシンkvm01をスナップショットkvm01-snap01の状態に復元する。仮想マシンは予めシャットダウンしておく必要がある。
~~~
virsh snapshot-revert kvm01 kvm01-snap01
~~~
### スナップショットの削除
仮想マシンkvm01のスナップショットkvm01-snap01を削除する。
~~~
virsh snapshot-delete kvm01 kvm01-snap01
~~~
### glusterのスナップショットとの違い
glusterのスナップショットから特定の仮想マシンの状態を復元するには
1. スナップショットをアクティベート
2. スナップショットをマウント
3. 仮想マシンをシャットダウン
3. マウントしたスナップにある仮想ディスクファイルで仮想マシンの仮想ディスクファイルを上書き

という手順を踏むため、kvmのスナップショットに比べて少し手間がかかる。しかしスナップショットにある仮想ディスクファイルをマウントして必要なファイルを取り出すことが可能で、きめ細かい復旧が可能である。kvmのスナップショットからの復旧では全体の復旧だけしか出来ない。

また、kvmのスナップショットは自動削除の機能はないが、glusterのスナップショットは自動削除が出来る。glusterではcronなどで定期的に自動スナップショットを作成し古いスナップショットを自動削除する機能を簡単に実装できる。一方kvmのスナップショットでは自動削除を自分で実装する必要がある。

なお、glusterボリュームに仮想ディスクファイルを置いている仮想マシンのスナップショットは稼働中だと取得できない。glusterボリュームのスナップショットとkvmのスナップショットは並用できず、glusterボリュームのスナップショットのみが使える。

自動削除が必要な理由であるが、一般論として古いスナップショットを残しておくと、ディスクのパフォーマンスに悪影響が出る。またディスクスペースの消費も増大し続ける。このため、長期間にわたりスナップショットを残しておくことはkvmでもglusterでも好ましくない。
# 参考文献
https://docs.redhat.com/en/documentation/red_hat_gluster_storage/3.5