PBS で実現する VM・ファイル・K8s PVC・遠隔レプリケーションの統合バックアップ

Note

このドキュメントは Claude Code を用いて作業しながら生成したものです。

GiganticMinecraft/seichi_infra での実装例を題材にしていますが、Proxmox Backup Server の活用方法を広く紹介することを主な目的としています。

はじめに

自宅で Minecraft サーバーや各種インフラを運用している中で、バックアップ基盤として Proxmox Backup Server (PBS) を採用しています。本記事では、VM バックアップだけでなく Kubernetes の PVC バックアップや遠隔地レプリケーションまで PBS で統合している事例を紹介します。

環境の全体像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─────────────────────────────────────────────────────────────┐
│  自宅 (本家)                                                 │
│                                                             │
│  ┌──────────────┐   ┌──────────────┐   ┌────────────────┐  │
│  │ Proxmox VE   │──▶│    PBS       │   │ K8s クラスタ    │  │
│  │ (複数クラスタ) │   │ sc-proxbksrv │◀──│ (seichi-onp)   │  │
│  └──────────────┘   │  -01         │   └────────────────┘  │
│                     │              │                        │
│                     │ zfs-vol-01(2T)│                        │
│                     │ zfs-vol-02(2T)│                        │
│                     └──────┬───────┘                        │
└────────────────────────────┼────────────────────────────────┘
                             │ Tailscale VPN
                             │ (毎時 Pull Sync)
┌────────────────────────────┼────────────────────────────────┐
│  実家 (バックアップ拠点)      │                                │
│                     ┌──────▼───────┐                        │
│                     │    PBS       │                        │
│                     │ zikka-prox   │                        │
│                     │  bksrv-01    │                        │
│                     │              │                        │
│                     │ zfs-vol-ssd  │                        │
│                     │  -01(2T)     │                        │
│                     │ zfs-vol-hdd  │                        │
│                     │  -01(2T)     │                        │
│                     └──────────────┘                        │
└─────────────────────────────────────────────────────────────┘
  • 本家側 PBS (sc-proxbksrv-01): PBS 4.1.x / ZFS データストア 2本 (各2TB, lz4圧縮)
  • 実家側 PBS (zikka-proxbksrv-01): PBS 4.1.x / ZFS データストア 2本 (SSD 2TB + HDD 2TB, lz4圧縮)
  • 両拠点間は Tailscale で接続し、実家側が毎時 Pull Sync でレプリケーション

1. なぜ PBS を選んだか

PBS を選んだ理由は以下の通りです。

  • バックアップ対象の広さ: VM だけでなくファイルベースのバックアップにも対応しており、あらゆる環境のバックアップ管理を一元化できる
  • 遠隔レプリケーションの容易さ: 拠点間の Sync が PBS の標準機能として用意されている
  • チャンクベースの重複排除: 転送量・ストレージ使用量の両方を効率化できる
  • Proxmox VE との統合度: ストレージ追加だけで VM バックアップが完結する

以降のセクションで、それぞれの側面を実際の構成とともに紹介していきます。

2. PVE 連携と PBS の基本構成

PVE 側の設定はストレージ追加だけ

Proxmox VE の Web UI から「ストレージ追加 → Proxmox Backup Server」を選ぶだけで、PBS がバックアップ先として使えるようになります。vzdump のバックアップジョブをスケジュール設定すれば、あとは放っておくだけです。

namespace によるマルチクラスタ管理

自宅では複数の Proxmox VE クラスタを運用しています。PBS の namespace 機能を使うと、1つのデータストアを論理的に分離してクラスタごとにバックアップを整理できます。

実際の構成では、監視用クラスタ (proxmox-mon) の VM は namespace proxmox-mon 配下にバックアップしていますが、メインクラスタの VM はデータストアの root namespace に直接置いてしまっています。

1
2
3
4
zfs-vol-01/
├── vm/102, vm/104, vm/120, ...   ← メインクラスタ (root namespace)
└── ns/proxmox-mon/
    └── vm/1001, vm/1002, ...     ← 監視クラスタ

これは namespace 機能を知る前にバックアップを取り始めた名残で、理想的にはすべてのクラスタを個別の namespace に分離すべきです。root namespace にバックアップが混在していると、ACL でクラスタ単位の権限分離をしようとしたときに root namespace 全体へのアクセス権が必要になってしまい、分離の意味が薄れます。今後の改善ポイントです。

API トークンの ACL と namespace を組み合わせることで、各クラスタが自分の領域だけにアクセスする権限分離を実現できます。

API トークンによる権限分離

PBS ではクラスタごと・用途ごとに API トークンを発行し、最小権限で運用しています。

ユーザートークン用途権限
machine-user--bk@pbsprox-cluster--seichi-networkPVE クラスタからの VM バックアップDatastoreAdmin
machine-user--bk@pbsprox-cluster--proxmox-mon監視クラスタからの VM バックアップDatastoreAdmin
machine-user--bk-k8s@pbsseichi-onp-k8sK8s からの PVC バックアップDatastoreBackup
monitoring@pbszabbix-monZabbix からの監視DatastoreReader

トークンごとに namespace のスコープも指定できるため、K8s バックアップ用トークンが VM バックアップ領域に触れない、といった分離が簡単に実現できます。

3. proxmox-backup-client ── どこでも使える汎用バックアップツール

PBS の強みの一つが proxmox-backup-client の手軽さです。Debian/Ubuntu 環境なら apt-get install proxmox-backup-client だけでインストールでき、PVE 管理外のマシンからでもすぐにバックアップが取れます。

proxmox-backup-client は任意のディレクトリを pxar 形式でバックアップできるため、本質的には 汎用的なファイルレベルバックアップツール です。後述する K8s PVC のバックアップも、PVC をマウントしたディレクトリに対して proxmox-backup-client backup "data.pxar:/data" を実行しているだけで、やっていることはファイルレベルバックアップそのものです。

コマンド一発でバックアップ

基本的な使い方は非常にシンプルです。

1
2
3
4
5
6
7
8
# 環境変数でPBS接続情報を設定
export PBS_REPOSITORY="user@pbs!token@pbs-host:datastore"
export PBS_FINGERPRINT="XX:XX:..."
export PBS_PASSWORD="token-secret"

# ディレクトリをまるごとバックアップ (pxar形式)
proxmox-backup-client backup "data.pxar:/path/to/data" \
  --backup-id "my-backup"

Proxmox VE をまったく使っていない環境でも、PBS をバックアップサーバーとして独立利用できます。

チャンクベースの重複排除と差分転送

ここで、PBS のバックアップが効率的な理由を説明しておきます。

PBS はバックアップデータをチャンクに分割して保存しています。VM イメージは固定長(通常 4MiB)、pxar ファイルアーカイブは可変長(Buzhash ローリングハッシュで境界を決定)のチャンクに分割されます。各チャンクはその内容の SHA-256 チェックサムで識別され、同じ内容のチャンクはデータストア内で再利用されます。公式ドキュメントでは次のように説明されています。

The deduplication of datastores is based on reusing chunks, which are referenced by the indexes in a backup snapshot.

つまり、インデックスファイルが「元のデータを復元するにはどのチャンクをどの順で並べればよいか」を保持し、同一内容のチャンクは複数のスナップショットから共有されます。バックアップ時もサーバー側に既に同じチェックサムのチャンクがあれば実データの転送はスキップされるため、各スナップショットは論理的には完全バックアップでありながら、転送量はインクリメンタルです。

この仕組みは、大量の小さなファイル(Minecraft のワールドデータなど)のバックアップや、後述する遠隔レプリケーションで特に効果を発揮します。

VM 以外のバックアップにも対応

自宅では Proxmox VE の VM バックアップだけでなく、以下のような用途で proxmox-backup-client を直接利用しています。

バックアップ対象方式backup-id
Minecraft サーバーの PVC (5サーバー分)K8s CronWorkflow から実行mcserver--s1mcserver--s7
Garage (S3互換オブジェクトストレージ)S3 dump → pxar 化garage
Minecraft ロビーサーバーK8s CronWorkflow から実行mcserver--lobby

PBS に host/ タイプとして記録されるこれらのバックアップは、VM バックアップと同じ Web UI 上で一覧・リストア・検証が可能です。バックアップ基盤をPBSに統合することで、監視や管理の対象がバラけずに済んでいます。

4. Kubernetes PVC バックアップの実践

proxmox-backup-client が汎用的なファイルバックアップツールだと分かったところで、K8s 上でどのように活用しているかを紹介します。

VolumeSnapshot ではなくバックアップが必要な理由

Kubernetes には VolumeSnapshot という仕組みがあり、CSI ドライバーを通じて PVC のスナップショットを取得できます。しかし、VolumeSnapshot はバックアップの代替にはなりません。

VolumeSnapshotPBS バックアップ
保存先元の PVC と同じストレージバックエンド別サーバー (PBS)
障害ドメインストレージ障害で本体もスナップショットも失われるストレージとは独立
オフサイトコピー不可PBS Sync で遠隔拠点にレプリケーション可能
ストレージ非依存のリストア不可(同じ CSI ドライバーとストレージが必要)PBS からどこにでもリストア可能
整合性検証なしPBS の Verify ジョブで定期検証

VolumeSnapshot は「うっかり削除してしまったファイルをすぐ戻したい」といったオペレーションには便利ですが、ストレージ障害やクラスタ全損からのリカバリには使えません。自宅環境では TrueNAS や Synology NAS をストレージバックエンドに使っていますが、これらが壊れたときに VolumeSnapshot も一緒に消えます。

そのため、PVC のデータを PBS という別のサーバーに取り出し、さらに実家の PBS にレプリケーションすることで、ストレージ障害からもクラスタ全損からも復旧できるバックアップにしています。

なお、VolumeSnapshot を PBS バックアップと組み合わせることで改善できる余地もあります。現在の構成では StatefulSet を停止してから PVC を直接マウントしてバックアップしているため、その間 Minecraft サーバーにダウンタイムが発生します。「Pod 稼働中にスナップショットを取得 → スナップショットから PVC を作成 → その PVC をマウントして PBS にバックアップ」という流れにすれば、ダウンタイムを大幅に短縮できるはずです。これは今後の改善候補です。

構成: Argo Workflows + proxmox-backup-client

K8s 上の PVC バックアップは Argo Workflows の CronWorkflow で自動化しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
毎日 04:00 JST (Minecraft サーバー)
毎日 10:00 JST (Garage)
   CronWorkflow 起動
  ┌──────────────────┐
  │ StatefulSet を     │  ← データ整合性のため Pod を停止
  │ replicas: 0 に     │
  └────────┬─────────┘
  ┌──────────────────┐
  │ PVC をマウントした  │
  │ ジョブ Pod を起動   │
  │                    │
  │ proxmox-backup-   │
  │ client backup     │
  │ "data.pxar:/data" │
  └────────┬─────────┘
  ┌──────────────────┐
  │ ArgoCD sync で     │  ← Pod を再起動
  │ StatefulSet 復帰   │
  └──────────────────┘

ポイント: StatefulSet 停止による整合性確保

Minecraft サーバーのようなステートフルワークロードでは、稼働中にファイルをコピーするとデータ破損のリスクがあります。そこで、バックアップ前に StatefulSet のレプリカを 0 にスケールダウンし、Pod が完全に停止したことを確認してから PVC をマウント・バックアップしています。

バックアップ完了後は、ワークフロー内から argocd app sync を明示的に呼び出して StatefulSet を Git 上の定義(replicas: 1)に戻します。

ここで厄介なのが ArgoCD の selfHeal との競合です。バックアップ中に kubectl patch で replicas を 0 にしますが、selfHeal が有効だと ArgoCD が「Git の定義と違う」と検知して replicas を元に戻してしまいます。かといって selfHeal を常時無効にすると、意図しないドリフトの自動修復が効かなくなります。

そこで ArgoCD の SyncWindow を使い、selfHeal が動作する時間帯を制限しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
syncWindows:
  # バックアップ中に selfHeal で replicas が戻されないよう、
  # selfHeal の実行時間を制限する
  - kind: allow
    schedule: "00 7 * * *"    # JST 7:00 から
    duration: 1h              # 1時間だけ selfHeal を許可
    timeZone: "Asia/Tokyo"
    manualSync: true          # 手動 sync はいつでも可能
  - kind: deny
    schedule: "00 8 * * *"    # JST 8:00 から
    duration: 23h             # 23時間 selfHeal を抑止
    timeZone: "Asia/Tokyo"
    manualSync: true

バックアップは JST 4:00 に開始されるため、deny ウィンドウ中に実行されます。selfHeal は抑止されつつ、manualSync: true によりワークフローからの明示的な sync は通ります。JST 7:00〜8:00 の allow ウィンドウで selfHeal が動作し、万が一ワークフローが途中で失敗して replicas が 0 のままになっていても自動修復されます。

ポイント: ジョブ内で proxmox-backup-client をインストール

バックアップジョブの実行コンテナ内で PBS のリポジトリを追加し、proxmox-backup-client をインストールしています。常駐エージェントを K8s 内にデプロイする必要がなく、ジョブ単位で完結するためクラスタへの影響が最小限です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# ワークフロー内のインストールステップ (抜粋)
script:
  image: debian:13
  command: [bash]
  source: |
    apt-get update
    apt-get install -y proxmox-backup-client
    proxmox-backup-client backup "data.pxar:/data" \
      --repository "$PBS_USER@$PBS_HOST:$PBS_DATASTORE" \
      --backup-id "{{ inputs.parameters.backup-id }}"

障害通知

バックアップワークフローが失敗した場合、Argo Workflows の exit handler から Discord Webhook で通知が飛びます。深夜のバックアップ失敗にもすぐに気付ける体制にしています。

実際のコード

この仕組みは OSS として公開しています。

5. Tailscale × PBS Sync で遠隔地レプリケーション

自宅のデータを物理的に離れた実家にレプリケーションするために、PBS の Sync JobTailscale を組み合わせています。

構成

  • 実家側 PBS から自宅側 PBS に対して Pull Sync を毎時実行
  • 接続は Tailscale の VPN 経由 (ポート開放不要)
  • データストアの対応: zfs-vol-01 → zfs-vol-ssd-01, zfs-vol-02 → zfs-vol-hdd-01
1
2
3
4
# 実家側 PBS の sync-job 設定
Remote: sc-proxbksrv-01 (100.x.x.x)  ← Tailscale IP
Schedule: hourly
Mode: Pull

なぜ PBS Sync が良いか

PBS の Sync は単なるファイルコピーではなく、チャンク単位の差分転送です。セクション3で説明したチャンクの仕組みが Sync でもそのまま働くため、既に転送済みのチャンクはチェックサムの照合だけで済みます。

これにより、自宅〜実家間の Tailscale 回線 (実質的には家庭用インターネット回線) でも、毎時の同期が現実的な時間で完了しています。

6. 運用のコツ

GC・Prune・Verify の三点セット

PBS ではバックアップの保守タスクとして以下の3つをスケジュール実行しています。

タスクスケジュール役割
Prune毎時保持ポリシーに基づいて古いスナップショットのメタデータ(マニフェスト、インデックス等)を削除
GC (Garbage Collection)毎日Prune の結果どこからも参照されなくなったチャンクを削除し、ストレージを解放
Verify毎日バックアップデータの整合性を検証 (30日以内に検証済みのものはスキップ)

余談: PBS の重複排除と ZFS dedup は別物だった

正直に書くと、筆者はつい最近まで「PBS のデータストアに ZFS が必要なのは ZFS dedup で重複排除するためだ」と思い込んでいました。この記事を書く過程で実際に確認してみたところ:

1
2
3
4
# zfs get dedup zfs-vol-01 zfs-vol-02
NAME        PROPERTY  VALUE  SOURCE
zfs-vol-01  dedup     off    default
zfs-vol-02  dedup     off    default

ZFS dedup は off でした。PBS の重複排除はあくまで PBS 自身がチャンクレベルで行っているものであり、ZFS の dedup 機能とは無関係です。

そもそも PBS のデータストアは ZFS 必須ではなく、公式ドキュメントにも:

The current implementation uses a directory inside a standard Unix file system (ext4, xfs or zfs) to store the backup data.

と書かれている通り、ext4 や XFS でも構いません。

では なぜ ZFS を選ぶのか? 重複排除のためではなく、以下の機能がバックアップサーバーと相性が良いからです。

ZFS の機能バックアップサーバーにおけるメリット
lz4 圧縮CPU 負荷がほぼゼロで透過的に容量を節約できる。自宅環境では 1.02〜1.04x の圧縮が効いている
scrub + チェックサム全ブロックにチェックサムを持ち、読み書き時に常時検証する。scrub は定期的にディスク上の全データを走査し、ハードウェア障害によるサイレントエラーを検出する。ext4 等のジャーナリングは「書き込み途中のクラッシュからファイルシステム構造を守る」仕組みであり、正常に書き込まれた後のビット腐敗(ビットロット)は検出できない。ZFS はこのサイレントなデータ破損を検出できる。バックアップは「いざというとき読めなければ意味がない」ため、これが重要

PBS は Verify ジョブでバックアップデータの整合性を検証しますが、これはアプリケーションレベルのチェックです。ZFS の scrub はファイルシステムレベルで全データを検証するため、両者を組み合わせることで二重の安全網になります。

一方、ZFS dedup を PBS の上で有効にするのは逆効果です。PBS のチャンクは既に SHA-256 で一意に管理されているため ZFS 側で重複がほとんど見つからず、dedup テーブルが RAM を大量に消費する(一般に 1TB あたり数 GB)だけで終わります。

結論: PBS のデータストアには「ZFS + lz4 圧縮 + dedup なし」が最適解。 重複排除は PBS に任せ、ZFS には圧縮とデータ整合性の仕事をしてもらうのが正しい役割分担です。

Zabbix による PBS バックアップ監視

PBS の REST API と Zabbix を組み合わせて、全バックアップグループの鮮度を自動監視しています。

監視アーキテクチャ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
PBS ホスト上:
  /usr/local/bin/pbs-backup-monitor  ← カスタムスクリプト
         │ PBS REST API (monitoring@pbs!zabbix-mon トークン)
  JSON で全バックアップグループの情報を出力
         │ Zabbix Agent 2 UserParameter
  Zabbix Server
         │ LLD (ローレベルディスカバリ)
  バックアップグループごとにアイテム・トリガーを自動生成

PBS ホスト上に配置したカスタムスクリプト (pbs-backup-monitor) が PBS REST API を叩き、全バックアップグループの情報を JSON で出力します。これを Zabbix Agent 2 の UserParameter 経由で収集し、ローレベルディスカバリ (LLD) で各バックアップグループのアイテムとトリガーを自動生成しています。

自作テンプレート「PBS Backup Monitoring」

PBS ホスト上のスクリプトがローカルの PBS API を叩き、その結果を Zabbix Agent 経由で Zabbix サーバーに返す構成です。カスタムスクリプトとテンプレートは以下のファイルとして公開しています。セットアップ手順はテンプレートの description に記載しています。

  • pbs-backup-monitor.py — PBS ホスト上に配置するカスタムスクリプト (/usr/local/bin/pbs-backup-monitor)
  • pbs.conf — Zabbix Agent 2 の UserParameter 設定 (/etc/zabbix/zabbix_agent2.d/pbs.conf)
  • pbs-backup-monitoring-template.yaml — Zabbix 7.0 テンプレート (インポートしてそのまま使えます)

テンプレートの構成:

要素内容
マスターアイテムpbs.groups.all — 全バックアップグループの JSON を一括取得
ディスカバリルールpbs.groups.discovery — マスターアイテムの JSON からグループを自動検出
アイテムプロトタイプグループごとに Last backup age (最終バックアップからの経過秒数) と Snapshot count (スナップショット数) を生成
トリガープロトタイプBackup is too old (経過時間がしきい値超過) / No backup snapshots (スナップショット数 0)

しきい値のカスタマイズ

トリガーのしきい値はホストマクロ {$PBS.MAX.AGE.SECONDS} で制御しており、バックアップグループ単位で上書きできます。

1
2
3
4
5
6
7
# デフォルト (日次バックアップ向け)
{$PBS.MAX.AGE.SECONDS} = 129600  (36時間)

# 週次バックアップのグループは個別に緩和
{$PBS.MAX.AGE.SECONDS:"zfs-vol-02::vm:1001"} = 720000  (約200時間)
{$PBS.MAX.AGE.SECONDS:"zfs-vol-02::vm:1101"} = 720000
...

日次バックアップなら 36 時間以内に次のバックアップが来なければアラート、週次のものは 200 時間、というようにグループの性質に合わせて調整しています。

監視のポイント

この仕組みの良いところは、PBS にバックアップグループが増えると LLD が自動検出し、新しいアイテムとトリガーが勝手に生成される点です。新しい VM を Proxmox VE のバックアップジョブに追加したり、K8s で新しい PVC バックアップを設定したりしても、Zabbix 側は何もしなくて済みます。

また、本家・実家の両方の PBS に同じテンプレートを適用しているため、Sync の遅延や失敗も間接的に検知できます。実家側で特定グループの Last backup age が異常に大きくなれば、Sync が止まっているか元のバックアップ自体が止まっているかのどちらかです。

まとめ

Proxmox Backup Server を使ってみて感じた利点をまとめます。

  • Proxmox VE とのシームレスな統合: VM バックアップがストレージ追加だけで完結する
  • proxmox-backup-client の汎用性: VM に限らず、K8s の PVC やオブジェクトストレージのデータなど、あらゆるファイルベースのバックアップに使える
  • チャンク単位の重複排除・差分転送: 回線帯域が限られる遠隔地レプリケーションでも実用的
  • namespace と API トークンによる権限分離: マルチクラスタ・マルチ用途でも1台の PBS で安全に管理できる
  • Web UI でのバックアップ一覧・リストア・検証: VM もファイルバックアップも同じ画面で管理できる統一感

これらを組み合わせた結果、バックアップの「3-2-1 ルール」もおおむね達成できています。

ルール実現方法
3 コピー本番データ + 自宅 PBS + 実家 PBS
2 種類のメディア本番環境のストレージ + PBS の ZFS (SSD/HDD)
1 つはオフサイト実家に物理的に分離された PBS

「PBS は Proxmox VE のオマケ」と思われがちですが、実際には Proxmox VE なしでもスタンドアロンのバックアップサーバーとして十分に機能します。特に proxmox-backup-client の手軽さは、Kubernetes のようなコンテナ環境との組み合わせで真価を発揮します。

自宅サーバーのバックアップに悩んでいる方は、ぜひ PBS を検討してみてください。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。