Ansibleほど大規模にVMを構築する気はないけど、OSのインストールは対話的にでなく自動で行いたい、という場合はcloud-initが選択肢に入るらしい。ちょっと試してみましょう。
セットアップ
今回はUbuntu20.04(x86_64)でやってみました。
$ sudo apt install qemu-kvm libvirt-daemon-system virtinst # libvirtグループにユーザを追加する # (apt install時に追加されているはずだが念の為) $ sudo adduser $USER libvirt
virt-installでインストール
ゲストOSがUbuntuの場合、公式docに従えばインストールできてしまいます。
ただそれだと味気ないので、virt-installを使用してインストールにトライしてみます。
# 前半は公式docのProviding the autoinstall data over the networkと同じ $ 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を参照しようとすると読み取り専用で開けないと怒られます。なにか適切な設定があるのかもしれませんが、今回はとりあえず適当な場所にコピーしておきます。
# /home以下のような任意の場所にvmlinuzとinitrdをコピーしておく $ mkdir casper $ cp /mnt/casper/initrd ./casper/ $ cp /mnt/casper/vmlinuz ./casper/ # virt-installで実際にインストールをせずXMLを表示するには # --print-xmlと--dry-runオプションを付加する $ 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) # defineしない一時的なVMを作成する # (.imgにはインストールされているがlibvirt管理下にない状態になる) 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) # VMをdefineする 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
今回はこれまで。