犬でも分かるKVM(3): 仮想マシンのイメージを移行する

システムを仮想マシンで構築することのメリットは、ハードウェアのリソースを効率的に使うという点ももちろんそうですが、仮想マシンに構築したシステムを、イメージファイルとしてまるごと別の環境に簡単に移行(移植)できるという点にあるのではないでしょうか。ということで、あるマシン上で作成した仮想マシンを別のマシンに移行するにはどうしたら良いか、を解説します。

仮想マシンのデータはどのように保存されているか..

タイトルの通り、ここではKVM (Kernel-based Virtual Machine) における話しをします。
KVMで仮想マシンを作成すると、仮想マシンのシステムがインストールされたディスクイメージのファイル(イメージファイル)と、仮想マシンのリソースの定義やホスト側と接続するためのネットワーク設定等が定義されたファイル(XML設定ファイル)が作成されます。KVMの標準設定では、それぞれのファイルは以下に作成されます。

種類パス
イメージファイル/var/lib/libvirt/images/<imagefile>.qcow2
XML設定ファイル/etc/libvirt/qemu/<domain>.xml

ここで、<domain> と <imagefile> はそれぞれ、virsh-install で仮想マシンを作成したときに指定したドメイン名(–name オプション)とイメージファイルのパス(–disk オプション)です。

KVMのファイル格納先

この2つのファイルをコピーして、XML設定ファイルを環境に合わせて修正するという方法もありますが、XML設定ファイルはホスト側の情報も入っているので、修正は容易ではありません。

イメージファイルをimportするのが楽

仮想マシンを別のホストマシンに(もちろん同一のホストマシンでも)移行(移植)するには、イメージファイルだけをコピーして、そのイメージファイルを指定してvirt-install コマンドを –import オプション付きで実行するのが一番楽です。
何故なら、この処理によって、移行先のホストマシンに合わせて、XML設定ファイルを作成してくれるからです。

今回の移行テストで使用するのは、以下のイメージファイルです。

・ファイル名は test0-rocky9.4.qcow2, ファイルのサイズは20GB
・仮想マシンのOSはRocky9.4
・仮想マシンはネットワークインターフェースを2つ(enp1s0とenp2s0)有し、ホストマシンの2つの仮想ブリッジにそれぞれが接続(IPアドレスは固定)

このファイルを別のホストマシンの /var/lib/libvirt/images/ にコピーします。
次の以下の通りコマンドを実行して、このイメージファイルをimportします。

virt-install \
--import \
--name test0 \
--ram 2048 \
--disk path=/var/lib/libvirt/images/test0-rocky9.4.qcow2,size=20 \
--vcpus 1 \
--os-variant rocky9.0 \
--network bridge=virbr0 \
--network bridge=br3 \
--graphics none \
--console pty,target_type=serial

この仮想マシンを作成したホストマシン(ホストA)で実行したvirt-installコマンドと、移行(import)先のホストマシン(ホストB)で実行したvirt-installコマンドでは、一部の引数オプションを変更しています。

ホストBでは新規作成ではなく移行なので、当然「–import」を追加しています。一方で、インストールメディアを指定する「–location」は必要ありません。

ホストAで指定していた「–os-variant rocky9」はホストBでは無効となっていたので、「–os-variant rocky9.0」を指定しました。これは、ホストAとホストBのKVM(libvirt,virt-manager,qemu-kvm,その他のどれなのかは分からないですが)のバージョンの違いから発生していると思われます。
ちなみに「–os-variant」オプションは「–osinfo」オプションという新しい名前に変わっていくみたいなので(この2つのオプションはaliasの関係になっている)、今後は「–osinfo」のほうが良いかもしれません。

ホストAで稼働していたときには、仮想マシンはKVMが作成した仮想ブリッジvirbr0と、ホストAの物理NICに接続可能な仮想ブリッジbr2と接続していましたが、ホストBでは、KVMが作成した仮想ブリッジvirbr0とホストBの物理NICに接続可能な仮想ブリッジbr3に接続しようと思います。従って、「–network bridge=virbr0 –network bridge=br3」のように「–network」オプションを2つ指定しています。

ホストAで仮想マシンを作成するときに指定していたカーネルに対する引数「–extra-args ‘console=ttyS0,115200n8’」は、移行(import)のときはエラー(ERROR Kernel arguments are only supported with location or kernel installs)となったので、削除しました。

virt-installコマンドの実行によって、ホストB向けのXML設定ファイルが新たに作成されます。

importが完了したらそのままコンソールでログイン

virt-install –import … の処理が正常に完了すると、そのまま仮想マシンのコンソールに接続された状態となりますのでrootでログインします。
(コンソールから抜ける場合は、Ctrl + ] キー を同時押下して下さい)

元の仮想マシンは、ネットワークインターフェースに固定のIPアドレスを設定していたので、コンソールからログインした後、IPアドレスの変更します。

まずは、ネットワークインターフェース(デバイス)の状態を確認します。ホストAで稼働していたときと同じインターフェースが存在しています。

# nmcli d s
DEVICE TYPE STATE CONNECTION
enp1s0 ethernet 接続済み enp1s0
enp2s0 ethernet 接続済み enp2s0
lo loopback 接続済み (外部) lo

次に、IPアドレスを確認します。

# ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp1s0: mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:3c:e3:55 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.203/24 brd 192.168.122.255 scope global noprefixroute enp1s0
valid_lft forever preferred_lft forever
3: enp2s0: mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:db:dc:ad brd ff:ff:ff:ff:ff:ff
inet 10.210.28.203/20 brd 10.210.31.255 scope global noprefixroute enp2s0
valid_lft forever preferred_lft forever

仮想ブリッジvirbr0に接続するコネクションenp1s0のIPアドレスのサブネットは、virbr0のサブネットが192.168.122.0/24と同じだったので、仮想マシンを移行(import)直後から192.168.122.0のネットワークに接続できました。 よって、enp1s0のIPアドレスはこのままにしておきます。

コネクションenp2s0のIPアドレスのサブネットは、ホストBの仮想ブリッジbr3が接続しているサブネット 192.168.2.0/24とは異なるので、IPアドレスを変更します。
nmcliコマンドで変更しても良いですが、/etc/NetworkManager/system-connections/enp2s0.nmconnection を直接修正してしまいましょう(こちらのほうが楽なので)。以下のように修正しました(太文字の部分)。

[connection]
id=enp2s0
uuid=4b0e9bc8-b365-3d02-9404-ab74d02fb888
type=ethernet
autoconnect-priority=-999
interface-name=enp2s0
timestamp=1724550496

[ipv4]
address1=192.168.2.203/24
method=manual

[ipv6]
addr-gen-mode=eui64
method=disabled

enp2s0.nmconnectionを修正したら、以下のようにして修正を反映させます(一度、ネットワークインターフェースをdownしてから反映をロードすることに注意して下さい)。

# nmcli con down enp2s0
# nmcli con load /etc/NetworkManager/system-connections/enp2s0.nmconnection
# nmcli con up enp2s0

これで、ホストBの仮想ブリッジbr3が接続しているサブネットにも接続することができました。

移行先でリソースを変更するには..

移行先のホストマシンのリソース(CPUコア数, メモリ容量, ストレージサイズ)の都合によって、移行先では仮想マシンに割り当てるリソースを変更したい場合があります。

コア数とメモリ容量の変更は簡単

コア数(vCPU数)とメモリ容量を変更するには、「virt-install –import」コマンド実行時に指定する「–cpus」オプションと「–ram」オプションで指定して下さい。

今回の例では、ホストA上では、この仮想マシンは、vCPU数 2, メモリ容量 4096MBで稼働していました。ホストB上では、vCPU数 1, メモリ容量 2048MBで問題なく稼働しました。

ストレージサイズの変更は面倒くさい

今回使ったイメージファイルは20GBもあるので、ファイル転送するのにとても時間がかかりました。実際に使用している容量は3GB程度なので、総容量を小さくしたいと思っています。

ネットで調べてみると、大きく2種類の方法があることが分かりました。

・ひとつ目は、仮想マシン内のパーティションサイズはそのままで、イメージファイルから無駄な領域を取り除いて圧縮する方法。
・ふたつ目は、仮想マシン内のパーティションサイズを変更する方法。

本来はふたつ目の方法で、イメージファイルサイズを縮小したいのですが、現状の私の知識では、その方法が確立できていません(本当にできるのかできないのかも分からない)。なんかいろいろと複雑だということをネット記事では見つけました。

なので、今回はひとつ目の方法だけ試してみたので、以下の通り紹介したいと思います。この方法だけでも、イメージファイルを別のマシンに持っていくときに転送容量を削減できるので、目的の半分は達成しているとも言えます。

(1) ストレージの空いている領域いっぱいにゼロを書き込む

分かりやすくいうと、ストレージの空き容量がなくなるくらいの巨大ファイル(中身はゼロが書き込まれている)を作成するということです。ddコマンドを使って、/bigtmpfile というファイルを容量いっぱいまで作成します(ファイル名はなんでも良いです)。

# dd if=/dev/zero of=/bigtmpfile bs=4096k
dd: '/bigtmpfile' の書き込みエラー: デバイスに空き領域がありません
3581+0 レコード入力
3580+0 レコード出力
15015804928 bytes (15 GB, 14 GiB) copied, 51.9185 s, 289 MB/s
[root@test0 ~]# df
ファイルシス 1K-ブロック 使用 使用可 使用% マウント位置
devtmpfs 4096 0 4096 0% /dev
tmpfs 909132 0 909132 0% /dev/shm
tmpfs 363656 5128 358528 2% /run
/dev/mapper/rl-root 17756160 17755268 892 100% /
/dev/vda1 983040 318720 664320 33% /boot
tmpfs 181824 4 181820 1% /run/user/0

案の定、「デバイスに空き領域がありません」というエラーになります。

(2) (1)で作成した巨大ファイルを削除する

# rm -f /bigtmpfile

意味のないことのように思えますが、ゼロが書き込まれた巨大ファイルを削除した後も、イメージファイル内にはこの容量分だけゼロデータが書き込まれた状態になります。

(3) 仮想マシンをshutdownする

# virsh shutdown test0
Domain 'test0' is being shutdown

(4) イメージファイルを圧縮する

「qemu-img convert」コマンドで、元のイメージファイルを圧縮します。圧縮するとゼロデータの領域をきれいに取り除いた、新しいイメージファイルを作成してくれます。
以下のコマンドは元のイメージファイル test0-rocky9.4.qcow2 を圧縮して、test1-rocky9.4.qcow2 を新たに作成しています。

# qemu-img convert -O qcow2 /var/lib/libvirt/images/test0-rocky9.4.qcow2 /var/lib/libvirt/images/test1-rocky9.4.qcow2
# ls -al /var/lib/libvirt/images/test1-rocky9.4.qcow2
-rw-r--r-- 1 root root 3561816064 Oct 20 10:52 /var/lib/libvirt/images/test1-rocky9.4.qcow2

なるほど、(1)(2)でゼロデータファイルを作成して削除したのは、こういう意味があったのですね。圧縮後のイメージファイルのサイズは3.6GBほどになっています。圧縮前のファイルは20GBほどあったので、効果は抜群です。

(5) 圧縮したファイルを元のイメージファイルに上書きする

# mv /var/lib/libvirt/images/test1-rocky9.4.qcow2 /var/lib/libvirt/images/test0-rocky9.4.qcow2

これでイメージファイルの縮小は完了です。試しに仮想マシンを起動して、パーティションのサイズを確認してみましょう。

# df
ファイルシス 1K-ブロック 使用 使用可 使用% マウント位置
devtmpfs 4096 0 4096 0% /dev
tmpfs 909128 0 909128 0% /dev/shm
tmpfs 363652 5128 358524 2% /run
/dev/mapper/rl-root 17756160 17755416 744 100% /
/dev/vda1 983040 318720 664320 33% /boot
tmpfs 181824 4 181820 1% /run/user/0

はいっ、パーティションの総容量は、圧縮前とまったく変わっていません(笑)。

この仮想マシンを利用していくと、イメージファイルのサイズは増えて20GB近くになるのだと思います。このイメージファイルを、再度、別のマシンに移行するときは、移行前にこの手順で、イメージファイルの圧縮をすると良いと思います。

広告主へのリンク




ミニPCのお勧め

おウチでLinuxを勉強するのに、ミニPCはどうですか。
仮想環境(KVM)使えば、仮想マシンを複数起動できますし、とても安価にシステム構築の練習ができます。ミニPCはとっても静かで消費電力も小さいので部屋で常時起動させています。

このブログにおける関連リンク

犬でも分かるKVMシリーズ

コメント

タイトルとURLをコピーしました