前回の記事では、Synology DS1522+をセットアップ・コンテナをAnsibleで起動できるようにしました。 これだけでも非常に便利ですが、ときにはコンテナだけではなくVMもあると便利です。
幸い、SynologyのNAS製品にはVirtual Machine Managerという仮想マシンを扱うためのアプリケーションを無料で導入することができます。 今回は、この機能を使ってSynology DS1522+に仮想マシンを立てていきます。 後述のように試した範囲では完全にIaCのようにはできませんでしたが、Packerを使ったコード管理されたイメージを利用しているためそれでお茶を濁しています。
TL; DR;
- イメージのビルドをPackerで行うことによってUI上での手動ですがSynologyのNASでVMをデプロイすることができました。
- デプロイもTerraformで行うこともできますが、その場合UI上で現れなくなります。
Virtual Machine Managerのインストール
前回の記事で既に基本的なセットアップは済んでいることは前提としています。
DSM(ブラウザ上で操作できる専用OS)の「パッケージセンター」アプリから、Virtual Machine Managerをインストールします。
インストール自体は簡単でウィザードに従っていくだけです。
Packerによるゴールデンイメージの作成
Virtual Machine Managerで作成したVMにOSをインストールし、その上で様々な設定投入を行うのも可能ですが、我が家の宅鯖ではイミュータブルインフラストラクチャを実践するようにしています。 具体的には、Packerを使ってAnsibleによって設定が投入されたイメージを作成し、そのイメージを旧イメージと入れ替えることで実現します。 詳しくは、以前記事に書いたため、そちらを読んでいただければと思います。
以降では、ビルド方式を問わず、cloud-initによる設定が可能なKVMイメージを利用することを前提とします。
半分IaCによるVMのデプロイ
半分IaCとは?
Synology Virtual Machine Managerでは、内部ではlibvirtを利用しています。 そのため、Terraformのlibvirt providerを利用してVMをデプロイすることが可能です。
$ systemctl status pkg-libvirtd.service
● pkg-libvirtd.service - libvirt daemon
Loaded: loaded (/usr/local/lib/systemd/system/pkg-libvirtd.service; static; vendor preset: disabled)
Active: active (running) since Sun 2024-07-28 14:37:47 KST; 2 months 9 days ago
Main PID: 27114 (libvirtd)
CGroup: /Virtualization.slice/pkg-libvirtd.service
├─20318 /usr/local/bin/qemu-system-x86_64 -name guest=d6ef89a3-a353-453a-957c-2a62ae24a1ef,debug-threads=on -S -object secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-1-d6...
└─27114 /var/packages/Virtualization/target/usr/local/bin/libvirtd -d --listen --config /etc/libvirt/libvirtd.conf --pid-file /var/run/libvirtd.pid
しかし、この方法によるVMのデプロイを試してみたのですが、Virtual Machine ManagerのUI上には現れませんでした。 おそらくですが、libvirtのさらに一つ上のレイヤにVirtual Machine Manager自体の状態を管理するレイヤがあり、UI上ではそこの情報を表示するようになっているようです。
いずれにしても、Terraformのlibvirt providerを使った方法だとUIの管理外となってしまうということです。 完全にTerraformで管理するのでUIはいらない、という割り切りもありだとは思いますが、私はUIでもみられるようにしたかったので、デプロイする部分は手動で行いつつも、可能な限りその範囲を狭めるようにしていきます。 なお、Terraformでデプロイしたいという方もいると思うので、Terraformでデプロイする方法をおまけセクションに書いています。
デプロイ方法
完全なIaCを行うことはやめましたが、それでもある程度はIaCを実践したいです。 そこで、デプロイの際に用いるcloud-initのISOイメージに関してはTerraformで作成していきます。 つまり、デプロイの流れとしては、
- Terraformでcloud-initのISOイメージを作成
- Virtual Machine ManagerのUIでPackerで作成したイメージとcloud-initのISOイメージを使ってVMを作成
というものになります。
Terraformによるcloud-init ISOイメージの作成
まずは、cloud-initのISOイメージをTerraformを用いて作成する部分ですが、以下のようにnull_resourceとlocal-execを用いて、Terraform CLIを実行したホスト上でコマンドを実行しています。 作成に用いているコマンドは、cloud-localdsというもので、userdataやnetworkconfigを指定することでそれらを含むISOイメージを作成してくれます。
resource "null_resource" "cloudinit-iso" {
provisioner "local-exec" {
command = "cloud-localds cloudinit-iso-repository/cloudinit.iso <(echo \"$USERDATA\") -N <(echo \"$NETWORK_CONFIG\")"
interpreter = ["/bin/bash", "-c"]
environment = {
USERDATA = templatefile("userdata.cfg.tftpl",
{
hostname = "vm1",
username = "localadmin",
password = null,
authorized_keys = [],
})
NETWORK_CONFIG = templatefile("network-config.tftpl",
{
ipaddress = "172.16.1.2/24",
})
}
}
provisioner "local-exec" {
when = destroy
command = "rm cloudinit-iso-repository/cloudinit.iso"
}
}
userdataやnetwork-configは適当に作成します。 ちなみにこのuserdataでは、パスワードを指定しないとパスワードなしのsudoが可能・指定するとそのパスワードでのsudoが可能というものになっています。 以前Hashicorp Vault SSH OTPを使ったワンタイムパスワードログインを実装した際のVMを作っているということです。
# userdata.cfg.tftpl
#cloud-config
hostname: ${hostname}
users:
- name: ${username}
%{ if password != null }passwd: ${password}
lock_passwd: false%{ endif }
sudo: %{ if password != null }"ALL=(ALL) ALL"%{ else }"ALL=(ALL) NOPASSWD:ALL"%{ endif }
shell: /bin/bash
%{ if authorized_keys != null }
ssh_authorized_keys:
%{ for authorized_key in authorized_keys ~}
- "${authorized_key}"
%{ endfor }
%{ endif }
# network-config.tftpl
network:
version: 2
renderer: networkd
ethernets:
mainif:
match:
driver: "virt*"
dhcp4: false
dhcp6: true
addresses:
- ${ipaddress}
routes:
- to: default
via: 172.16.1.254
nameservers:
addresses: [172.16.1.201, 172.16.1.200]
ISOイメージが作成される先をNASのNFS共有マウントとしておくことで、この後のVM作成をスムーズに行えます。
VMの作成
既存のイメージからVMを作成するにはインポートを行います。
ストレージの部分ではPackerで作成したイメージを指定します。
この方法で作成したVMでは、Packerイメージビルド時のディスクサイズから変えることができないため注意が必要です。
qemu-img resizeコマンドで変更することはできますが、可能なら作成時に変えられるようにしてほしいところです。
そのまま作成ウィザードを進めていきます。
追加のISOファイルとして、Terraformで作成したcloud-initのISOファイルを指定します。
このようにするとVMが作成されます。
電源を入れて接続してみると、Packerイメージが使われていることがわかります。(Packerイメージはkubernetesノードとして利用することを想定しているためkubeletが入っている)
まとめ
自動化はcloud-initのISOのみではありますが、SynologyのNASにおいてもIaCによるVMのデプロイを実現することができました。 今のところ完全にIaCを綺麗に行う方法を見つけていませんが、Terraform Providerもドキュメント不足ですがあるみたいなので引き続き模索してみようと思います。
おまけ 〜完全IaC〜の模索
Virtual Machine Managerの内部ではlibvirtが動いているため、UI上で管理できなくなることを許容するならばTerraformのKVM providerを利用することで人力を介さない完全なIaCを実現することができます。
$ systemctl status pkg-libvirtd.service
● pkg-libvirtd.service - libvirt daemon
Loaded: loaded (/usr/local/lib/systemd/system/pkg-libvirtd.service; static; vendor preset: disabled)
Active: active (running) since Sat 2024-07-13 09:33:25 KST; 2 days ago
Main PID: 8850 (libvirtd)
CGroup: /Virtualization.slice/pkg-libvirtd.service
└─8850 /var/packages/Virtualization/target/usr/local/bin/libvirtd -d --listen --config /etc/libvirt/libvirtd.conf --pid-file /var/run/libvirtd.pid
ただし、libvirtへの接続方式としてSSHを利用する方式だと、NASのOS内に必要なコマンドがないため接続することができません。
そこでlibvirtの設定を変更することでSSH経由ではなくlibvirtに対して直接TCPで接続するようにします。 設定自体はlibvirt - ArchWikiを参考にしました。
libvirtの設定ファイルである/etc/libvirt/libvirtd.confを直接変更する形だと、なぜか再起動を跨ぐと設定が揮発してしまったため、適当な場所に設定ファイルを作成して、libvirtのユニットファイルにおいてその設定ファイルを読み込ませるような設定をします。
$ cat /usr/local/lib/systemd/system/pkg-libvirtd.service
[Unit]
Description=libvirt daemon
Requires=pkg-virtlogd.service
After=pkg-virtlogd.service
IgnoreOnIsolate=yes
DefaultDependencies=no
[Service]
KillMode=process
Restart=always
RestartSec=1
StartLimitBurst=5
StartLimitInterval=10
Type=forking
ExecStartPre=/bin/bash /var/packages/Virtualization/conf/systemd/insert_libvirtd_ko.sh
# この行の--configを/etc/libvirt/libvirtd.confから自分で用意したものに変える
ExecStart=/var/packages/Virtualization/target/usr/local/bin/libvirtd -d --listen --config /var/services/homes/localadmin/libvirtd.conf --pid-file /var/run/libvirtd.pid
ExecStopPost=/bin/bash /var/packages/Virtualization/conf/systemd/umount_qemu_mem.sh
PIDFile=/var/run/libvirtd.pid
Slice=Virtualization.slice
Delegate=yes
[X-Synology]
設定自体は簡単で、listenするアドレスを変更・認証の無効化をしているだけです。
$ sudo diff /var/services/home/localadmin/libvirtd.conf /etc/libvirt/libvirtd.conf
522,523c522
< #listen_addr = "127.0.0.1"
< listen_addr = "172.16.1.1"
---
> listen_addr = "127.0.0.1"
525,526c524
< #auth_tcp = "sasl"
< auth_tcp = "none"
---
> auth_tcp = "sasl"