ずいぶん前に Docker Desktop (Docker for Windows) のために Hyper-V を有効にしたため VirtualBox が使えなくなりました。それに伴って Vagrant も使わなくなりました。
最初は Vagrant が使えなくなるのは辛いかな・・と思っていたのですが、開発系の作業は Docker で事足りているし、サーバ系の作業でどうしても Docker では足りないときは KVM (virt-install) とか EC2 にインスタンスを作るなどすればいいので、Vagrant が使えなくなってもそれほど困りませんでした。
ただ、Vagrant でローカルの VirtualBox に環境を作るのと比べると KVM に virt-install でセットアップしたり EC2 インスタンスをマネジメントコンソールや CLI で作るのは作業のステップが多くて煩雑な気もしています。
そこで、KVM ホストにゲストをサクサク作ったり消したりできるよう vagrant-libvirt を試してみました。vagrant-libvirt なら Hyper-V 有効なままでも使用できます。
libvirtd のリモートアクセス
KVM は Vagrant を実行するホストとは別のホストに構築しているので(というか Vagrant を実行するホストは Windows の WSL の Fedora 31 なので)、まずは KVM ホストの libvirtd にリモート接続できるようにする必要があります。
下記によると TCP や TLS で接続できるようですが、素のままでも SSH で接続できます。
次の手順で SSH 経由で接続できることを確認しました。
# libvirt-client パッケージを入れる sudo dnf install libvirt-client # 設定ファイルのディレクトリを作成 mkdir -pv ~/.config/libvirt/ # デフォルトの接続先を指定 echo 'uri_default = "qemu+ssh://root@192.0.2.123/system"' > ~/.config/libvirt/libvirt.conf # 接続確認 virsh list --all
vagrant-libvirt をインストール
Vagrant に vagrant-libvirt プラグインを入れます。
Fedora 31 だと普通にはインストールできず CONFIGURE_ARGS
を指定する必要がありました。
sudo dnf install -y libvirt-devel env CONFIGURE_ARGS="with-libvirt-include=/usr/include/libvirt with-libvirt-lib=/usr/lib64" \ vagrant plugin install vagrant-libvirt
この手順で vagrant-libvirt はインストールできたものの vagrant up
がエラーになりました。
vagrant up : /usr/lib64/libssh.so.4: undefined symbol: EVP_KDF_ctrl, version OPENSSL_1_1_1b - ...
簡単には解決出来なさそうです。
dnf で Fedora のリポジトリから Vagrant とプラグインを一緒にインストール出来るようなので、オフィシャルのパッケージは使わずに Fedora のリポジトリからインストールしました。
sudo dnf install vagrant vagrant-libvirt
ゲストを作る
Vagrantfile を次の内容で作成します。
Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.provider :libvirt do |libvirt| libvirt.driver = "qemu" libvirt.host = "192.0.2.123" libvirt.username = "root" libvirt.connect_via_ssh = true end config.vm.define :sv01 do |sv01| sv01.vm.network :private_network, :ip => "10.99.0.101" sv01.vm.network :forwarded_port, guest: 80, host: 8080 end config.vm.define :sv02 do |sv02| sv02.vm.network :private_network, :ip => "10.99.0.102" sv02.vm.network :public_network, :dev => "br0" end config.vm.provision "shell", inline: <<-SHELL set -eux yum -y install httpd systemctl start httpd SHELL end
ゲストを作成します。
vagrant up --provider=libvirt
ストレージ
デフォルトでは default というストレージプールにディスクイメージが作成されます。普通に libvirtd をセットアップすると /var/lib/libvirt/images
になっていると思います。
ls -1 /var/lib/libvirt/images/ # centos-VAGRANTSLASH-7_vagrant_box_image_2004.01.img # hogehoge_sv01.img # hogehoge_sv02.img
Vagrant Box に含まれる qcow2 のイメージをベースとして差分のディスクイメージが作成されます。
virsh dumpxml hogehoge_sv01 # <disk type='file' device='disk'> # <driver name='qemu' type='qcow2'/> # <source file='/var/lib/libvirt/images/hogehoge_sv01.img'/> # <backingStore type='file' index='1'> # <format type='qcow2'/> # <source file='/var/lib/libvirt/images/centos-VAGRANTSLASH-7_vagrant_box_image_2004.01.img'/> # <backingStore/> # </backingStore> # <target dev='vda' bus='virtio'/> # <alias name='virtio-disk0'/> # </disk>
Vagrantfile の storage_pool_name
で別のストレージプールを指定できます。
virsh pool-create-as --name data --type dir --target /data
# ...Vagrantfile snippet... config.vm.provider :libvirt do |libvirt| libvirt.storage_pool_name = "data" end
この場合も Box イメージは default ストレージプールのみに保存されていました。
ls -1 /var/lib/libvirt/images/ # centos-VAGRANTSLASH-7_vagrant_box_image_2004.01.img ls -1 /data/ # hogehoge_sv01.img # hogehoge_sv02.img
Box イメージはこの指定とは無関係に全てのストレージプールから検索されるようです。見つからなかったときのダウンロードの保存先は指定したストレージプールになります。
試しに default ストレージプールから Box イメージを削除して vagrant up
すると、指定したストレージプールに Box イメージがダウンロードされました。
ls -1 /data/ # centos-VAGRANTSLASH-7_vagrant_box_image_2004.01.img # hogehoge_sv01.img # hogehoge_sv02.img
なお、LVM のストレージプールを指定すると次のエラーになりました。
unexpected exit status 5: Snapshots of snapshots are not supported. (Libvirt::Error)
lvcreate
でボリュームを作ろうとはするようなのですが・・・下記によると LVM は対応してなさそうです。
Management Network
vagrant up
すると vagrant-libvirt
というネットワークが自動的に作成され、ゲストのインタフェースに接続されます。
このインタフェースを通じて Vagrant とゲストの通信や、ゲストから外向き通信の NAT が行われます。
virsh net-dumpxml vagrant-libvirt # <network connections='2' ipv6='yes'> # <name>vagrant-libvirt</name> # <forward mode='nat'> # <nat> # <port start='1024' end='65535'/> # </nat> # </forward> # <bridge name='virbr1' stp='on' delay='0'/> # <ip address='192.168.121.1' netmask='255.255.255.0'> # <dhcp> # <range start='192.168.121.1' end='192.168.121.254'/> # </dhcp> # </ip> # </network> virsh dumpxml hogehoge_sv01 # <interface type='network'> # <source network='vagrant-libvirt' bridge='virbr1'/> # <target dev='vnet2'/> # <model type='virtio'/> # <alias name='net0'/> # </interface>
ネットワーク名やアドレス範囲は変更もできます。
# ...Vagrantfile snippet... config.vm.provider :libvirt do |libvirt| libvirt.management_network_name = "vagrant-management-network" libvirt.management_network_address = "10.255.0.0/24" end
Private Network
private_network
は libvirt のネットワークとして作られます。
# ...Vagrantfile snippet... config.vm.define :sv01 do |sv01| sv01.vm.network :private_network, :ip => "10.99.0.101" end
virsh net-dumpxml hogehoge0 # <network connections='2' ipv6='yes'> # <name>hogehoge0</name> # <forward mode='nat'> # <nat> # <port start='1024' end='65535'/> # </nat> # </forward> # <bridge name='virbr2' stp='on' delay='0'/> # <ip address='10.99.0.1' netmask='255.255.255.0'> # <dhcp> # <range start='10.99.0.1' end='10.99.0.254'/> # </dhcp> # </ip> # </network> virsh dumpxml hogehoge_sv01 # <interface type='network'> # <source network='hogehoge0' bridge='virbr2'/> # <target dev='vnet3'/> # <model type='virtio'/> # <alias name='net1'/> # </interface>
Forwarded Port
forwarded_port
は ssh ポートフォワードを用いて実現されています。
# ...Vagrantfile snippet... config.vm.define :sv01 do |sv01| sv01.vm.network :forwarded_port, guest: 80, host: 8080 end
forwarded_port
が指定された Vagrantfile で vagrant up
するとポートフォワードのための ssh がバックグラウンドで立ち上がります。
このポートフォワードは Vagrant を実行しているローカルの PC から Vagrant のゲストに対して行われます。なのでローカルのポート、Vagrant を WSL で実行しているなら Windows 上のポートからゲストのポートに転送されます。KVM ホストのポートからではありません。
Public Network
public_network
は macvtap なるもので実現されています。macvtap はだいぶ前にちょっと試したことありました。
KVM ホストのブリッジのデバイス名を指定する必要があります。
# ...Vagrantfile snippet... config.vm.define :sv01 do |sv01| sv01.vm.network :public_network, :dev => "br0" end
virsh dumpxml hogehoge_sv02 # <interface type='direct'> # <source dev='br0' mode='bridge'/> # <target dev='macvtap0'/> # <model type='virtio'/> # <alias name='net2'/> # </interface>
デフォルトが eth0
なので、ホストでブリッジを構成しておらず eth0
がそのままIPアドレスを持っているような構成なら :dev
は指定しなくても良いです。
ドメイン名(libvirt のゲスト名)
libvirt のドメイン名(ゲスト名)は、素のままならカレントディレクトリのベース名がプレフィックスに付与されます。
例えば /path/to/hogehoge
で vagrant up
すれば hogehoge_sv01
などとなります。
プレフィックスは default_prefix
で変更できます。また random_hostname
でランダムなサフィックスが付与できます。
# ...Vagrantfile snippet... config.vm.provider :libvirt do |libvirt| libvirt.default_prefix = "oreore-" libvirt.random_hostname = true end
この設定で oreore-sv01_1596269544_a38c163196854065a16f
のようなドメイン名になります。
ディスクイメージも同様に oreore-sv01_1596269544_a38c163196854065a16f.img
のような名前で作成されます。
プライベートネットワークの名前もカレントディレクトリのベース名がプレフィックスに付与されますが(hogehoge0
など)、default_prefix
はプライベートネットワークの名前には影響しません。
プライベートネットワークの名前は個別に指定する必要があります。
# ...Vagrantfile snippet... config.vm.define :sv01 do |sv01| sv01.vm.network :private_network, :ip => "10.99.0.101", :libvirt__network_name => "oreore-private" end config.vm.define :sv01 do |sv02| sv01.vm.network :private_network, :ip => "10.99.0.102", :libvirt__network_name => "oreore-private" end
さいごに
前回試してみた vagrant-aws はちょっとどうかなと思いましたが、vagrant-libvirt はまだメンテされてそうなので、検証用などで KVM にサクッと環境つくりたいときには便利かもしれません。