2026年5月19日火曜日

古い Proxmove VE 環境からVMをリカバリする

 古くなったSATA SSDが時々リブートで見えなくなったり、ついには稼働中にI/Oエラーを起こしたりするようになったので交換することにしました。


新しいSSDにProxmox VEをクリーンインストールした後、ダメモトで旧ドライブをUSBマスストレージコントローラ経由で接続したところ、認識成功。見えているうちにデータを復元することにします。

仮想マシンの定義ファイルは /etc/pve/ 以下にあるのだが

Proxmox VE の仮想マシン定義ファイルは /etc/pve/nodes/{hostname}/qemu-server/ 以下にテキストの定義ファイルとして置かれています。これをバックアップするため以下のコマンドを実行しました。

cd /mnt/oldroot
tar cvzf ~/old.etc.pve.tar.gz ./etc/pve
cd ~
tar xvzf old.etc.pve.tar.gz

そうするとあら不思議... 空のディレクトリが。/etc/pve は普通のディレクトリではなく、pmxcfs による分散設定ファイルシステムになっています。実体は /var/lib/pve-cluster/config.db に保存されていました。

ドライブが読めなくなる前にDBファイルを保存します。

cd /mnt/oldroot
tar cvzf ~/old.var.lib.pve-cluster.tar.gz ./var/lib/pve-cluster
cd ~
tar xvzf old.var.lib.pve-cluster.tar.gz

sqlite3で覗いてみます。

% sqlite3 config.db
SQLite version 3.51.0 2025-06-12 13:14:41
Enter ".help" for usage hints.
sqlite> select * from tree;
0|0|46624809|0|1777877202|8|__version__|

6|0|7|0|1682337809|8|datacenter.cfg|keyboard: en-us

8|0|8|0|1682337874|4|virtual-guest|
9|0|9|0|1682337875|4|priv|
11|0|11|0|1682337875|4|nodes|
12|11|12|0|1682337875|4|pve|
13|12|13|0|1682337875|4|lxc|
14|12|14|0|1682337875|4|qemu-server|
15|12|15|0|1682337875|4|openvz|
16|12|16|0|1682337875|4|priv|
17|9|17|0|1682337875|4|lock|
24|0|25|0|1682337875|8|pve-www.key|-----BEGIN RSA PRIVATE KEY-----
...

なるほど、6つ目のフィールドがファイル名で、7つ目のフィールドにファイルコンテンツがあるだけの模様。ただ手作業でファイルをひとつひとつリカバリするのも面倒です。


opencode + Local LLM で DB 構造を解析させリカバリプログラムを書かせる

もう、やることが決まってしまった作業は退屈なだけです。

これぐらいのタスクなら軽量の Local LLM でも十分こなせます。外部のAPIサービスに投げない理由は、DBのなかにシークレットなどの情報が混ざっているかもしれないので、手許で推論します。

Mac に先の sqlite3 データベースをコピー、また適当に python venv を用意して、opencode を起動。プロンプトしました。推論にはローカル LM Studio で動く Qwen 3.6 35B-A3B を使用しました。

you will find config.db file which is in sqlite3 format. inspect its "tree" table and build a python script that recover files in the database. use venv/bin/python


呼吸していたら、動く recover.py が出てきました。(大したものでもないですが gist においておきます

このスクリプトを実行したら確かに DB 内のファイルが recovered/ サブディレクトリに階層構造つきで書き出されました。

% find recovered 
recovered
recovered/mapping
recovered/mapping/directory.cfg
recovered/authkey.pub
recovered/pve-www.key
recovered/sdn
recovered/nodes
recovered/nodes/pve
recovered/nodes/pve/pve-ssl.pem
recovered/nodes/pve/ssh_known_hosts
recovered/nodes/pve/openvz
recovered/nodes/pve/pve-ssl.key
recovered/nodes/pve/lxc
recovered/nodes/pve/qemu-server
recovered/nodes/pve/qemu-server/237.conf
recovered/nodes/pve/qemu-server/104.conf
recovered/nodes/pve/qemu-server/105.conf
recovered/nodes/pve/qemu-server/200.conf
recovered/nodes/pve/qemu-server/102.conf
recovered/nodes/pve/qemu-server/231.conf
recovered/nodes/pve/qemu-server/103.conf
recovered/nodes/pve/qemu-server/100.conf
recovered/nodes/pve/qemu-server/249.conf
recovered/nodes/pve/qemu-server/232.conf
recovered/nodes/pve/qemu-server/245.conf
recovered/nodes/pve/qemu-server/101.conf
recovered/nodes/pve/qemu-server/235.conf
...


VM定義ファイルを新環境に展開

経験上、これで qemu-server/XXX.conf ファイルを書き込んであげればVMが出てくるのを知っています。

% cd recovered/nodes/pve/qemu-server/
% scp * "root@{PVE}:/etc/pve/nodes/pve/qemu-server/"
root@{PVE}'s password: 100.conf 100% 537 113.0KB/s 00:00 101.conf 100% 421 110.6KB/s 00:00 102.conf 100% 575 129.4KB/s 00:00 103.conf 100% 417 84.1KB/s 00:00 104.conf 100% 462 102.1KB/s 00:00 105.conf 100% 462 99.0KB/s 00:00 106.conf 100% 485 103.6KB/s 00:00 200.conf 100% 253 54.6KB/s 00:00 ... 

qemu-server/XXX.conf が復元できれば、Proxmox VE は VM を再認識します。

Proxmox VE のコンソールを確認したところ、仮想マシンがホスト上に表示されるようになりました。


NVMeドライブ上のLVMプールを新環境に追加

仮想マシンのディスク本体は起動ドライブとは別の NVMe ドライブにあるので無事です。ただし、VM定義だけではディスク実体にアクセスできないため、NVMe上のLVM Volume Groupを新しい PVE 環境へ再登録します。

root@pve:~# pvesm add lvm NVMe1_ZP4000GM30023_SERIAL123 --vgname NVMe1_ZP4000GM30023_SERIAL123 \ --content images


ネットワーク設定も単純だったこともあり、過去のドライブ上のISOメディアやUSBデバイスの接続など、イレギュラーな設定が入っているものをのぞけば、これでVMが起動できるところまで復元できました。


古いドライブからVMを救出する場合

古いドライブの LVM に VM がある場合は、VMイメージもリカバリしたくなるかもしれません。Proxmox VE は qm コマンドで VM をオンラインでマイグレーションさせられるので、以下の手順でできそうです。
  • 古いドライブを pvesm add lvm コマンドにて新しい名前で登録
    (デフォルトの LVM 名がコンフリクトする)
  • sed などで VM定義のなかのディスクイメージのプール名を編集
  • qm move_disk で新しいドライブへマイグレーションさせる

今回はこのニーズがないため、この記事では扱いません。


まとめ

Proxmox VE の仮想マシンデータのリカバリを、面倒くさい部分は生成AIで自動化して済ませました。仮想ディスク本体が別ストレージに残っている場合、VM定義ファイルを DB から抜き出してしまえば優勝です。

手間のかかる部分をスクリプト生成・実行で済ませたので何も困ることはありませんでしたが、復元作業よりも、このエントリを書くことのほうに時間がかかりました。

次の投稿ネタまでに、作業記録の記事化をどう自動化するかを考えたほうがいいのかもしれません。

2026年5月1日金曜日

Proxmox VEホストのZFSデータセットにTimeMachineバックアップ

MacBook Proを新しく購入したので、Proxmox VE上のZFSストレージをバックアップ用途として見直し、TimeMachineの保存先として利用することにしました。

■ バックアップの圧縮と暗号化のポリシー: ZFSの機能を利用する

万が一どころか億が一、ハードディスクの持ち去り対策としてデータの暗号化を検討します。まぁ個人の Mac で、プール全体を暗号化していないので片手落ちなのですが...

データの圧縮や暗号化をどのレイヤーで行うか考え、最終的にどちらも ZFS のレイヤで行うことにしました。

結論としては、TimeMachine自体は暗号化することはあっても積極的には圧縮しないようで、ZFSのレイヤで圧縮をかけるとしても、TimeMachineの暗号化を有効にすると、ZFS側での圧縮はほぼ効かなくなります。圧縮したければTimeMachineの暗号化機能は利用しないほうがよい、ということになります。


■ Proxmox VE側の設定

まずは zpool、データセットの準備を行います。

array5 zpool

今回は既存の zpool プールを使用しますので、 zpool の作成方法などについては割愛します。 10TB HDD x5, RAIDZ2 で構成したプールです。

hasegaw@pve:~$ zpool list
NAME     SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
array5  45.5T  11.2T  34.3T        -         -     0%    24%  1.00x    ONLINE  -

array5/backup
array5/backup/timemachine

ここにはarray5/backup, array5/backup/timemachine データセットを作成します。まぁ、データセットも作成済みなのですが、

hasegaw@pve:~$ zfs list array5/backup
NAME            USED  AVAIL  REFER  MOUNTPOINT
array5/backup 1.31T 20.2T 211G /export/backup
array5/backup/timemachine 959G 20.2T 185K /export/backup/timemachine

新たに作成するのであれば以下のようなコマンドラインになりますね。

sudo zfs create array5/backup
sudo zfs create -o compression=lz4 array5/backup/timemachine

compression=lz4 を指定していますが、とても積極的な圧縮とまでいかなくても、ゼロが続くような領域についてはランレングス圧縮がかかれば良いな、というぐらいの期待値です。ここで gzip などを指定すれば多少圧縮率もあがるかもしれませんが、CPUサイクルとのバランス感は微妙だと思っています。

また、このホストではデータセットのマウント先を /export/backup にしています。(-o mountpoint=/export/backup)


array5/backup/Arseille

続いて TimeMachine 用の array5/backup/Arseille を作成します。 TimeMachine はマウントされたファイルシステムをまるごとバックアップ先として使用する仕様なので、TimeMachineの単位でSMB共有を作成することになります。

折角なので、ZFSのレイヤでも、ZFSデータセットとしても分けておきます。スナップショット単位の操作(zfs send/recv や destroy)を考えると、分けない理由があまり思いつきません。

  • MacBook Pro の内蔵SSDが 4TB なので、 TimeMachine バックアップと、ストレージが溢れた状態でもある程度の世代が残るように、 5TB でクオータを設定します。
  • 暗号化用の鍵をプロンプトにて指定します。鍵は別途管理します。

root@pve:~# zfs create -o quota=5T -o encryption=aes-256-gcm -o keyformat=passphrase \
-o keylocation=prompt array5/backup/timemachine/Arseille Enter new passphrase: Re-enter new passphrase:

TimeMachineで使用するユーザで、データセットのディレクトリに書き込めるようにしておきます。

# chown hasegaw:hasegaw /export/backup/timemachine/Arseille
hasegaw% touch /export/backup/timemachine/Arseille/hoge
hasegaw% unlink /export/backup/timemachine/Arseille/hoge

Samba のインストール

# apt upgrade
# apt install samba

smbpasswd

まだ Samba 用のパスワードを設定していなければ smbpasswd で設定しておきます。

今回はパスワード登録済みですが、新たに登録する場合は以下のイメージになります。

# smbpasswd -a hasegaw
※ -a は新規ユーザ登録。事前の /etc/passwd にそのユーザを作っておくこと

この SMB サーバに対しては私自身が普段使いのユーザアカウントで SMB マウントするため、今回はメインのユーザアカウントで TimeMachine 接続を想定します。

/etc/samba/smb.conf

もらってきた設定を貼り付けて少し弄っただけですが、以下の要領になります。

  • fruit:time machine=yes
  • fruit:time machine max size = 5000G: 設定は可能なのですが ZFSの quota で最大容量は伝わっているかなと思うので、いったん様子見で外しています。どのように振る舞うのかが判っていませんが、 ZFS 側の制限がハードクオータ、こちらの設定が TimeMachine に対する容量のヒントとして振る舞うように見えるので、 ZFS データセットよりもすこし小さい値に設定してほうがよいかもしれません。
  • ea support = yes となっていますが実際には ZFS レベルで EA を有効化していません。現状、手許では問題なく動いているように見えていますが、将来的に見直すかもしれません。
[timemachine_Arseille]
path = /export/backup/timemachine/Arseille
browseable = yes
read only = no
guest ok = no
writable = yes
valid users = hasegaw
fruit:time machine = yes
; fruit:time machine max size = 5000G
ea support = yes

Samba プロセスを再起動しておきます。

root# systemctl restart smbd


■ Mac 側の設定

MacBook Proからのマウント確認

Finderから Cmd-K 、接続先に smb://hostname_or_ip_address/ を入力し接続します。

ユーザ名、パスワードを聞かれたら PVE ホスト側のユーザ名および先に smbpasswd で指定したパスワードを入力します。

TimeMachine バックアップ先となる timemachine_Arseille 共有が見えたら、それをマウントします。中にフォルダを作成できることを確認し、書き込みが正しくできることを確認できたらテストで作成したフォルダを削除し、共有を改めて空の状態にしておきます。

Finder の左側ペインから、該当する共有のマウントを解除します。

TimeMachine 設定

バックアップ対象の MacBook Pro 側で、以下の要領で TimeMachine 先を設定します。

root# tmutil setdestination smb://user:passwd@hostname/timemachine_Arseille
root# tmutil startbackup

設定で TimeMachine を検索し、バックアップが開始されたことを確認します。



バックアップ終了後に ZFS データセットの割り当て量が増えていることが確認できます。

root@pve:~# zfs list array5/backup/timemachine/Arsielle
NAME                                 USED  AVAIL  REFER  MOUNTPOINT
array5/backup/timemachine/Arsielle  9.17G  4.99T  9.17G  /export/backup/timemachine/Arsielle


■ 再起動後の暗号化解除

Proxmox VEで動作するZFSのプールのうち、該当する timemachine 領域をマウントするには鍵を提供する必要があります。このためホストの再起動後はプールが見えなくなり、バックアップや TimeMachine の内容確認などの操作ができなくなります。

 zfs load-key コマンドにてプロンプトから鍵を入力しアンロックします。

root# zfs load-key array5/backup/timemachine/Arseille
root# systemctl restart smbd

プールが見えない状態では TimeMachine が sparse bundle を見つけられないため、バックアップは(安全に)失敗します。次回以降のバックアップで再マウントいs sparse bundle が見つかれば、バックアップは再開します。


■ ToDo: Mac持ち出し時の他ネットワークでのバックアップ防止

Mac の TimeMachine 機能はスケジュール通りにバックアップを繰り返すため、自宅から Mac を持ち出している間も、 tmutil setdestination で指定した hostname に対して user:passwd でログインしようとする挙動がつづきます。

セキュリティ的には気持ちが悪い挙動なので、ネットワーク条件に応じてTimeMachineを有効/無効に切り替える仕組みを今後検討したいと思います。

クラスCアドレスなので仮にインターネットに繋がっていても経路がなくSMB共有は繋がらないはずですが、接続先のWiFiが(以下自主規制)

かといって、この状況で IPsec などを使うのもどうかと思うし :S 自宅LANに接続していなければ自動バックアップ無効までやりたいと思いますが... これは後日としたいと思います。