Entry / Article

Dockerを使うHerdr基盤でLXCではなくVMを選んだ理由

Proxmox VE上にHerdrとCodexの実行環境を用意する際、LXCとVMを比較しました。Dockerを扱うAI Agentの権限と障害範囲を考え、Ubuntu Server VMへ分離した判断をまとめます。

Proxmox VE(PVE)上に、HerdrでCodexを常時動かす環境を作りました。最初に迷ったのは、軽量なLXCコンテナと仮想マシン(VM)のどちらへ載せるかです。

HerdrだけならLXCでも動かせそうです。しかし、私の開発ではDockerイメージを作り、コンテナを起動して実動作を確かめます。AI AgentにDockerを操作させるなら、わずかなリソース差よりも、PVEホストとの境界が明快なことを優先した方がよいと判断しました。選んだUbuntu Server VMには、GPUやGUIを持たせません。

この記事は3本構成の1本目です。ここでは隔離方式の判断を扱い、2本目でHerdrとCodexの疎通、3本目で常時稼働と権限管理へ進みます。

HerdrよりもAgentが実行する処理を基準にする

Herdrは、CodexなどのコーディングAgentが動くターミナルを管理するランタイムです。SSH接続が切れてもバックグラウンドサーバーがターミナルを保持し、Agentの状態を一覧できます。

Herdr自体にGPUは要りません。CPUとメモリを消費するのは、Agentが実行するビルドやテストです。たとえば複数のAgentが同時に次の処理を始めれば、負荷の中心は開発ツール側へ移ります。

bash
docker build -t app:test .
docker compose up -d
pnpm test
pytest

なので、配置先は「Herdrが起動するか」だけで決められません。Agentへどこまでコマンド実行を許すか、その環境が壊れたときに影響をどこで止めたいかが判断軸になります。

LXC内のDockerは隔離の説明が難しくなる

PVEのLXCはホストとLinuxカーネルを共有します。Proxmox VEの公式資料は、最大限の隔離やlive migrationが必要な場合、アプリケーションコンテナをQEMU VM内で動かす構成を推奨しています。

unprivileged LXCの中でDockerを動かす方法はあります。ただし、コンテナの入れ子や権限制御、ストレージ、セキュリティ設定など、通常のLinux VMにはない調整箇所が増えます。Dockerを動かしやすくするためにprivileged LXCへ寄せれば、今度はコンテナ内のrootとPVEホストの距離が近くなります。

さらに、Dockerデーモンのソケットへアクセスできるユーザーは、そのゲスト環境内で強い権限を持ちます。任意のシェルコマンドを実行するAgentへDocker操作を許すなら、この権限を小さく見積もるべきではありません。

今回避けたかったのは、次の構成です。

text
PVEホスト
└─ privileged LXC
   ├─ Herdr
   ├─ Codex
   └─ Docker socket

構築はできますが、「Agentが誤った操作をしたとき、どこまで影響するか」を簡潔に説明しにくくなります。

壊れても作り直せるVMにする

採用した構成は次のとおりです。

text
PVEホスト
├─ Ubuntu Server VM
│  ├─ Herdr
│  ├─ Codex
│  ├─ Git worktree
│  └─ Docker
└─ その他のVM・LXC

VMのゲストOSは、PVEホストとカーネルを共有しません。AgentがDockerを使っても、影響はまずVMの境界で止められます。

ネットワークの到達先とVM内の認証情報も絞ります。必要なデータはGit、SSH、API経由で取得する方針にしました。

検討時の初期案は4 vCPU、メモリ8GB、ディスク64〜128GBでした。ただし、これは性能測定から導いた値ではありません。同時に走らせるビルドやテストを見ながら増減するための出発点です。

GPUとGUIは割り当てません。ローカルLLMを動かす場合は推論用VMを別にすると、それぞれの役割を保てます。

worktreeとCompose project名も分ける

VMはPVEホストとの境界にはなりますが、VM内部のAgent同士の衝突までは防ぎません。Git worktreeで作業ツリーを分け、Docker Composeを使う場合はproject名もAgentごとに変える想定です。

bash
docker compose -p backend-agent up -d
docker compose -p frontend-agent up -d

これでコンテナ名やネットワーク名の衝突を避けやすくなります。ただし、固定したcontainer_name、同じhost portやbind mount、明示的に共有するnetworkやvolumeは別途衝突します。同じDockerデーモンを共有する事実も変わりません。より強い分離が必要になった時点で、Agentごとの実行環境をコンテナや別VMへ分けようと思います。

軽さより障害範囲を選んだ

Dockerを使わず、Gitや言語処理系のCLIだけを動かすなら、unprivileged LXCを選んでいました。

今回はAgentがDockerイメージを作り、任意の開発コマンドを実行します。この条件では、LXCの軽さより「被害範囲はこのVMまで」と言いやすい構成に価値がありました。Herdr VMを消耗品として再構築できるようにし、重要なサービスと分離する。これがPVE上でAI Agentを常駐させる際の出発点になりました。