趣味の棚

自分の興味のあることを好きなだけ

【Ansible】KVMのダイナミックインベントリを使う

はじめに

Linux仮想マシンを構築できるKVM。最近はVMwareのあれこれで注目度も上がっている気がする。
そんなKVMにも、Ansibleのダイナミックインベントリ用プラグイン公開されている。

今回はそのプラグインを使ってKVMVMをダイナミックインベントリで読み込み接続するまでをやる。

私がKVMのダイナミックインベントリを使えるようにするまでに経験したエラーとその対処方は最後に説明する。

VMの構築

KVMでのVM構築はXMLのテンプレートを用意すればどんな設定でも可能だと思う。今回はそこらへんが躓きポイントとならないようになるべく平易な構築を行いたい。
と言うことで今回はvirt-installコマンドを使ってVMを構築する。
構築に使用したコマンドは以下。

virt-install --virt-type kvm --location /path/iso_file --os-type linux --os-variant rhel9.0 --ram 4086 --vcpus 4 --disk /path/kvm/images/ansible_target.qcow2,size=20 --name ansible_target --network bridge=br0 --hvm --noautoconsole --initrd-inject /path/rhel_kickstart.cfg --extra-args "inst.ks=file:/rhel_kickstart.cfg ksdevice=enp1s0 onboot=yes"

インストール作業が手間だったためkickstartを使用しているが特別な設定は行っていない。

kickstartで使用したファイルも一応記載しておく。<クリックで展開>(適当にインストールしたRHELから引っ張ったものなので最適化されていないが)

# Generated by Anaconda 34.25.4.9
# Generated by pykickstart v3.32
#version=RHEL9
text

%addon com_redhat_kdump --enable --reserve-mb='auto'

%end

# Keyboard layouts
keyboard --xlayouts='us'
# System language
lang en_US.UTF-8

%packages
@^server-product-environment

%end

# Run the Setup Agent on first boot
firstboot --enable

# Generated using Blivet version 3.6.0
ignoredisk --only-use=vda
autopart
# Partition clearing information
clearpart --none --initlabel

# System timezone
timezone Asia/Tokyo --utc

# Root password
rootpw --iscrypted hoge
user --groups=wheel --name=holtit --password=fuga --iscrypted --gecos="holtit"


firewall --disabled

selinux --disabled

reboot

VM側の手順

VMが作成できたので必要な設定を行う。
まずはゲストエージェントを入れる。こいつを入れることで外部からコマンド叩いてゲストOSの情報を取得したりできるようになる。

qemu-guest-agentのインストール

エージェントのインストールを行う。
RHEL系の場合
dnf -y install qemu-guest-agent
Debian系の場合
apt -y install qemu-guest-agent
パッケージ名が同じなので分かりやすい。

エージェントの起動

qemu-guest-agentはサービスとして動作するため起動する。

systemctl start qemu-guest-agent

無事起動できればOK。

systemctl status qemu-guest-agent
● qemu-guest-agent.service - QEMU Guest Agent
     Loaded: loaded (/usr/lib/systemd/system/qemu-guest-agent.service; enabled; preset: enabled)
     Active: active (running) since Tue 2024-11-05 21:56:02 JST; 18min ago
   Main PID: 4894 (qemu-ga)
      Tasks: 2 (limit: 23084)
     Memory: 3.3M
        CPU: 332ms
     CGroup: /system.slice/qemu-guest-agent.service

Ansible側の設定、準備

KVM側の設定が終わったのでようやくAnsibleの話。

community.libvirtのインストール

KVMVMをダイナミックインベントリで扱うためにはcommunity.libvirt.libvirtを使用する。
そのため環境にcommunity.libvirtが入っていない場合はインストールする必要がある。

ansible-galaxy collection install community.libvirt

インストール完了or既にしてある場合はちゃんとインストールができているか確認する。

ansible-galaxy collection list | grep libvirt
community.libvirt                        1.3.0  

ちゃんとできてる。

ライブラリ等のインストール

正直全部を書くことは面倒難しいので私の環境で追加で入れたものだけ列挙する。

# Pythonライブラリ
libvirt-python
# その他パッケージ
libvirt-dev
pkg-config

ダイナミックインベントリの作成

いよいよダイナミックインベントリを作成する。
と言っても内容はこれだけ。

---
plugin: community.libvirt.libvirt
uri: 'qemu+ssh://holtit@kvm-server/system'

今回、Ansibleを実行するマシンとKVMを動かしているマシンは別なので、uriでは上記のような指定になっている。
では読み込めるか見てみよう。 インベントリの内容を確認するには以下のコマンドを実行。

ansible-inventory -i inventory.yml --list

出力が長すぎるので一部割愛する。

            "ansible_target": {
                "ansible_connection": "community.libvirt.libvirt_qemu",
                "ansible_libvirt_uri": "qemu+ssh://holtit@home-server/system",
                "guest_info": {
                    "os.id": "rhel",
                    "os.name": "Red Hat Enterprise Linux",
                    "os.pretty-name": "Red Hat Enterprise Linux 9.4 (Plow)",
                    "os.version": "9.4 (Plow)",
                    "os.version-id": "9.4",
                },
                "info": {
                    "cpuTime_ns": 37860000000,
                    "maxMem_kb": 4184064,
                    "memory_kb": 4184064,
                    "nrVirtCpu": 4,
                    "state": "running",
                    "state_number": 1
                },
                "interface_addresses": {
                    "enp1s0": {
                        "addrs": [
                            {
                                "addr": "192.168.50.34",
                                "prefix": 24,
                                "type": 0
                            },

ホスト名やIPアドレスなどが取得できていることが分かる。
あとはこれをインベントリとして使用するだけ。

ダイナミックインベントリを使用したAnsible接続

ansible -i inventory.yml ansible_target -m ping
ansible_target | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.9"
    },
    "changed": false,
    "ping": "pong"
}

はい成功。

スムーズにできたように書いたが苦戦しまくって2回ぐらい諦めた。
以下に私の環境で発生し、そして解決できたエラーを記載する。

躓いたエラー集

qemu-guest-agentサービスが起動できないとき

現段階ではゲストOS側から一方的に接続を試みている段階。ホストOSと接続するための経路を用意してあげる必要がある。

具体的な手順としては以下のブロックをVMに追加する。

<channel type='unix'>
  <target type='virtio' name='org.qemu.guest_agent.0'/>
  <address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>

IDは自身の環境に合わせてほしい。

上記の設定が終わったらVMを再起動する。
私の環境ではVM内から発行するrebootコマンドではダメで、一度VMを完全に停止させる必要がありそう。

GUIからの確認で申し訳ないが、上記の設定がうまくいくとchannel欄が新たに追加される。

VMを起動すると以下の行が追加されるはず。

<target> type="virtio" name="org.qemu.guest_agent.0" state="connected"/>

connectedになってればよさげ。

再起動が終わったら再びqemu-guest-agentを起動しよう。

systemctl start qemu-guest-agent
systemctl status qemu-guest-agent
* qemu-guest-agent.service - QEMU Guest Agent
     Loaded: loaded (/lib/systemd/system/qemu-guest-agent.service; static)
     Active: active (running) since Tue 2024-11-05 20:23:57 JST; 12min ago
   Main PID: 492 (qemu-ga)
      Tasks: 2 (limit: 2293)
     Memory: 10.6M
        CPU: 153ms
     CGroup: /system.slice/qemu-guest-agent.service
             `-492 /usr/sbin/qemu-ga

無事起動できた。

Ansibleで接続した時Domain does not have required capabilitiesになるとき

ansible-inventory -i inventory.yml --listではVMの情報が表示できるのにいざansible -i inventory.yml ansible_target -m pingなどで接続すると失敗するケースがある。
エラー文は以下。

ansible -i kvm_inventory/inventory.yml ansible_target -m ping
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
ansible_target | UNREACHABLE! => {
    "changed": false,
    "msg": "Domain does not have required capabilities",
    "unreachable": true
}

解決法と言うかトラシューのやり方はターミナルに書いてある通り。
詳細な出力をさせてみる。(長いので抜粋)

ansible -i kvm_inventory/inventory.yml ansible_target -m ping -vvv

<ansible_target> REQUIRED CAPABILITIES MISSING: ['guest-exec', 'guest-exec-status', 'guest-file-close', 'guest-file-open', 'guest-file-read', 'guest-file-write']

重要なのはREQUIRED CAPABILITIES MISSINGここ。
qemu-guest-agentがサービスとして起動していてもすべての機能が有効になっているわけではない。
Ansibleで接続するためには上記のものが有効になっている必要があるようだ。

と言うことで有効化する。
※私が以後作業するのはRHEL9.4である。Debian系のやり方までは扱わない。
理由としては手元のDebian12はqemu-guest-agentをインストールしサービスを起動しただけでAnsible接続に必要なものがすべて有効化されていたためである。

以下のファイルを編集する。
/etc/sysconfig/qemu-ga
いろいろコメントで書かれているだろうが修正するのは1行だけ。
FILTER_RPC_ARGSの行。 あとはこの行にREQUIRED CAPABILITIES MISSINGで不足していると言われたものを追記するだけ。
私の環境では長々しく以下のようになった。※これで1行

FILTER_RPC_ARGS="--allow-rpcs=guest-sync-delimited,guest-sync,guest-ping,guest-get-time,guest-set-time,guest-info,guest-shutdown,guest-fsfreeze-status,guest-fsfreeze-freeze,guest-fsfreeze-freeze-list,guest-fsfreeze-thaw,guest-fstrim,guest-suspend-disk,guest-suspend-ram,guest-suspend-hybrid,guest-network-get-interfaces,guest-get-vcpus,guest-set-vcpus,guest-get-disks,guest-get-fsinfo,guest-set-user-password,guest-get-memory-blocks,guest-set-memory-blocks,guest-get-memory-block-info,guest-get-host-name,guest-get-users,guest-get-timezone,guest-get-osinfo,guest-get-devices,guest-ssh-get-authorized-keys,guest-ssh-add-authorized-keys,guest-ssh-remove-authorized-keys,guest-get-diskstats,guest-get-cpustats,guest-exec,guest-exec-status,guest-file-close,guest-file-open,guest-file-read,guest-file-write"

あとはサービスを再起動する。

systemctl restart qemu-guest-agent

これで完了。Ansibleから疎通テストしてみる。

ansible -i kvm_inventory/inventory.yml ansible_target -m ping 

ansible_target | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.9"
    },
    "changed": false,
    "ping": "pong"
}

成功!!

終わりに

今回はKVMのダイナミックインベントリを使ってみた。
KVMはそもそも構成要素が分かりづらく、そこにAnsibleやAnsibleのダイナミックインベントリが絡むと更に分からなくなった。 記事内で挙げたエラー2つは私がめちゃくちゃ悩んだものなので誰かの参考になれば幸いである。

これで自宅の仮想環境にKVMを選んだことを肯定する材料が増えた。 とても満足!!