zaki work log

作業ログやら生活ログやらなんやら

[Proxmox VE] AnsibleプレイブックでPVEのVM作成やクローンをより効率化する (community.general)

AnsibleのCommunity.GeneralコレクションにはProxmoxVEを操作するためにモジュールがいくつか含まれており、これを使ってVMを作ったり、作成済みテンプレートからクローンしたりできる。
特にクラウドイメージを使ったVMのクローンにおける追加パッケージインストールなどのPVEがUIでサポートしてないカスタムcloud-Initの機能は、Ansibleでvendor設定ファイルを作成・配置・設定して全自動で処理できるので、よく使う機能をパラメタ設定できるようにしておけば、VM作成をもっと効率的にできるようになる。(例えばAWXやAAPのUIを使う、とかね)

前置きはこれくらいにして、主要なVM操作のプレイブック例についてまとめ

環境

  • PVEバージョン: 8.3.3
  • Ansible Core 2.18.2 (Ansible 11.2.0)
  • Python
    • proxmoxer 2.2.0
    • requests 2.32.3
  • Collection
    • community.general 10.3.0

AnsibleはPVEとは別ホストで実行。

準備

Proxmox操作のモジュールはproxmoxerrequestsに依存するのでAnsible実行ホストでpipで入れておく。

接続方法

Playのターゲットはlocalhostにして、各タスクでモジュールのパラメタのapi_hostapi_userで接続情報を設定するのがシンプル。
この場合実行環境がlocalhostになるので、必要な依存モジュール(proxmoxerなど)はローカルに入れれば良い。(リモートで実行する場合は、リモートに依存モジュールが必要)

api_hostなどの接続・認証情報については、10.3.0時点のドキュメントには記載がないが、PROXMOX_PASSWORDPROXMOX_PORT以外にもPROXMOX_HOSTPROXMOX_USERも使用できるので、基本的にはプレイブックでなく環境変数でセットした方が機密情報をコードに書かずに済むので個人的にはお勧め。

ただし現時点では、トークン系の環境変数指定は未対応の模様。

        api_host=dict(type='str',
                      required=True,
                      fallback=(env_fallback, ['PROXMOX_HOST'])
                      ),
        api_port=dict(type='int',
                      fallback=(env_fallback, ['PROXMOX_PORT'])
                      ),
        api_user=dict(type='str',
                      required=True,
                      fallback=(env_fallback, ['PROXMOX_USER'])
                      ),
        api_password=dict(type='str',
                          no_log=True,
                          fallback=(env_fallback, ['PROXMOX_PASSWORD'])
                          ),

# https://github.com/ansible-collections/community.general/blob/10.3.0/plugins/module_utils/proxmox.py#L27-L53

なお、PVEのユーザーアカウント(初期構築時の管理者ユーザーであればroot)の場合、ここで指定する文字列はRealmを加えてroot@pamになるので注意。

proxmoxer.github.io

作成関連

VMの作成

docs.ansible.com

素のVMを作る機会はもうあまりないけど、まずは基本。

- name: create VM
  community.general.proxmox_kvm:
    name: "{{ vm_name }}"
    vmid: "{{ vm_id }}"
    node: "{{ pve_node }}"
    cpu: x86-64-v2-AES
    sockets: 2
    cores: 2
    memory: 8192
    net:
      net0: 'virtio,bridge=vmbr0'
      net1: 'virtio,bridge=vmbr1,firewall=1'
      net2: 'e1000,bridge=vmbr2'
    sata:
      sata0: 'local-lvm:10'
    ide:
      ide2: 'media=cdrom,pecorino-dev:iso/ubuntu-22.04.1-live-server-amd64.iso'

個人的によく使うと思う基本的なパラメタは以下の通り。

パラメタ 意味
name VM
vmid VMのID
node (クラスタ組んでるときの)VMを作るPVEノード名
cpu CPU種別
sockets CPUソケット数
cores CPUコア数
memory メモリサイズ
  • NIC(net)について
    • 1つ目は net0、2つ目はnet1でサブキーを作成
    • 値はカンマ区切りでモデル、ブリッジを指定
      • firewallも使用するなら、,firewall=1をさらに追加
      • 書式は手動でNICを追加するときに表示される内容を記載すればOK
  • ストレージ(sata)について
    • 1つ目は sata0、2つ目はsata1でサブキーを作成
    • 値はコロン区切りで、ストレージ名、ディスクサイズ(GB)
    • 前述の記述例は、PVE構築時にデフォルトで作成されるはずのlocal-lvmに10GBのディスクをセット
  • CD/DVD(OSインストール用isoファイルセット)について
    • ide.ide2を使う
      • なぜ2なのかはGUIVM作成したときに使われるから(詳細は未確認)
    • 値はカンマ区切りでmedia=cdromに、あとは「ISOファイルを置いているストレージ名:ISOファイルのパス」

テンプレートからVMのクローンとVM設定

本題。

クローン

使うモジュールはVM作成と同じくcommunity.general.proxmox_kvmモジュール。
ただしVM作成と異なり、VMスペック系のパラメタはクローン時に指定していても無効になるため、クローン後に別途更新する必要がある。
まずはVMのクローンは以下。

- name: create VM from template
  community.general.proxmox_kvm:
    clone: "{{ template }}"
    name: "{{ vm_name }}"
    newid: "{{ vm_id }}"
    node: "{{ pve_node }}"
    full: false
パラメタ 意味
clone クローン元のテンプレート/VM
name クローン先のVM
newid クローン先のVMのID
node (クラスタ組んでるときの)VMを作るPVEノード名

クローンできたら、次はVMの設定更新。
ただしNIC等の更新は現バージョンだとupdate_unsafe: trueをセットしないと受け付けない仕様になっており、名前的にちょっと不安感あるため、NICとストレージは別モジュールを使って別途実行する。

NICの更新

docs.ansible.com

まずはNICの更新。
例としてNIC0とNIC1をセット。
使うのはcommunity.general.proxmox_nicモジュール。

- name: set NIC0
  community.general.proxmox_nic:
    vmid: "{{ vm_id }}"
    model: e1000
    interface: net0
    bridge: vmbr0

- name: set NIC1
  community.general.proxmox_nic:
    vmid: "{{ vm_id }}"
    interface: net1
    bridge: vmbr1

modelを省略した場合はデフォルトのvirtioが使用される。
テンプレートの方にNICを定義してあり、クローンした時点で変更が不要の場合は、これらのタスクは定義しなくても良い。
また、NIC0のみテンプレートに定義があり、NIC1のみ追加したい場合は、NIC1の定義のみ記述すればOK

ストレージサイズの設定

docs.ansible.com

次にストレージサイズの設定。
使うのはcommunity.general.proxmox_diskモジュール。

- name: update disk size
  community.general.proxmox_disk:
    vmid: "{{ vm_id }}"
    disk: scsi0
    size: 30G
    state: resized

GUIからだと「増分」の指定になるが、Ansibleからだと指定サイズへ固定設定ができる。
この例だと、「30GBのディスク」としてセットされる。
ただし使用として増加しかできずに縮小はできないため、クローンした時点のディスクサイズより小さいサイズは指定できない(エラーになる)ので注意。 GUIと同様に「増分」を指定したい場合は、例えば+5GBであれば+5と記述すればOK (その場合再実行の際に5GBずつ増加するのでやはり注意)

VMとcloud-Init設定

使うのはVM作成やクローンと同じくcommunity.general.proxmox_kvmモジュール。
ただしパラメタにupdateを指定する。

- name: update VM configuration
  community.general.proxmox_kvm:
    update: true
    vmid: "{{ vm_id }}"
    node: "{{ pve_node }}"
    cpu: x86-64-v4
    sockets: 2
    cores: 4
    memory: 8192
    tags:
    - dev
    - infra
    ## 以降はcloud-init設定
    ciuser: cloud-user
    cipassword: curry_tabetai
    sshkeys: 'ssh-...... ............'
    ipconfig:
      ipconfig0: 'ip=192.168.0.87/24,gw=192.168.0.1'
      ipconfig1: 'ip=172.16.0.87/24'
      # ipconfig1: ip=dhcp
    nameservers:
    - 192.168.0.4
    - 8.8.8.8
    cicustom: "vendor=pecorino-dev:snippets/{{ vm_id }}.yaml"
パラメタ 意味
update VM作成でなく更新時はtrue指定
vmid 更新対象VMのID
node (クラスタ組んでるときの)VMを作るPVEノード名
cpu CPU種別
sockets CPUソケット数
cores CPUコア数
memory メモリサイズ
tags タグ(list)

cloud-initに関するパラメタは以下。
内容は基本的にGUIで指定するときと同じ値を指定するので特に問題ないと思う。(雑)

パラメタ 意味
ciuser ユーザー名
cipassword パスワード
sshkeys SSH公開鍵文字列
ipconfig IPアドレス設定
nameservers DNS設定
cicustom カスタムvendor設定ファイル

cicustomについては[Proxmox VE] Cloud-Initのvendorデータを使ってサブスクリプション済みRHELのVMをサクッと作成するでも触れたが、vendor=ストレージ名:snippets/vendoer設定ファイル名の書式。

verndor設定ファイルをテンプレートからのVMクローンの一連の流れに組み込むのであれば、PVEサーバー(あるいは別のNFSサーバーとか)で使えるストレージに対してLinuxサーバー自動化で普段使っているcopyモジュールで事前にコピーしてやればOK
以下はAnsible実行環境の~/.ssh/configに接続情報が定義されてる前提で、pve01でアクセスできるPVEサーバーへ定義ファイルを作成する例。
サーバー構成的にはスニペット対応のNFSサーバーの領域を/mnt/pve/pecorino-devにマウントしており、タスク定義はdelegate_toを使用し、これまでの他タスクはローカルホスト実行しながら、このタスク単体はpve01で処理するように定義している。

- name: create snippets file
  ansible.builtin.copy:
    content: |
      #cloud-config

      packages:
      - qemu-guest-agent
      - podman
    dest: "/mnt/pve/pecorino-dev/snippets/{{ vm_id }}.yaml"
  delegate_to: pve01

これを応用すれば、PVEのGUIで使えるcloud-init設定以外のカスタマイズもAnsibleでパラメタ化してやれば、効率的にVMを生やせる。

ちなみに、NIC設定をproxmox_kvmへ組み込むなら以下の通り。

    net:
      net0: 'virtio,bridge=vmbr0'
      net1: 'virtio,bridge=vmbr1'
    update_unsafe: true

その他

VM IDの取得

VM情報参照関連はcommunity.general.proxmox_vm_infoモジュールを使う。
VMのID一覧を列挙するには以下。
(IDでなく全情報を出力したければフィルタしなければOK)

  - name: list VM
    community.general.proxmox_vm_info:
      node: "{{ pve_node }}"
    register: vm_list

  - debug:
      msg: "{{ vm_list.proxmox_vms | map(attribute='vmid') }}"

指定VM(のID)の情報を得るには以下。

- name: vm info
  community.general.proxmox_vm_info:
    node: "{{ pve_node }}"
    vmid: "{{ vmid }}"
    # network: true
  register: vm_info

- debug:
    var: vm_info

NICの情報は現状得ることができないが、network: trueを指定かつ、VMが起動しておりqemu-guest-agentが動作していれば、NICというよりはIPアドレス情報が得られる。
GUIのサマリ画面のIP情報とほぼ同等。

電源管理

シャットダウンする

- name: stop instance
  community.general.proxmox_kvm:
    vmid: "{{ vmid }}"
    state: stopped

ブートする

- name: start instance
  community.general.proxmox_kvm:
    vmid: "{{ vmid }}"
    state: started

state: restartedにするとリブートするが、停止状態から起動はしない。(running状態からの場合にリブートする)

VMの削除

- name: remove instance
  community.general.proxmox_kvm:
    vmid: "{{ vmid }}"
    state: absent

参考

モジュールの使い方についてはAnsibleのドキュメントに加えて、内部で使用しているproxmoxerのドキュメントも参照するのも良い。

proxmoxer.github.io