ローカル開発用の VM を構築するための速習 libvirt¶
導入¶
個人的にローカル開発用の仮想マシン(以下 VM)を構築するのに snap の multipass を利用していましたが、VM を停止できなくなったり、status が unknown になったりとどうも不安定 [1] なので、いっそのこと libvirt を使ってみることにしました。そのため、必要知識を整理しました。
基礎¶
libvirt は openvz, kvm, qemu, virtualbox, xen などの仮想化エンジンを統一的に制御するためのAPI群
最近はハイパーバイザーとして Qemu/KVM が用いられる
libvirt の cli として virsh, virt-install がある
接続タイプ¶
libvirt の接続タイプとして qemu:///system と qemu:///session がある
qemu:///system
root ユーザあるいは
libvirtユーザグループに属しているユーザはシステム接続になる
qemu:///session
libvirtユーザグループに属していない非 root ユーザはセッション接続になる他のユーザ(root を含む)によって作成された仮想マシンにはアクセスできない
セッション接続ではネットワークの作成が不可、VM の自動起動設定が不可などの制限がある
接続タイプの確認は以下のコマンドで行える
$ virsh uri
qemu:///session
$ sudo virsh uri
qemu:///system
システム接続の方がやれることは多いが、開発目的ではセッション接続で十分だと思われるので、以後その前提で進める。 [2]
環境構築¶
libvirt, virt-manager, passt をインストール
# arch系ディストリビューションの場合 $ sudo pacman -S libvirt passt virt-manager
デフォルトでインストールされている場合もある [3]
virt-manager は VM を管理するための GUI ツールなので必須ではないが、libvirt では VM 等の設定が xml で管理されるので GUI で確認・編集できた方が便利
セッション接続の場合、libvirt デーモンは不要だが、一応状況を確認しておく
$ sudo systemctl status libvirtd
$ sudo systemctl status virtqemud
$ sudo systemctl status virtnetworkd
$ sudo systemctl status virtstoraged
libvirtd はレガシーで現在はモジュールに分割された複数のデーモンで構成される
virtqemud, virtnetworkd, virtstoraged など
稼働中の VM がなければ libvirtd より分割されたデーモンを使う方がよいと思われる
VM の作成(1): イメージ¶
開発用の VM を作成するたびに毎回対話的に設定を行うのは面倒なのでクラウドイメージ [4] と cloud-init を使う
Ubuntu では以下のサイトでクラウドイメージが配布されている
VM 内の変更はクラウドイメージに直接書き込まれる形になる
cloud-init は二重に実行されないための仕組みがあり、cloud-init が実行済み状態のディスクイメージに対して設定の変更が反映されない場合がある
上記の都合により cloud-init の検証時は毎回新しいイメージを作成する必要があり、都度コピーをとったりダウンロードしなおすのは面倒なので、差分イメージを利用する
差分イメージ¶
qemu の機能でその名前のとおり対象のイメージからの差分情報のみを含んだイメージを作成できる
ベースイメージへは絶対パスで参照するのでベースイメージの配置を変えてはいけない
変える必要がある場合は
$ qemu-img rebaseでパスを書き換える
ベースイメージは上書きするのも NG なので日付などで管理し、書き込みも禁止しておく
# ディレクトリ構成はお好みでよいが運用の一例としてこんな感じかと
$ mkdir -p ~/libvirt/{base,vms}
$ cd ~/libvirt
$ curl -o base/ubuntu-22.04-20250702.qcow2 https://cloud-images.ubuntu.com/jammy/20250702/jammy-server-cloudimg-amd64-disk-kvm.img
$ chmod 444 base/ubuntu-22.04-20250702.qcow2
# 差分イメージを作成する。ディスクサイズもここで指定する。
$ qemu-img create -f qcow2 -b $HOME/libvirt/base/ubuntu-22.04-20250702.qcow2 -F qcow2 vms/myvm1.qcow2 16G
# イメージの確認
$ qemu-img info vms/myvm1.qcow2
Tip
qcow2 は qemu で使用されるイメージフォーマット
VM の作成(2): cloud-init¶
cloud-init を使う際、以前は設定情報を記述した user-data.yaml と meta-data.yaml を含んだ seed.iso を作る必要があったが virt-install の --cloud-init オプションを使うと yaml ファイルを直接指定できる。
しかし環境によっては --cloud-init オプションが期待した通りに動作しないこともあるらしいので、一旦最低限の VM を作成し動作検証する。この状態ではネットワーク設定をしていないので VM からインターネットに接続したり外から ssh ができない。
user-data.yaml
#cloud-config
ssh_pwauth: false
users:
- default
chpasswd:
list: |
ubuntu:ubuntu
expire: False
Ubuntu のクラウドイメージでは、default ユーザは "ubuntu" という名前で作成される
明示的に users で指定しないと ubuntu ユーザは作成されない
デフォルトで
ubuntuユーザのパスワードは設定されていないため、chpasswdでパスワード(ubuntu) を設定する [5]ssh 接続(公開鍵)を設定した場合はパスワードは不要となるが、最初のうちは console で接続できるようにパスワードを設定した方が良いと思われる
Tip
好みが分かれるところだと思われるが、 default でなく明示的に ubuntu ユーザを指定してもよい
#cloud-config
ssh_pwauth: false
users:
- name: ubuntu
uid: 1001
gecos: Ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
lock_passwd: false
plain_text_passwd: ubuntu
meta-data.yaml
instance-id: myvm1
local-hostname: myvm1
instance-id はなんでもよいため上記では myvm1 としているが、
$ uuidgenで uuid を生成するのが無難か
user-data.yaml と meta-data.yaml を作成後、以下のコマンドで VM を作成する。
$ virt-install \
--name myvm1 \
--autoconsole text \
--import \
--memory 2048 --vcpus=2 \
--osinfo generic \
--disk bus=virtio,path="$HOME/libvirt/vms/myvm1.qcow2" \
--cloud-init user-data=user-data.yaml,meta-data=meta-data.yaml
--autoconsoleで自動的に$ virsh console myvm1を行いシリアル接続する最初のうちは console で入って cloud-init が期待した設定になっているかは確認したほうがよい。不要な場合は
--noautoconsoleに変える。起動後に
ubuntu/ubuntuでログインできるか確認するcloud-init の状態は
$ cloud-init statusコマンドや/var/log/cloud-init.logのログで確認するconsole は
ctrl+]で抜ける
seed.iso¶
--cloud-init が期待した動作にならない場合、以下のコマンドで seed.iso を作成してみる
$ genisoimage -output seed.iso -volid cidata -joliet -rock user-data.yaml meta-data.yaml
virt-install の --cloud-init オプションを以下に変更する
--disk path=seed.iso,device=cdrom
VM の操作¶
よく使うコマンドを例に挙げる。
$ virsh start myvm1 # 停止中の VM を起動
$ virsh shutdown myvm1 # 起動中の VM を停止
$ virsh destroy myvm1 # 起動中の VM を強制停止
$ virsh undefine myvm1 # VM を登録解除
$ virsh undefine myvm1 --remove-all-storage # VM を登録解除してディスク( ~/libvirt/vms/myvm1.qcow2 など) を削除
$ virsh console myvm1 # VM にシリアル接続
$ virsh dumpxml myvm1 # VM の設定を出力(xml)
$ virsh edit myvm1 # VM の設定を編集
virt-manager では xml を直接編集せずに GUI で設定の変更が可能 ( すべての設定に対応しているわけではない )
ネットワーク¶
セッション接続でネットワークを利用する場合、 passt が利用できる。 passt ではホストから VM へのアクセスはポートフォワーディングを用いる。
先ほどの VM を削除し、 virt-install にネットワークオプションを追加する。また、 user-data.yaml を編集し、ssh 接続用のユーザを追加する。
Tip
VM を削除しなくても設定の変更でネットワーク設定を追加することもできる
#cloud-config
ssh_pwauth: false
users:
- default
- name: yourname
lock_passwd: true
sudo: ALL=(ALL) NOPASSWD:ALL
uid: 1000
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3Nz...(略, .ssh/id_ed25519.pub 等の内容をコピペ)
chpasswd:
list: |
ubuntu:ubuntu
expire: False
$ virsh shutdown myvm1
$ virsh undefine myvm1 --remove-all-storage
$ # イメージ再作成
$ qemu-img create -f qcow2 -b $HOME/libvirt/base/ubuntu-22.04-20250702.qcow2 -F qcow2 vms/myvm1.qcow2 16G
$ virt-install \
--name myvm1 \
--autoconsole text \
--import \
--memory 2048 --vcpus=2 \
--osinfo generic \
--disk bus=virtio,path="$HOME/libvirt/vms/myvm1.qcow2" \
--network passt,model=virtio,portForward=20222:22 \
--cloud-init user-data=user-data.yaml,meta-data=meta-data.yaml
これにより、VM からのインターネット接続と、ssh接続 (ローカルの 20222 を 22 にポートフォワーディング ) ができる。
VM を複数起動する場合、ローカルポートはかぶらないようにする(若干面倒)
$ ssh -i .ssh/id_ed25519 -p 20222 yourname@localhost
Tip
portForward はコマンドラインからでは複数設定できない(?) ようなので、複数のポートを公開したい場合は xml を編集する必要がある
<!-- 追加で 8000番ポートを開ける例 -->
<interface type="user">
<mac address="52:54:00:62:e2:d6"/>
<portForward proto="tcp">
<range start="20222" to="22"/>
<range start="8000" to="8000"/>
</portForward>
<model type="virtio"/>
<backend type="passt"/>
<alias name="net0"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0"/>
</interface>
フォルダ共有¶
ファイルシステムのドライバーとして virtio-9p (デフォルト) と virtiofs が選択できる
パフォーマンスは
virtiofsの方がよいらしいが、virtiofsをセッション接続で用いる場合、ゲストOSの共有対象のディレクトリのユーザがホストOSの root にマッピングされるため [6] 、ゲストOS側のログインユーザでは Permisson Error が起きる可能性があり、使い勝手が若干よくない。共有フォルダに書き込みを行う場合は 9p の方が楽なので、ここでは virtio-9p を使う。ただし、ホストOS とゲストOS で UID/GID を揃える必要がある。
共有フォルダを利用する場合、 virt-install に以下のような --filesystem オプションを追加する
--filesystem $HOME/path/to/shared,shared,type=mount,accessmode=passthrough \
VM にログインして以下のコマンドでマウントする
$ mkdir shared
$ sudo mount -t 9p -o trans=virtio shared shared
virtiofs の場合¶
virtiofs を利用する場合は --memorybacking の設定が追加で必要になる。
# virtiofs が利用できるか確認
$ modinfo virtiofs
# virt-install のオプションに以下を追加
--memorybacking source.type=memfd,access.mode=shared \
--filesystem $HOME/path/to/host/shared,shared,type=mount,driver.type=virtiofs \
virtiofs の場合の mount コマンドは以下
$ mkdir shared
$ sudo mount -t virtiofs shared shared
Tip
VSCode では remote-ssh, sftp, rsync などいろいろなファイル同期手段があるので、開発用途VM としてはそれらを使うのもよい
おまけ: システム接続でのコマンド¶
あまり検証してないメモ書きなので、参考程度に
環境設定
$ sudo systemctl status libvirtd
$ sudo systemctl enable --now virtqemud.socket
$ sudo systemctl enable --now virtnetworkd.socket
$ sudo systemctl enable --now virtstoraged.socket
$ sudo systemctl start virtqemud
$ sudo systemctl start virtnetworkd
$ sudo systemctl start virtstoraged
ネットワーク設定
# net-undefine したときに xml ごと消されることがあるのでバックアップしておく
$ sudo cp /etc/libvirt/qemu/networks/default.xml /etc/libvirt/qemu/networks/default.xml.bak
# ネットワークを作成
$ sudo virsh net-define /etc/libvirt/qemu/networks/default.xml
$ sudo virsh net-autostart default
$ sudo virsh net-start default
$ sudo virsh net-list --all
# ネットワークの自動起動解除
$ sudo virsh net-autostart --disable default
# ネットワークの削除(xml も同時に消される模様)
$ sudo virsh net-undefine default
VM でネットワーク利用
--network bridge=virbr0,model=virtio \
# or
--network default,model=virtio \
IP の確認
$ virsh domifaddr myvm1
$ sudo virsh net-dhcp-leases default
$ sudo virsh dumpxml myvm1 | grep interface -A 10
VM 側でのネットワークインターフェイスの確認・有効化
$ ip -br addr show
$ sudo ip link set virbr0 up
参考¶
ローカル開発用の VM を構築するための速習 libvirt — ykrods note
https://www.ykrods.net/posts/2025/07/13/libvirt-local-vm-quickstart/