Proxmox VEでIPAM+cloud-initを用いてIP管理

# はじめに
Proxmoxを使っている皆さん、IP管理はどのようにしていますか？

一般的にはDHCPサーバーに一任したり、起動後に固定したりすることが多いと思います。
私の環境ではVMを作成後、DHCPサーバーからIPを受け取った後、itamaeという構成管理ツール(ansibleのようなもの)を用いてIPを固定していました。しかし、VMを作るたびにitamaeを当てるのも大変ですし、私の環境ではVMの寿命が短いことが多いため、わざわざitamaeを当てるほどでもないことも多いです。

そこで、ProxmoxのIPAMを用いてIP管理を行いました。また、DHCPサーバーから受け取ったIPを用いてIPを自動で固定するようにcloud-initの設定を行いました。IPAM+cloud-initを用いることでVMを作成すると同時にDHCPサーバーから受け取った重複のないIPアドレスで固定されたマシンを作成することができます。
なお当然ですが、IPAMを用いずにDHCPサーバーから受け取ったIPで固定しているとリース切れでIPが重複する自体が起こります。

# IPAMの設定
Proxmox VE 8.1からIPAMの設定がWeb UIからできるようになっています。ただし、サポートしていない設定もあるためターミナル上で直接ファイルを編集する必要がある場合はあります。

## インストール
DHCP機能を用いるためには、dnsmasqを追加インストールする必要があります。
さらに、デフォルトのdnsmasqサービスを無効にする必要があります。
Proxmoxクラスタに属する全ノードに対し以下のコマンドを実行してください。
```bash
apt install dnsmasq
systemctl disable --now dnsmasq
```

## VNetの作成
### Simple Zoneの作成
続いて、IPAMに使用するinterfaceを作成します。
`Datacenter > SDN > Zones` で、新しいSimple Zoneを作成してください。DHCPを有効にする場合は `Advenced` にチェックを入れ、 `automatic DHCP` オプションにチェックを入れてください。IPAMにはデフォルトで存在する `pve` を選択します。

![zone.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/ff6e7e1d-c79d-3ef4-2be4-642cda1d5ce5.png)

### VNetの作成
VNetパネルに移動し、任意のID（この例ではvnet0）で新しいVNetを作成します。VNetを作成する際、前のステップで作成したゾーンを選択します。
![vnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/93af922d-0d7f-8237-ca34-e31fbbf7d027.png)

### Subnetの作成
同じビューで、先に作成したVNetにSubnetを作成します。VNetを選択し、右側のSubnetsで「Create」をクリックします。任意のローカル・サブネット（この例では192.168.240.0/24）を入力します。ゲートウェイ用のIPも定義する必要があります。そうしないとDHCPが機能しません（この例では192.168.240.1）。このVNetでSNATを有効にするには、「SNAT」オプションにチェックを入れます。今回は利用しないためチェックは入れていません。

![subnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/6e09457c-5c12-2aea-7bfd-da242ca9bc3f.png)

また、DHCPを使用するには、このsubnetのDHCP rangeを作成する必要があります。「DHCP Ranges」タブに切り替え、Start AddressとEnd Addressを入力します。今回は192.168.240.10と192.168.240.190を選びました。

![dhcp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/02a4e6b1-bfc1-b84b-235b-2498fe2ea4f6.png)

### apply
以上でVNetの作成は完了です。SDNパネルに移動してApplyをクリックすると、適用できます。ここで。エラーや警告なしに完了すれば、新しく作成した VNet を使用できます。

しかし、私の環境では 
`missing 'source /etc/network/interfaces.d/sdn' directive for SDN support!`
というwarningが表示されました。これは `/etc/network/interfaces.d/sdn` の設定が読み込まれていないことが原因でしたので、読み込まれるように `/etc/network/interfaces` に `source /etc/network/interfaces.d/*` を追記し、再度applyしたところ正常に完了しました。

### 物理NICに紐づけ
以上でVNetの作成はできています。VNetの設定は `/etc/network/interfaces.d/sdn` にあります。
先程の設定を行うと
```bash
#version:13

auto vnet0
iface vnet0
        address 192.168.240.3/24
        bridge_ports none
        bridge_stp off
        bridge_fd 0
        ip-forward on
```
となります。
この設定では `bridge_ports none` となっているため、物理NICに紐づいていません。そのため、ハイパバイザ外のネットワークに接続することができません。
外部ネットワークに接続するには `bridge_ports enp4s0` などとすると物理NICに紐づけることができます。
私の環境ではVLANの設定を行っているため、以下のような設定ファイルになります。
```bash
#version:13

auto vnet0
iface vnet0
        address 192.168.240.3/24
        bridge_ports enp4s0.240
        bridge_stp off
        bridge_fd 0
        ip-forward on
```
以上の設定を行った後 `ifreload -a` すると適用されます。

### 注意
`System > Network` でネットワーク構成を変更し、Apply Configurationをクリックすると `/etc/network/interfaces.d/sdn` の設定が `bridge_ports none` に戻ってしまうようです。

https://pve.proxmox.com/wiki/Software-Defined_Network#pvesdn_config_zone によると、
> Simple Zones
This is the simplest plugin. It will create an isolated VNet bridge. This bridge is not linked to a physical interface, and VM traffic is only local on each the node. It can be used in NAT or routed setups.

とあるので、Simple Zoneは外部ネットワークに接続することは想定されていないようです。VLAN Zonesは現状DHCP機能を用いることができないため、説明には反しますが手動でファイルが書き換えるしか方法はなさそうです。

# cloud-initの作成
続いて、IP固定の設定を行うためにcloud-initを用います。今回はUbuntu-22.04を使用します。
まず以下のコマンドを実行し、必要なパッケージをインストールします。
```bash
apt install libguestfs-tools
```
大部分は[Proxmox VE cloud-init で Ubuntu 20.04 を起動する](https://blog.naa0yama.com/p/33w15-2j8gmw31/)を参考にさせていただいています。

## イメージの修正

Ubuntu-22.04を使用するためダウンロードします。
```bash
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
cp -av jammy-server-cloudimg-amd64.img ubuntu-22.04.img
```

続いて、以下のファイルを作成します。
```yaml:11_template.cfg 
#cloud-config

write_files:
  - path: /etc/systemd/network/enp0s18.network
    content: |
      [Match]
      Name=enp0s18

      [Network]
      Address=$DHCP_IP/24
      Gateway=192.168.240.1
      DNS=192.168.220.53

runcmd:
  - dhclient
  - DHCP_IP=$(ip addr show enp0s18 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
  - sed -i "s/\$DHCP_IP/$DHCP_IP/" /etc/systemd/network/enp0s18.network
  - systemctl restart systemd-networkd
  - systemctl enable systemd-networkd
```

行っていることは以下のとおりです。
- `write_files`で `/etc/systemd/network/enp0s18.network` にcontentの内容のファイルを作成する
- `dhclient` でDHCPサーバーからIPアドレスを取得する
- `DHCP_IP=$(ip addr show enp0s18 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')` で `enp0s18` についたIPv4アドレスを取得する
- `sed -i "s/\$DHCP_IP/$DHCP_IP/" /etc/systemd/network/enp0s18.network` で `/etc/systemd/network/enp0s18.network` の `$DHCP` という文字列を先ほど設定した `DHCP_IP` に置き換える
- `systemctl restart systemd-networkd` と `systemctl enable systemd-networkd` で `systemd-networkd` を起動、有効化する

続いて、作成したファイルをイメージに適用します。
```bash
virt-copy-in -a ubuntu-22.04.img 11_template.cfg /etc/cloud/cloud.cfg.d
```

## テンプレート用VMの作成
では、テンプレートとするVMを作成します。
最初に以下のように変数を設定します。 `PVE_VOLUME` は各自で変更してください。
```bash
 export PVE_TMP_VMID=9000
 export PVE_VOLUME=local
```
以下のコマンドでVMを作成します。
```bash
 qm create ${PVE_TMP_VMID} --memory 2048 --net0 virtio,bridge=vnet0
 qm importdisk ${PVE_TMP_VMID} ubuntu-22.04.img ${PVE_VOLUME}  --format qcow2
 qm set ${PVE_TMP_VMID} --scsihw virtio-scsi-pci --scsi0 ${PVE_VOLUME}:${PVE_TMP_VMID}/vm-${PVE_TMP_VMID}-disk-0.qcow2
 qm resize ${PVE_TMP_VMID} scsi0 15G
 qm set ${PVE_TMP_VMID} --ide2 ${PVE_VOLUME}:cloudinit
 qm set ${PVE_TMP_VMID} --boot c --bootdisk scsi0
 qm set ${PVE_TMP_VMID} --serial0 socket --vga serial0
 qm set ${PVE_TMP_VMID} --ostype l26
 qm set ${PVE_TMP_VMID} --agent enabled=1,fstrim_cloned_disks=1
 qm template ${PVE_TMP_VMID}
```
## 確認
以上で `VM 9000` が作成され、`Datacenter > SDN > IPAM` を見ると、先ほど作成したDHCPサーバーにより `VM 9000` に `192.168.240.10` が振られているのが確認できます。

![ipam.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/a598b470-387e-2034-7075-28f0487b1718.png)

# テンプレートを用いたVMの作成
では先程作成したテンプレートを用いて新しいVMを作成します。
Datacenter上の `VM 9000` を右クリックし、Cloneを選択します。
Modeは `Full Clone` に設定し、Nameは自由に設定してください。

![vm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/b9a3ad55-31bd-9d19-f491-2302dcda596a.png)

作成したVMを見てみると、無事IPアドレスを割り当てられ、IPアドレスが固定されていることが確認できます！

![ip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/394c6de4-a154-852b-984f-4b2300f8a78f.png)

`Datacenter > SDN > IPAM` を見ると、割り当てられたIPアドレスを確認することができます。
以下の図では3つのVMを作成した時の様子です。DHCPサーバーはIPAMを参照し、空いているIPを割り当てます。

![ipam.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3245951/343d5d0a-b16a-5899-0308-4cf667dcec9f.png)

# おわりに
IPAMを使うことでIPアドレス管理から開放されました！
Proxmoxがbuilt-inのIPAMをもっているのはさすがの高性能でした。便利です。

# 参考
- [Setup Simple Zone With SNAT and DHCP](https://pve.proxmox.com/wiki/Setup_Simple_Zone_With_SNAT_and_DHCP)
- [Proxmox 7.2 SDN Creation Issue](https://forum.proxmox.com/threads/proxmox-7-2-sdn-creation-issue.112137/)
- [Proxmox VE cloud-init で Ubuntu 20.04 を起動する](https://blog.naa0yama.com/p/33w15-2j8gmw31/)