22 KiB
NixOSデプロイメントの課題と改善案
概要
このドキュメントは、PhotonCloudプロジェクトにおけるNixOSベースのベアメタルデプロイメントに関する現状分析、課題、および改善案をまとめたものです。
目次
現状の実装状況
実装済みの機能
A. Netboot → nixos-anywhere でのインストール経路
- netbootイメージ:
nix/images/*とbaremetal/image-builder/build-images.shで生成可能 - PXEサーバー:
chainfire/baremetal/pxe-server/assetsへのコピーまで想定済み - VMクラスタ検証:
baremetal/vm-cluster/にスクリプトが揃っている - デプロイフロー: PXE起動 → SSH接続 → disko + nixos-install(=nixos-anywhere)の流れが確立
評価: deploy-rs/colmena系よりベアメタル寄りの王道路線として成立している。
ただし: 速度はバイナリキャッシュの有無と再ビルドの頻度に大きく依存する。
B. Bootstrap ISO(phone-home → 自動パーティション → nixos-install)経路
- ISO生成:
nix/iso/plasmacloud-iso.nixに実装済み - 自動化フロー:
- Deployerへの
POST /api/v1/phone-home disko実行nixos-install --flake ...
- Deployerへの
- Deployer API:
deployer/にHTTP API実装あり(/api/v1/phone-home)
評価: 形は整っているが、本番でのゼロタッチ運用には未成熟。
C. 構成管理(NixOSモジュール + クラスタ設定生成)
- サービスモジュール:
nix/modules/に各サービスがモジュール化済み - cluster-config.json生成:
plasmacloud.cluster(nix/modules/plasmacloud-cluster.nix)で/etc/nixos/secrets/cluster-config.jsonを生成
実装済み機能 ✅
(1) トポロジ→cluster-config→first-bootの一貫したルート
- ✅
plasmacloud-cluster.nixでクラスタトポロジからcluster-config.jsonを自動生成 - ✅
environment.etc."nixos/secrets/cluster-config.json"でファイルが自動配置される - ✅
first-boot-automation.nixがcluster-config.jsonを読み込んでサービス間の接続を自動化
(2) Deployerの実運用要件
- ✅ SSH host key 生成:
LocalStorage.get_or_generate_ssh_host_key()で ED25519 鍵を生成・永続化 - ✅ TLS証明書配布:
LocalStorage.get_or_generate_tls_cert()で自己署名証明書を生成・永続化 - ✅ machine-id → node割当: pre_register API + in-memory fallback 実装済み
- ✅ ChainFire非依存:
local_state_pathがデフォルトで設定され、LocalStorage を優先使用
(3) netbootイメージの最適化
- ✅
netboot-base.nix: 超軽量インストーラ専用イメージ(サービスバイナリなし) - ✅
netboot-worker.nix: netboot-base.nix をベースに使用 - ✅
netboot-control-plane.nix: netboot-base.nix をベースに使用 - ✅ サービスバイナリは nixos-anywhere でインストール時に追加(netboot には含めない)
残りの改善点
ISOの最適化(Phase 2以降)
- ISOは
isoImage.contents = [ { source = ../../.; ... } ]で リポジトリ丸ごとISOに埋め込みになっており、変更のたびに再パック&評価対象が増えやすい - 将来的には必要なファイルのみを含めるように最適化する
課題の分析
「途方もない時間がかかる」問題の根本原因
最大のボトルネック: Rustパッケージの src = ./. が重すぎる
flake.nix のRustビルドは src = repoSrc = ./.; になっており、これにより:
docs/やbaremetal/など ビルドに無関係な変更でも全Rustパッケージが再ビルドされ得る- さらに最悪なのは、
deployer/target/のような 巨大で変動する成果物ディレクトリが混入している場合、毎回ソースハッシュが変わってキャッシュが死ぬこと - 結果:毎回「初回ビルド」に近い時間が発生
ここが直るだけで「体感の遅さ」が一段落ちる可能性が高い。
その他のボトルネック
-
netbootイメージが肥大化
- サービスバイナリや重いツールをnetbootに含めている
- initrd配布もビルドも遅くなる
-
ISOにリポジトリ全体を埋め込み
- 変更のたびにISO再ビルドが必要
- 評価対象が増える
注意:
- リモートバイナリキャッシュ(Cachix/Attic)は後回し(Phase 3で実装)
- Deployer[Bootstrapper]ではローカルNixストアのキャッシュを活用する前提
他のシステムとの比較
cloud-init との比較
cloud-initの得意領域: 既に焼いたOSイメージ(主にクラウドVM)に対して、初回起動時にユーザデータ/メタデータで「最後のひと押し」をする
このプロジェクトの得意領域: そもそもOSとサービス構成をNixで宣言し、同一の入力から同一のシステムを作る(= cloud-initより上流)
評価:
- 置き換え関係というより補完。cloud-initは「既存OSに後付けで整える」方向、NixOSは「最初からそれがOSの本体」。
- 速度面は、バイナリキャッシュがあるなら NixOSでも十分実用レンジに寄るが、キャッシュ無しだとcloud-init(既成イメージ前提)の圧勝になりがち。
Ansible との比較
Ansibleの強み: 既存の多様なOSに対して、成熟したエコシステムで「変更差分を適用」しやすい
NixOSの強み: 変更適用が「宣言→生成→スイッチ」で、ドリフト/雪片化を構造的に起こしにくい
評価:
- 同じ「構成管理」領域ではかなり戦える。特にクラスタ基盤(あなたのプロジェクトのコア)みたいに「全ノード同質で、更新頻度も高く、止められない」世界はNixが刺さりやすい。
- ただし現状だと、Ansibleが当たり前に持っている 実運用の周辺機能(インベントリ、秘密情報配布の標準手、実行ログ/監査、段階ロールアウト、失敗時の自動復旧/再試行設計)が、Nix側では自作領域になりがち。ここをDeployerで埋める設計。
OpenStack(Ironic等のベアメタル)との比較
Ironicの強み(Day0の王者):
- IPMI/Redfish等のBMCで電源制御
- PXE/iPXE、インスペクション(ハードウェア自動検出)
- クリーニング(ディスク消去)、RAID/BIOS設定
- 大規模・マルチテナント前提の運用(権限、クオータ、ネットワーク統合)
このプロジェクトの現状:
- PXE/Netboot・ISO・disko・nixos-install・first-boot は揃っている
- でも BMC連携/インスペクション/クリーニング/多数ノードの状態機械は薄い(Deployerがその芽)
評価:
- Ironicの「同じ土俵」ではまだ厳しい(特に「台数が増えた時に壊れない運用」)。
- 逆に言うと、Ironicが重い/過剰な環境(単一DC・少〜中規模・同一HW寄り・「クラウド基盤自体をNixOSでガチガチに固めたい」)では、NixOS方式は運用コストと一貫性で勝ち筋がある。
実務的な勝ち筋:
- 小〜中規模はNixOS主導で十分戦える(ただしキャッシュ導入と、ビルド入力の安定化が必須)。
- 大規模/多拠点/多機種/マルチテナントのDay0は、Ironic相当の機能をどこかで用意する必要がある。
- 現実解は「Day0はIronicや既存のプロビジョナに寄せて、Day1/Day2をNixOSで統一」が強い。
スケーリングの課題
10,000台規模での問題点
1. Deployerサーバーが単一インスタンス前提
axum::serve(listener, app)で単一HTTPサーバーとして動作- 10,000台が同時にPhone Homeすると、単一プロセスが全リクエストを処理する必要がある
- CPU/メモリ/ネットワークI/Oがボトルネック
2. 状態管理はChainFireで分散可能だが、Deployer側の調整がない
- ChainFireはRaftベースで分散可能
- しかし、Deployerインスタンス間の調整(リーダー選出、ジョブ分散、ロック)がない
- 複数Deployerを起動しても、同じジョブを重複実行する可能性
3. デプロイジョブの管理がない
- Phone Homeはあるが、「nixos-anywhereを実行する」ジョブの管理がない
- 10,000台を順次デプロイする場合、キューイング/並列制御/リトライが必要
他のシステムとの比較(スケーリング設計)
OpenStack Ironic
API層: 複数インスタンス + ロードバランサー
ワーカー層: 複数conductorで並列処理
状態管理: PostgreSQL(共有DB)
ジョブキュー: RabbitMQ(分散キュー)
Ansible Tower
Web層: 複数インスタンス
ワーカー層: Celery workers(スケーラブル)
状態管理: PostgreSQL
ジョブキュー: Redis
Kubernetes Controller
コントローラー層: 複数インスタンス + Leader Election
状態管理: etcd
並列処理: ワーカーPodで分散
10,000台規模での性能見積もり
現状(単一インスタンス):
- Phone Home: 10,000リクエスト ÷ 1サーバー = 10,000リクエスト/サーバー
- デプロイ: 順次実行 = 10,000台 ÷ 1ワーカー = 非常に遅い
改善後(API層10台 + ワーカー100台):
- Phone Home: 10,000リクエスト ÷ 10サーバー = 1,000リクエスト/サーバー(10倍高速化)
- デプロイ: 10,000台 ÷ 100ワーカー = 100台/ワーカー(並列実行で大幅短縮)
例: 1台あたり10分かかる場合
- 現状: 10,000台 × 10分 = 100,000分(約69日)
- 改善後: 100台/ワーカー × 10分 = 1,000分(約17時間)
改善案
Deployer[Bootstrapper]の位置づけ
現状のDeployer実装は Deployer[Bootstrapper] として位置づけ、以下の前提で設計する:
- 実行環境: 仮設マシンや手元のマシン(deploy-rsのように)
- 役割: 0→1の初期デプロイ(クラスタの最初の数台)
- 独立性: 他のソフトウェア(ChainFire、FlareDB等)から完全に独立している必要がある
- キャッシュ前提: 手元/仮設マシンにはNixストアのキャッシュがあるため、リビルドは多くないはず
将来の移行: ある程度デプロイが進んだら、完全に自動なデプロイ環境(キャッシュ実装済み、ISOはオブジェクトストレージ、スケーラブル)に移行する。ただし、この完全自動デプロイ環境の実装は他のソフトウェアが安定してからにしたい。
(将来)リモートflake化 + バイナリキャッシュ(Phase 3以降)
目的: ビルド時間を大幅に短縮(完全自動デプロイ環境用)
実装内容(Phase 3で実装):
- リモートにflakeを置く(GitHub等)
- 注意: 現在のコードベースは大胆に変更される可能性があるため、GitHubへの公開は後回し
- バイナリキャッシュを用意(Cachix、セルフホストならattic等)
flake.nixのnixConfigと、nix/images/netboot-base.nix/ 各ノード設定に substituters/trusted-public-keys を入れて、netboot/ISO/インストール時のnixが自動でキャッシュを引くようにする
効果: nixos-anywhere の実体が「ビルド」から「ダウンロード」に変わる。
優先度: Phase 3以降(完全自動デプロイ環境の実装時)。Deployer[Bootstrapper]ではローカルで動くことを優先し、キャッシュ系は後回し。
P0: src = ./. をやめ、ソースをフィルタする ✅ 実装済み
目的: 無関係な変更で再ビルドが発生しないようにする
実装内容 (flake.nix の repoSrc):
repoSrc = pkgs.lib.cleanSourceWith {
src = ./.;
filter = path: type:
! (dropPrefix [ "docs/" "baremetal/" ".git/" ".cccc/" "result" "result-" ] ||
base == "target" ||
dropSuffix [ ".qcow2" ".img" ".iso" ".qcow" ]);
};
除外されるファイル/ディレクトリ:
- ✅
**/target/(Cargoビルド成果物) - ✅
docs/,baremetal/(Rustビルドに不要) - ✅
.git/,.cccc/,result*(Nix成果物) - ✅
.qcow2,.img,.iso,.qcow(大きなバイナリファイル)
効果: ソース変更がなければNixのキャッシュが効き、再ビルドを回避。
P1: netbootは「最小のインストーラ」に寄せる ✅ 実装済み
目的: netbootイメージのサイズとビルド時間を削減
実装内容 (nix/images/netboot-base.nix):
- ✅
netboot-base.nix: 最小限のインストーラツールのみ(disko, parted, curl, jq等) - ✅ サービスバイナリや仮想化ツールは含めない
- 役割:netbootは「SSHで入れてnixos-anywhereできる」だけに絞る
- サービスは インストール後のNixOS構成で入れる方が速く・安全
効果: initrd配布もビルドも速くなる。
P1: トポロジ生成とfirst-bootの接続を完成させる ✅ 実装済み
目的: 構成管理の運用ループを完成させる
実装内容:
- ✅
plasmacloud-cluster.nix: クラスタトポロジ定義とcluster-config.jsonの自動生成 - ✅
first-boot-automation.nix: cluster-config.json を読み込んでChainfire/FlareDB/IAMへの自動接続 - ✅
environment.etc."nixos/secrets/cluster-config.json"でファイル配置
効果: 「構成管理」が「運用の自動化」に直結する。
P2: ISOルートは「本番のゼロタッチ」に必要な要件を埋める(Phase 2以降)
目的: ISOベースの自動デプロイを本番対応にする
実装内容:
- ✅ Deployerの鍵・証明書生成は実装済み(
LocalStorage.get_or_generate_*) - TODO: ISO内で disko を同梱してローカル実行に寄せる(現状はネットワーク依存)
P1: Deployer[Bootstrapper]の独立性確保 ✅ 実装済み
目的: 他のソフトウェア(ChainFire、FlareDB等)に依存しない独立したデプロイツールにする
実装内容 (deployer/crates/deployer-server/):
- ✅
LocalStorage: ローカルファイルベースのストレージ(ChainFire不要) - ✅
config.local_state_path: デフォルトで/var/lib/deployer/stateに設定 - ✅
state.init_storage():local_state_pathがあれば LocalStorage を優先使用 - ✅ Phone Home API: 簡易HTTPサーバーとして動作(ChainFire不要)
- ✅ SSH host key / TLS証明書: LocalStorage で永続化
効果: ChainFire等が動いていなくても、Deployer[Bootstrapper]だけでデプロイが可能。
将来: Phase 3 で ChainFire との統合を実装(大規模デプロイ用)。
(将来)完全自動デプロイ環境の設計
目的: 大規模デプロイ(10,000台規模)に対応した、完全に自動化されたデプロイ環境
実装内容(Phase 3で実装):
- API層のStateless化: Phone Homeリクエストを複数APIサーバーで分散処理
- ワーカー層の追加: デプロイジョブを並列実行(ChainFireベースのジョブキュー)
- ISOのオブジェクトストレージ配布: LightningStor等にISOを保存し、高速配布
- バイナリキャッシュの完全実装: すべてのビルド成果物をキャッシュ
効果: マシンをいくら増やしても高速でデプロイできる。
前提条件: 他のソフトウェア(ChainFire、FlareDB、LightningStor等)が安定してから実装する。
優先度とロードマップ
Phase 1: Deployer[Bootstrapper]の改善 ✅ 完了
目標: 0→1の初期デプロイを高速化・安定化(ローカルで動くことを優先)
- ✅
srcフィルタリング(target/やdocs/を除外)flake.nixのrepoSrcで実装済み- ソース変更がなければ、Nixのキャッシュが効き、Cargoの再ビルドも避けられる
- ✅ Deployer[Bootstrapper]の独立性確保
LocalStorageでChainFire非依存local_state_pathがデフォルトで設定
- ✅ netbootイメージの最小化(サービスバイナリを除外)
netboot-base.nixを最適化netboot-worker.nix,netboot-control-plane.nixが netboot-base をベースに使用
- ✅ トポロジ→first-boot接続
plasmacloud-cluster.nixでクラスタトポロジ定義と cluster-config.json を自動生成first-boot-automation.nixでサービス間の自動接続
- ✅ SSH/TLS鍵生成
phone_home.rsで ED25519 鍵と自己署名証明書を生成・永続化
達成効果:
- Deployer[Bootstrapper]が他のソフトウェアから独立し、安定して動作
- ソース変更がなければ、ビルド時間が大幅に短縮
- Cachix/Attic連携なしでもローカルで動作
実行環境: 手元/仮設マシン(Nixストアのキャッシュがある前提)
Phase 2: 他のソフトウェアの安定化(数ヶ月)
目標: ChainFire、FlareDB、IAM等のコアサービスの安定化
- コアサービスの機能完成
- クラスタ運用の安定化
- 監視・ログ・バックアップ等の運用基盤の整備
期待効果: 完全自動デプロイ環境を構築する基盤が整う
Phase 3: 完全自動デプロイ環境の実装(将来、Phase 2完了後)
目標: 大規模デプロイ(10,000台規模)に対応した、完全に自動化されたデプロイ環境
- リモートflake化 + バイナリキャッシュ導入(Cachix/attic)
- GitHub等への公開(コードベースが安定してから)
- Cachix/Attic連携によるバイナリキャッシュ
- API層のStateless化 + ワーカー層の追加
- ジョブキューの実装(ChainFireベース)
- ISOのオブジェクトストレージ配布(LightningStor等)
- Deployerの鍵・証明書・インベントリ管理の実装
期待効果:
- マシンをいくら増やしても高速でデプロイできる
- 完全に自動化されたゼロタッチデプロイが可能
前提条件:
- Phase 2(他のソフトウェアの安定化)が完了していること
- コードベースが安定し、GitHub等への公開が可能になったこと
まとめ
現状の評価
このプロジェクトは、NixOSベースのベアメタル配備に必要な部品がすべて揃い、Phase 1が完了している:
✅ Phase 1 完了項目
- Deployer[Bootstrapper]の独立性: LocalStorage でChainFire非依存
- キャッシュ効率化:
repoSrcフィルタリングで不要ファイルを除外 - netboot最小化:
netboot-base.nixでインストーラ専用イメージ - トポロジ→first-boot接続: cluster-config.json 自動生成
- SSH/TLS鍵生成: ED25519 鍵と自己署名証明書の生成・永続化
残りの課題
- 完全自動デプロイ環境の未実装: 大規模デプロイに対応するための基盤(Phase 3で実装)
段階的なアプローチ
Phase 1 ✅ 完了: Deployer[Bootstrapper]の改善
- 0→1の初期デプロイを高速化・安定化
- 他のソフトウェアから独立
- 手元/仮設マシンで実行可能
Phase 2(現在): 他のソフトウェアの安定化
- ChainFire、FlareDB、IAM等のコアサービスの安定化
- クラスタ運用の確立
Phase 3(将来): 完全自動デプロイ環境の実装
- 大規模デプロイ(10,000台規模)に対応
- 完全に自動化されたゼロタッチデプロイ
- Phase 2完了後に実装
達成済みの成功条件
- ✅ Deployer[Bootstrapper]の独立性: 他のソフトウェアが動いていなくても、デプロイが可能
- ✅ ローカルでの動作優先: Cachix/Attic連携なしでも、ローカルNixストアのキャッシュで動作
- ✅ キャッシュの効率化:
srcフィルタリングで、ソース変更がなければNixのキャッシュが効く - ✅ トポロジ→first-boot接続: plasmacloud-cluster.nix からの設定生成が機能
- ✅ SSH/TLS鍵の永続化: LocalStorage で鍵を永続化
次のステップ
Phase 1を最優先で実装✅ 完了- Phase 2で他のソフトウェアを安定化(基盤の確立)
- Phase 3で完全自動デプロイ環境を実装(大規模対応)
- コードベースが安定してから、リモートflake化とバイナリキャッシュを実装
Phase 1が完了し、0→1のデプロイが可能になった。次はPhase 2でコアサービスの安定化を進める。