Ansibleほど大規模にVMを構築する気はないけど、OSのインストールは対話的にでなく自動で行いたい、という場合はcloud-initが選択肢に入るらしい。ちょっと試してみましょう。
セットアップ
今回はUbuntu20.04(x86_64)でやってみました。
$ sudo apt install qemu-kvm libvirt-daemon-system virtinst
$ sudo adduser $USER libvirt
virt-installでインストール
ゲストOSがUbuntuの場合、公式docに従えばインストールできてしまいます。
ubuntu.com
ただそれだと味気ないので、virt-installを使用してインストールにトライしてみます。
$ sudo mount -r ~/Downloads/ubuntu-20.04-live-server-amd64.iso /mnt
$ mkdir -p ~/www
cd ~/www
cat > user-data << 'EOF'
#cloud-config
autoinstall:
version: 1
identity:
hostname: ubuntu-server
password: "$6$exDY1mhS4KUYCE/2$zmn9ToZwTKLhCw.b4/b.ZRTIZM30JZ4QrOQ2aOXJ8yk96xpcCof0kxKwuX1kqLG/ygbJ1f8wxED22bTL4F46P0"
username: ubuntu
EOF
touch meta-data
$ python3 -m http.server 3003
$ truncate -s 10G image.img
$ cd /mnt
$ virt-install \
--name=test \
--os-variant=ubuntu20.04 \
--memory=2048 \
--vcpus=1 \
--network network=default \
--disk=<image.imgへのパス>,cache=none,format=raw,bus=virtio \
--location=<ubuntu-20.04.4-live-server-amd64.isoへのパス>,kernel=casper/vmlinuz,initrd=casper/initrd \
--extra-args "autoinstall ds=nocloud-net;s=http://_gateway:3003/" \
--noreboot
ちなみに--locationオプションのkernel, intridが/mnt/casper/vmlinuzや/mnt/casper/initrdだと以下のエラーが出たので、上記のように/mntに移動して実行しました。kvmコマンドの場合はフルパスでアクセスできたんですが...
Starting install...
ERROR Couldn't find kernel for install tree.
Domain installation does not appear to have been successful.
If it was, you can restart your domain by running:
virsh --connect qemu:///system start test
otherwise, please restart your installation.
さらに補足ですが、passwordは公式docにならって「ubuntu」のハッシュ値としています。実際に使用する際は変更するべきでしょう。ハッシュ生成は「Linux パスワード ハッシュ」などで検索していただくと、SHA-512でハッシュ値計算するワンライナーがひっかかると思います。
Python+libvirtでインストール
ついでにPythonでインストールする場合も試行してみました。
インストールに必要なパラメータを記載したXMLが必要ですが、自分で書くとそれなりに大変です。virt-installのオプションを使って生成・流用するのがよいでしょう。
ただし、virt-installの時とは異なり、Python+libvirtでは、/mnt配下のvmlinuzやinitrdを参照しようとすると読み取り専用で開けないと怒られます。なにか適切な設定があるのかもしれませんが、今回はとりあえず適当な場所にコピーしておきます。
$ mkdir casper
$ cp /mnt/casper/initrd ./casper/
$ cp /mnt/casper/vmlinuz ./casper/
$ truncate -s 10G xmlimage.img
$ virt-install \
--name=xmltest \
--os-variant=ubuntu20.04 \
--memory=2048 \
--vcpus=1 \
--network network=default \
--disk=<xmlimage.imgへのパス>,cache=none,format=raw,bus=virtio \
--location=<ubuntu-20.04.4-live-server-amd64.isoへのパス>,kernel=casper/vmlinuz,initrd=casper/initrd \
--extra-args "autoinstall ds=nocloud-net;s=http://_gateway:3003/" \
--noreboot \
--print-xml \
--dry-run
これで標準出力にXMLが出力されるのでコピーすればよいのですが、<domain type="kvm"> ... </domain>と囲まれたものが2つ出力されているかと思います。ここで使用するのは、先に出力された方です。後のものはdefine時に使用します。これも別でどこかに控えておいてください。
コピーしたXMLは、以下のkernelタグ、initrdタグの内容を、先程コピーした先のものに変更する必要があります。元はvirt-install実行時に/var/lib/libvirt/boot/配下に作成した一時的なコピーを参照しているようで、ここの記載を変更しないと「そんなファイルはない」と怒られます。
<os>
<type arch="x86_64" machine="q35">hvm</type>
<kernel>コピー先/casper/vmlinuz</kernel>
<initrd>コピー先/casper/initrd</initrd>
<cmdline>autoinstall ds=nocloud-net;s=http://_gateway:3003/</cmdline>
</os>
PythonでOSインストールするには、libvirtのPythonバインディングされたAPIがあるため、これを叩くことになります。なお本記事投稿時点のバージョンは6.1.0でした。
$ sudo apt install python3-libvirt
XMLはファイルとして読み込んでもいいですし、ちょっと試すだけならPythonに直書きでもOKです。とりあえず今回は後者で。
import sys
import libvirt
xmlcreate = """
XMLをここにペースト
"""
conn = libvirt.open('qemu:///system')
if conn == None:
print('[ERROR]open connection to qemu:///system')
exit(1)
dom_create = conn.createXML(xmlcreate, 0)
if dom_create == None:
print('[ERROR]createXML')
exit(1)
print('all success:'+dom_create.name())
conn.close()
exit(0)
バックエンドで動作を始めるので、インストールしている様子を見届けたければvirt-viewerをGUI環境で実行するなどします。
$ python3 install.py
$ virt-viewer
VMをdefineする
defineしていないので、virsh list --allなどしてもインストールしたVMが見えません。次のPythonスクリプトでdefineすることでlibvirtの管理が可能になり、virshでも表示されます。先程のvirt-installであとに出ていた方のXMLをペーストします。こちらは改変する必要は特にありません。
import sys
import libvirt
xmldefine = """
XMLをここにペースト
"""
conn = libvirt.open('qemu:///system')
if conn == None:
print('[ERROR]open connection to qemu:///system')
exit(1)
dom_define = conn.defineXML(xmldefine)
if dom_define == None:
print('[ERROR]defineXML')
exit(1)
print('all success:'+dom_define.name())
conn.close()
exit(0)
スクリプト実行はインストールが完了した後にしたほうがよいでしょう。実行後はvirshでdefineされているかの確認、virsh startとvirt-viewerで動作確認ができます。virt-viewer上でVMの操作が可能なので、pingなど試してみてもいいかもしれませんね。
$ python3 define.py
$ virsh list --all
Id Name State
--------------------------
- xmltest shut off
$ virsh start xmltest
Domain xmltest started
$ virt-viewer
今回はこれまで。