[{"content":" Note\nこのドキュメントは Claude Code を用いて作業しながら生成したものです。\nGiganticMinecraft/seichi_infra での実装例を題材にしていますが、Proxmox Backup Server の活用方法を広く紹介することを主な目的としています。\nはじめに 自宅で Minecraft サーバーや各種インフラを運用している中で、バックアップ基盤として Proxmox Backup Server (PBS) を採用しています。本記事では、VM バックアップだけでなく Kubernetes の PVC バックアップや遠隔地レプリケーションまで PBS で統合している事例を紹介します。\n環境の全体像 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 を選んだ理由は以下の通りです。\nバックアップ対象の広さ: VM だけでなくファイルベースのバックアップにも対応しており、あらゆる環境のバックアップ管理を一元化できる 遠隔レプリケーションの容易さ: 拠点間の Sync が PBS の標準機能として用意されている チャンクベースの重複排除: 転送量・ストレージ使用量の両方を効率化できる Proxmox VE との統合度: ストレージ追加だけで VM バックアップが完結する 以降のセクションで、それぞれの側面を実際の構成とともに紹介していきます。\n2. PVE 連携と PBS の基本構成 PVE 側の設定はストレージ追加だけ Proxmox VE の Web UI から「ストレージ追加 → Proxmox Backup Server」を選ぶだけで、PBS がバックアップ先として使えるようになります。vzdump のバックアップジョブをスケジュール設定すれば、あとは放っておくだけです。\nnamespace によるマルチクラスタ管理 自宅では複数の Proxmox VE クラスタを運用しています。PBS の namespace 機能を使うと、1つのデータストアを論理的に分離してクラスタごとにバックアップを整理できます。\n実際の構成では、監視用クラスタ (proxmox-mon) の VM は namespace proxmox-mon 配下にバックアップしていますが、メインクラスタの VM はデータストアの root namespace に直接置いてしまっています。\n1 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 全体へのアクセス権が必要になってしまい、分離の意味が薄れます。今後の改善ポイントです。\nAPI トークンの ACL と namespace を組み合わせることで、各クラスタが自分の領域だけにアクセスする権限分離を実現できます。\nAPI トークンによる権限分離 PBS ではクラスタごと・用途ごとに API トークンを発行し、最小権限で運用しています。\nユーザー トークン 用途 権限 machine-user--bk@pbs prox-cluster--seichi-network PVE クラスタからの VM バックアップ DatastoreAdmin machine-user--bk@pbs prox-cluster--proxmox-mon 監視クラスタからの VM バックアップ DatastoreAdmin machine-user--bk-k8s@pbs seichi-onp-k8s K8s からの PVC バックアップ DatastoreBackup monitoring@pbs zabbix-mon Zabbix からの監視 DatastoreReader トークンごとに namespace のスコープも指定できるため、K8s バックアップ用トークンが VM バックアップ領域に触れない、といった分離が簡単に実現できます。\n3. proxmox-backup-client ── どこでも使える汎用バックアップツール PBS の強みの一つが proxmox-backup-client の手軽さです。Debian/Ubuntu 環境なら apt-get install proxmox-backup-client だけでインストールでき、PVE 管理外のマシンからでもすぐにバックアップが取れます。\nproxmox-backup-client は任意のディレクトリを pxar 形式でバックアップできるため、本質的には 汎用的なファイルレベルバックアップツール です。後述する K8s PVC のバックアップも、PVC をマウントしたディレクトリに対して proxmox-backup-client backup \u0026quot;data.pxar:/data\u0026quot; を実行しているだけで、やっていることはファイルレベルバックアップそのものです。\nコマンド一発でバックアップ 基本的な使い方は非常にシンプルです。\n1 2 3 4 5 6 7 8 # 環境変数でPBS接続情報を設定 export PBS_REPOSITORY=\u0026#34;user@pbs!token@pbs-host:datastore\u0026#34; export PBS_FINGERPRINT=\u0026#34;XX:XX:...\u0026#34; export PBS_PASSWORD=\u0026#34;token-secret\u0026#34; # ディレクトリをまるごとバックアップ (pxar形式) proxmox-backup-client backup \u0026#34;data.pxar:/path/to/data\u0026#34; \\ --backup-id \u0026#34;my-backup\u0026#34; Proxmox VE をまったく使っていない環境でも、PBS をバックアップサーバーとして独立利用できます。\nチャンクベースの重複排除と差分転送 ここで、PBS のバックアップが効率的な理由を説明しておきます。\nPBS はバックアップデータをチャンクに分割して保存しています。VM イメージは固定長（通常 4MiB）、pxar ファイルアーカイブは可変長（Buzhash ローリングハッシュで境界を決定）のチャンクに分割されます。各チャンクはその内容の SHA-256 チェックサムで識別され、同じ内容のチャンクはデータストア内で再利用されます。公式ドキュメントでは次のように説明されています。\nThe deduplication of datastores is based on reusing chunks, which are referenced by the indexes in a backup snapshot.\nつまり、インデックスファイルが「元のデータを復元するにはどのチャンクをどの順で並べればよいか」を保持し、同一内容のチャンクは複数のスナップショットから共有されます。バックアップ時もサーバー側に既に同じチェックサムのチャンクがあれば実データの転送はスキップされるため、各スナップショットは論理的には完全バックアップでありながら、転送量はインクリメンタルです。\nこの仕組みは、大量の小さなファイル（Minecraft のワールドデータなど）のバックアップや、後述する遠隔レプリケーションで特に効果を発揮します。\nVM 以外のバックアップにも対応 自宅では Proxmox VE の VM バックアップだけでなく、以下のような用途で proxmox-backup-client を直接利用しています。\nバックアップ対象 方式 backup-id Minecraft サーバーの PVC (5サーバー分) K8s CronWorkflow から実行 mcserver--s1 〜 mcserver--s7 Garage (S3互換オブジェクトストレージ) S3 dump → pxar 化 garage Minecraft ロビーサーバー K8s CronWorkflow から実行 mcserver--lobby PBS に host/ タイプとして記録されるこれらのバックアップは、VM バックアップと同じ Web UI 上で一覧・リストア・検証が可能です。バックアップ基盤をPBSに統合することで、監視や管理の対象がバラけずに済んでいます。\n4. Kubernetes PVC バックアップの実践 proxmox-backup-client が汎用的なファイルバックアップツールだと分かったところで、K8s 上でどのように活用しているかを紹介します。\nVolumeSnapshot ではなくバックアップが必要な理由 Kubernetes には VolumeSnapshot という仕組みがあり、CSI ドライバーを通じて PVC のスナップショットを取得できます。しかし、VolumeSnapshot はバックアップの代替にはなりません。\nVolumeSnapshot PBS バックアップ 保存先 元の PVC と同じストレージバックエンド 別サーバー (PBS) 障害ドメイン ストレージ障害で本体もスナップショットも失われる ストレージとは独立 オフサイトコピー 不可 PBS Sync で遠隔拠点にレプリケーション可能 ストレージ非依存のリストア 不可（同じ CSI ドライバーとストレージが必要） PBS からどこにでもリストア可能 整合性検証 なし PBS の Verify ジョブで定期検証 VolumeSnapshot は「うっかり削除してしまったファイルをすぐ戻したい」といったオペレーションには便利ですが、ストレージ障害やクラスタ全損からのリカバリには使えません。自宅環境では TrueNAS や Synology NAS をストレージバックエンドに使っていますが、これらが壊れたときに VolumeSnapshot も一緒に消えます。\nそのため、PVC のデータを PBS という別のサーバーに取り出し、さらに実家の PBS にレプリケーションすることで、ストレージ障害からもクラスタ全損からも復旧できるバックアップにしています。\nなお、VolumeSnapshot を PBS バックアップと組み合わせることで改善できる余地もあります。現在の構成では StatefulSet を停止してから PVC を直接マウントしてバックアップしているため、その間 Minecraft サーバーにダウンタイムが発生します。「Pod 稼働中にスナップショットを取得 → スナップショットから PVC を作成 → その PVC をマウントして PBS にバックアップ」という流れにすれば、ダウンタイムを大幅に短縮できるはずです。これは今後の改善候補です。\n構成: Argo Workflows + proxmox-backup-client K8s 上の PVC バックアップは Argo Workflows の CronWorkflow で自動化しています。\n1 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 │ │ \u0026#34;data.pxar:/data\u0026#34; │ └────────┬─────────┘ ▼ ┌──────────────────┐ │ ArgoCD sync で │ ← Pod を再起動 │ StatefulSet 復帰 │ └──────────────────┘ ポイント: StatefulSet 停止による整合性確保 Minecraft サーバーのようなステートフルワークロードでは、稼働中にファイルをコピーするとデータ破損のリスクがあります。そこで、バックアップ前に StatefulSet のレプリカを 0 にスケールダウンし、Pod が完全に停止したことを確認してから PVC をマウント・バックアップしています。\nバックアップ完了後は、ワークフロー内から argocd app sync を明示的に呼び出して StatefulSet を Git 上の定義（replicas: 1）に戻します。\nここで厄介なのが ArgoCD の selfHeal との競合です。バックアップ中に kubectl patch で replicas を 0 にしますが、selfHeal が有効だと ArgoCD が「Git の定義と違う」と検知して replicas を元に戻してしまいます。かといって selfHeal を常時無効にすると、意図しないドリフトの自動修復が効かなくなります。\nそこで ArgoCD の SyncWindow を使い、selfHeal が動作する時間帯を制限しています。\n1 2 3 4 5 6 7 8 9 10 11 12 13 syncWindows: # バックアップ中に selfHeal で replicas が戻されないよう、 # selfHeal の実行時間を制限する - kind: allow schedule: \u0026#34;00 7 * * *\u0026#34; # JST 7:00 から duration: 1h # 1時間だけ selfHeal を許可 timeZone: \u0026#34;Asia/Tokyo\u0026#34; manualSync: true # 手動 sync はいつでも可能 - kind: deny schedule: \u0026#34;00 8 * * *\u0026#34; # JST 8:00 から duration: 23h # 23時間 selfHeal を抑止 timeZone: \u0026#34;Asia/Tokyo\u0026#34; manualSync: true バックアップは JST 4:00 に開始されるため、deny ウィンドウ中に実行されます。selfHeal は抑止されつつ、manualSync: true によりワークフローからの明示的な sync は通ります。JST 7:00〜8:00 の allow ウィンドウで selfHeal が動作し、万が一ワークフローが途中で失敗して replicas が 0 のままになっていても自動修復されます。\nポイント: ジョブ内で proxmox-backup-client をインストール バックアップジョブの実行コンテナ内で PBS のリポジトリを追加し、proxmox-backup-client をインストールしています。常駐エージェントを K8s 内にデプロイする必要がなく、ジョブ単位で完結するためクラスタへの影響が最小限です。\n1 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 \u0026#34;data.pxar:/data\u0026#34; \\ --repository \u0026#34;$PBS_USER@$PBS_HOST:$PBS_DATASTORE\u0026#34; \\ --backup-id \u0026#34;{{ inputs.parameters.backup-id }}\u0026#34; 障害通知 バックアップワークフローが失敗した場合、Argo Workflows の exit handler から Discord Webhook で通知が飛びます。深夜のバックアップ失敗にもすぐに気付ける体制にしています。\n実際のコード この仕組みは OSS として公開しています。\nMinecraft サーバー PVC バックアップ CronWorkflow 定義 — スケジュールと対象サーバー一覧 WorkflowTemplate — StatefulSet 停止 → PVC バックアップ → 復帰の DAG ServiceAccount / RBAC Garage バックアップ バックアップ WorkflowTemplate — S3 dump → PBS への pxar バックアップ リストア WorkflowTemplate — PBS からのリストア手順 5. Tailscale × PBS Sync で遠隔地レプリケーション 自宅のデータを物理的に離れた実家にレプリケーションするために、PBS の Sync Job と Tailscale を組み合わせています。\n構成 実家側 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 でもそのまま働くため、既に転送済みのチャンクはチェックサムの照合だけで済みます。\nこれにより、自宅〜実家間の Tailscale 回線 (実質的には家庭用インターネット回線) でも、毎時の同期が現実的な時間で完了しています。\n6. 運用のコツ GC・Prune・Verify の三点セット PBS ではバックアップの保守タスクとして以下の3つをスケジュール実行しています。\nタスク スケジュール 役割 Prune 毎時 保持ポリシーに基づいて古いスナップショットのメタデータ（マニフェスト、インデックス等）を削除 GC (Garbage Collection) 毎日 Prune の結果どこからも参照されなくなったチャンクを削除し、ストレージを解放 Verify 毎日 バックアップデータの整合性を検証 (30日以内に検証済みのものはスキップ) 余談: PBS の重複排除と ZFS dedup は別物だった 正直に書くと、筆者はつい最近まで「PBS のデータストアに ZFS が必要なのは ZFS dedup で重複排除するためだ」と思い込んでいました。この記事を書く過程で実際に確認してみたところ:\n1 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 機能とは無関係です。\nそもそも PBS のデータストアは ZFS 必須ではなく、公式ドキュメントにも:\nThe current implementation uses a directory inside a standard Unix file system (ext4, xfs or zfs) to store the backup data.\nと書かれている通り、ext4 や XFS でも構いません。\nでは なぜ ZFS を選ぶのか？ 重複排除のためではなく、以下の機能がバックアップサーバーと相性が良いからです。\nZFS の機能 バックアップサーバーにおけるメリット lz4 圧縮 CPU 負荷がほぼゼロで透過的に容量を節約できる。自宅環境では 1.02〜1.04x の圧縮が効いている scrub + チェックサム 全ブロックにチェックサムを持ち、読み書き時に常時検証する。scrub は定期的にディスク上の全データを走査し、ハードウェア障害によるサイレントエラーを検出する。ext4 等のジャーナリングは「書き込み途中のクラッシュからファイルシステム構造を守る」仕組みであり、正常に書き込まれた後のビット腐敗（ビットロット）は検出できない。ZFS はこのサイレントなデータ破損を検出できる。バックアップは「いざというとき読めなければ意味がない」ため、これが重要 PBS は Verify ジョブでバックアップデータの整合性を検証しますが、これはアプリケーションレベルのチェックです。ZFS の scrub はファイルシステムレベルで全データを検証するため、両者を組み合わせることで二重の安全網になります。\n一方、ZFS dedup を PBS の上で有効にするのは逆効果です。PBS のチャンクは既に SHA-256 で一意に管理されているため ZFS 側で重複がほとんど見つからず、dedup テーブルが RAM を大量に消費する（一般に 1TB あたり数 GB）だけで終わります。\n結論: PBS のデータストアには「ZFS + lz4 圧縮 + dedup なし」が最適解。 重複排除は PBS に任せ、ZFS には圧縮とデータ整合性の仕事をしてもらうのが正しい役割分担です。\nZabbix による PBS バックアップ監視 PBS の REST API と Zabbix を組み合わせて、全バックアップグループの鮮度を自動監視しています。\n監視アーキテクチャ 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) で各バックアップグループのアイテムとトリガーを自動生成しています。\n自作テンプレート「PBS Backup Monitoring」 PBS ホスト上のスクリプトがローカルの PBS API を叩き、その結果を Zabbix Agent 経由で Zabbix サーバーに返す構成です。カスタムスクリプトとテンプレートは以下のファイルとして公開しています。セットアップ手順はテンプレートの description に記載しています。\npbs-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 テンプレート (インポートしてそのまま使えます) テンプレートの構成:\n要素 内容 マスターアイテム 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} で制御しており、バックアップグループ単位で上書きできます。\n1 2 3 4 5 6 7 # デフォルト (日次バックアップ向け) {$PBS.MAX.AGE.SECONDS} = 129600 (36時間) # 週次バックアップのグループは個別に緩和 {$PBS.MAX.AGE.SECONDS:\u0026#34;zfs-vol-02::vm:1001\u0026#34;} = 720000 (約200時間) {$PBS.MAX.AGE.SECONDS:\u0026#34;zfs-vol-02::vm:1101\u0026#34;} = 720000 ... 日次バックアップなら 36 時間以内に次のバックアップが来なければアラート、週次のものは 200 時間、というようにグループの性質に合わせて調整しています。\n監視のポイント この仕組みの良いところは、PBS にバックアップグループが増えると LLD が自動検出し、新しいアイテムとトリガーが勝手に生成される点です。新しい VM を Proxmox VE のバックアップジョブに追加したり、K8s で新しい PVC バックアップを設定したりしても、Zabbix 側は何もしなくて済みます。\nまた、本家・実家の両方の PBS に同じテンプレートを適用しているため、Sync の遅延や失敗も間接的に検知できます。実家側で特定グループの Last backup age が異常に大きくなれば、Sync が止まっているか元のバックアップ自体が止まっているかのどちらかです。\nまとめ Proxmox Backup Server を使ってみて感じた利点をまとめます。\nProxmox VE とのシームレスな統合: VM バックアップがストレージ追加だけで完結する proxmox-backup-client の汎用性: VM に限らず、K8s の PVC やオブジェクトストレージのデータなど、あらゆるファイルベースのバックアップに使える チャンク単位の重複排除・差分転送: 回線帯域が限られる遠隔地レプリケーションでも実用的 namespace と API トークンによる権限分離: マルチクラスタ・マルチ用途でも1台の PBS で安全に管理できる Web UI でのバックアップ一覧・リストア・検証: VM もファイルバックアップも同じ画面で管理できる統一感 これらを組み合わせた結果、バックアップの「3-2-1 ルール」もおおむね達成できています。\nルール 実現方法 3 コピー 本番データ + 自宅 PBS + 実家 PBS 2 種類のメディア 本番環境のストレージ + PBS の ZFS (SSD/HDD) 1 つはオフサイト 実家に物理的に分離された PBS 「PBS は Proxmox VE のオマケ」と思われがちですが、実際には Proxmox VE なしでもスタンドアロンのバックアップサーバーとして十分に機能します。特に proxmox-backup-client の手軽さは、Kubernetes のようなコンテナ環境との組み合わせで真価を発揮します。\n自宅サーバーのバックアップに悩んでいる方は、ぜひ PBS を検討してみてください。\n","date":"2026-04-01T00:00:00Z","permalink":"http://blog.unchama.com/p/pbs-%E3%81%A7%E5%AE%9F%E7%8F%BE%E3%81%99%E3%82%8B-vm%E3%83%95%E3%82%A1%E3%82%A4%E3%83%ABk8s-pvc%E9%81%A0%E9%9A%94%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E7%B5%B1%E5%90%88%E3%83%90%E3%83%83%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97/","title":"PBS で実現する VM・ファイル・K8s PVC・遠隔レプリケーションの統合バックアップ"},{"content":" Note\nこのドキュメントは Claude Code を用いて作業しながら生成したものです。\nGiganticMinecraft/seichi_infra での実装例を題材にしていますが、democratic-csi で TrueNAS Scale を iSCSI バックエンドとして使用する方法を広く紹介することを主な目的としています。秘匿情報の管理や ArgoCD との連携といった seichi_infra 固有の構成はそのまま適用できない場合がありますが、CSI ドライバーの設定や TrueNAS Scale 側の権限設定については汎用的な参考になるはずです。\nはじめに オンプレミスの Kubernetes クラスター (seichi-onp-k8s) に、TrueNAS Scale をバックエンドとした iSCSI の動的ストレージプロビジョニングを導入しました。CSI ドライバーには democratic-csi を採用しています。\n全体構成 democratic-csi は Helm chart で提供されており、接続設定（TrueNAS の API キーや ZFS データセットのパス等）を Kubernetes Secret として渡すことで動作します。seichi_infra では GitOps パターン（ArgoCD + Terraform + GitHub Actions）を用いてこれを管理しています。\n1 2 3 4 5 6 7 8 9 10 GitHub Actions Secret (TF_VAR_ONP_K8S_DEMOCRATIC_CSI_SC_TRUENAS_03_DRIVER_CONFIG) ↓ terraform apply (main マージ時に自動実行) Kubernetes Secret: democratic-csi/democratic-csi-driver-config-sc-truenas-03 ↓ ArgoCD が参照 democratic-csi Helm chart v0.15.1 ↓ StorageClass: sc-truenas-03-iscsi VolumeSnapshotClass: sc-truenas-03-iscsi-vsc ↓ TrueNAS Scale (freenas-api-iscsi ドライバー / iSCSI) 実装の詳細 1. CSI ドライバーの設定 democratic-csi を Helm でインストールする際の最小限の設定は以下の通りです。接続設定（API キー等）は existingConfigSecret で既存の Kubernetes Secret を参照する形にしており、Helm values に秘匿情報を直接書かないようにしています。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 csiDriver: name: \u0026#34;org.democratic-csi.sc-truenas-03\u0026#34; # クラスター内で一意な名前 driver: existingConfigSecret: democratic-csi-driver-config-sc-truenas-03 # NOTE: existingConfigSecret 使用時も driver フィールドの指定が必要 config: driver: freenas-api-iscsi storageClasses: - name: sc-truenas-03-iscsi defaultClass: false reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true parameters: fsType: ext4 volumeSnapshotClasses: - name: sc-truenas-03-iscsi-vsc deletionPolicy: Delete parameters: fsType: ext4 # chart テンプレートのバグ回避 (空 map だと parameters: null になる) seichi_infra での ArgoCD Application 定義は app-of-other-apps/democratic-csi-sc-truenas-03.yaml を参照してください。\n2. 接続設定（Kubernetes Secret の中身） existingConfigSecret で参照する Secret には以下の形式の YAML を driver-config-file.yaml というキーで格納します。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 driver: freenas-api-iscsi httpConnection: protocol: https host: 192.168.16.234 # TrueNAS Scale の IP アドレス port: 443 apiKey: \u0026lt;TRUENAS_API_KEY\u0026gt; zfs: datasetParentName: pool-01/dataset-seichi-onp-k8s-01/volumes detachedSnapshotsDatasetParentName: pool-01/dataset-seichi-onp-k8s-01/snapshots iscsi: targetPortal: 192.168.16.234:3260 namePrefix: csi- nameSuffix: \u0026#34;-seichi-onp-k8s\u0026#34; # 複数クラスターを使う場合に区別しやすくするため付与 targetGroups: - targetGroupPortalGroup: 1 targetGroupInitiatorGroup: 2 targetGroupAuthType: None Note\nseichi_infra での秘匿情報管理について seichi_infra では GitHub Actions Repository Secret に TF_VAR_ プレフィックスで登録した値が、CI スクリプト経由で自動的に Terraform 変数として渡され、onp_cluster_secrets.tf が Kubernetes Secret を作成・更新します。詳細は cluster-boot-up/democratic-csi-sc-truenas-03.md を参照してください。\n3. TrueNAS Scale 側の事前設定 iSCSI サービスの有効化 Services から iSCSI サービスを有効化し、起動しておく必要があります。\niSCSI Portal Group iSCSI の接続受付口となる Portal Group を作成します。Shares \u0026gt; iSCSI \u0026gt; Portals から作成し、待ち受け IP アドレスとポート（デフォルト: 3260）を設定します。接続設定の targetPortal に指定するアドレスがここで設定したものになります。\nZFS データセット democratic-csi がボリュームとスナップショットを格納する ZFS データセットをあらかじめ作成しておく必要があります。Datasets から以下の2つを作成します。\nデータセット 用途 接続設定での対応キー \u0026lt;pool\u0026gt;/\u0026lt;任意のパス\u0026gt;/volumes PVC として払い出すボリュームの親 zfs.datasetParentName \u0026lt;pool\u0026gt;/\u0026lt;任意のパス\u0026gt;/snapshots VolumeSnapshot の格納先の親 zfs.detachedSnapshotsDatasetParentName democratic-csi はこれらのデータセット配下にボリュームごとの子データセットを自動で作成・削除します。親データセット自体は手動で用意しておく必要があります。\nAPI キー API キーは TrueNAS Scale WebUI の Credentials \u0026gt; API Keys から発行します。API キーは発行したユーザーの権限を継承します。democratic-csi は ZFS データセットの作成・削除（ボリューム・スナップショット用）、iSCSI ターゲット・Extent・Target-Extent 関連付けの作成・削除、iSCSI 設定の参照を TrueNAS Scale API 経由で実行するため、これらを行えるユーザーのキーが必要です。\nTrueNAS Scale のデフォルト管理者ユーザー（truenas_admin）の API キーを使えば上記すべての権限が得られます。最小権限の観点からは専用サービスアカウントを作成することが望ましいですが、TrueNAS Scale の RBAC は現時点では粒度が粗いため、管理者権限のキーを使うことが現実的な選択になりがちです。\niSCSI Initiator Group iSCSI では Initiator Group によって「どのホストからの接続を許可するか」を制御します。今回は TrueNAS Scale に Proxmox 向けの iSCSI 設定がすでに存在していたため、以下の2グループを共存させています。\nInitiator Group 用途 設定 ID: 1 Proxmox 用 Proxmox ホストの IQN を明示指定（それ以外は拒否） ID: 2 k8s 用（新設） 全許可（Initiators フィールド空欄） democratic-csi の接続設定で targetGroupInitiatorGroup: 2 を明示指定することで、democratic-csi が作成する iSCSI ターゲットは最初から Initiator Group 2 に紐付けられます。Proxmox 用のターゲットは Initiator Group 1 に紐付けられているため、ターゲットレベルで分離されており、同一の Portal Group（ID: 1、ポート 3260）を共用しながら Proxmox と k8s のストレージが互いに干渉しない構成になっています。\nk8s 用の Initiator Group を全許可にしているのは、k8s ノードの IQN が動的に変わりうることと、ノードを追加するたびに Initiator Group を更新する運用コストを避けるためです。iSCSI ターゲット名に nameSuffix: \u0026quot;-seichi-onp-k8s\u0026quot; を付与することで、k8s 用ボリュームと Proxmox 用ボリュームを名前で区別しています。\n既存の iSCSI 設定がない場合は、全許可の Initiator Group を1つ作成するだけで問題ありません。\n動作確認 以下の PVC と Pod を作成して動作を確認しました。\n1 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 28 29 30 31 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-sc-truenas-03-pvc namespace: default spec: accessModes: - ReadWriteOnce storageClassName: sc-truenas-03-iscsi resources: requests: storage: 1Gi --- apiVersion: v1 kind: Pod metadata: name: test-sc-truenas-03-pod namespace: default spec: containers: - name: test image: busybox command: [\u0026#34;sh\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;echo \u0026#39;Hello from sc-truenas-03!\u0026#39; \u0026gt; /data/test.txt \u0026amp;\u0026amp; cat /data/test.txt \u0026amp;\u0026amp; echo \u0026#39;Write test PASSED\u0026#39; \u0026amp;\u0026amp; sleep 3600\u0026#34;] volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: test-sc-truenas-03-pvc restartPolicy: Never 1 2 3 4 5 6 7 $ kubectl get pvc test-sc-truenas-03-pvc NAME STATUS VOLUME CAPACITY test-sc-truenas-03-pvc Bound pvc-c71022aa-0f2c-41a2-83c3-db43a6eb7c75 1Gi $ kubectl logs test-sc-truenas-03-pod Hello from sc-truenas-03! Write test PASSED PVC が TrueNAS Scale から動的にプロビジョニングされ、Pod からの読み書きが正常に動作することを確認しました。\nまとめ CSI ドライバー: democratic-csi v0.15.1（freenas-api-iscsi ドライバー） StorageClass: sc-truenas-03-iscsi（RWO、ext4、動的プロビジョニング） VolumeSnapshotClass: sc-truenas-03-iscsi-vsc 接続設定は existingConfigSecret で Kubernetes Secret から参照し、Helm values に秘匿情報を含めない TrueNAS Scale 上の既存 Proxmox iSCSI 設定との共存を Initiator Group の分離で実現 ","date":"2026-04-01T00:00:00Z","permalink":"http://blog.unchama.com/p/seichi-onp-k8s-%E3%81%AB-truenas-scale-iscsi-%E3%81%AE-csi-%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%83%BC%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%97%E3%81%9F/","title":"seichi-onp-k8s に TrueNAS Scale iSCSI の CSI ドライバーを導入した"}]