EPYC 9175F + Podman によるローカル開発基盤の設計と構築
EPYC 9175F + RTX PRO 6000 MAX-Q のCompute Server、Mac Studio、Storage Serverの3台構成でローカル開発基盤を設計・構築した全記録。CPU帯分離、SATA/NVMe階層化、LUKS暗号化、UID/GID戦略、rootless Podmanディレクトリ配置まで。
結論
3台のマシンとRouterOS配下の10GbEネットワークで、LLM推論、データ基盤、開発環境、監視基盤を同居させても衝突しないローカル開発基盤を構築した。設計の核は「CPU帯を先に切る」「SATA/NVMeの役割を分ける」「ディレクトリ配置を3層に整理する」「UID/GIDを固定する」の4点で、これらを先に固定したことで、サービスが増えても配置ルールが崩れない土台になった。
この設計の上に、Go + NATS + Dagster によるAIオーケストレーション基盤のdevstack(podman-compose)が稼働している。
ハードウェア構成
Compute Server (Ubuntu 24.04.3 LTS)
CPU: AMD EPYC 9175F (第5世代 Turin) 16core 4.2GHz-5GHz L3 cache 512MB
GPU: Nvidia RTX PRO 6000 MAX-Q (300W) 96GB (Blackwell世代)
RAM: DDR5-6400 64GB x 12 (768GB)
MB: Supermicro H13SSL-NT
Storage1: SATA 6Gbps 3.84TB
Storage2: M.2 PCIe4.0 3.84TB
PSU: 1500W (80Plus)
NIC: 10GbE x 2 (en0: 10.10.10.4, en1: disabled)
Desktop PC (macOS latest)
Mac Studio M1 Ultra
CPU: M1 Ultra
RAM: 64GB
Storage: 1TB
NIC: 10GbE x 1 (10.10.10.2)
Wi-Fi: disabled
Storage Server (Ubuntu 24.04.3 LTS)
Mac mini late 2018
CPU: Core i3
RAM: DDR4 SO-DIMM 8GB
Storage: SSD 2TB, ext M.2 SSD 4TB, SATA HDD 24TB
NIC: 1GbE, ext 10GbE (RTL8159, 10.10.10.3)
Wi-Fi: disabled
ネットワーク
RouterOS (CRS304) 配下で全ホストを10GbEで接続。
VDSL -> Router (RouterOS)
Wi-Fi 7 -> mobile SSID, IoT SSID
DHCP Server
| -> CRS304 (Router+Switch)
|-- Port1 → Desktop PC 10.10.10.2
|-- Port2 → Storage Server 10.10.10.3
|-- Port3 → Compute Server 10.10.10.4
|-- Port4 → Wi-Fi 6 AP 128.0.0.1 (DHCP Server)
|-- Port5 → WAN 192.168.0.2/24 (1GbE DHCP Client)
| ホスト | 役割 | 稼働 |
|---|---|---|
| Compute Server | GPU推論 + データ基盤 + ワークフロー | 作業時のみ |
| Desktop PC | Gateway + メッセージング + UI | 作業時のみ |
| Storage Server | Observability + オブジェクトストレージ | 24/7 |
Desktop PCとCompute Serverは同時に起動・停止する前提。Storage Serverだけが常時稼働し、Prometheus/Grafana/Lokiで全体を監視する。
ディスク設計
設計方針
SATA側を「OS + 設定 + 安定シーケンシャル書込み」、NVMe側を「高速ランダムI/O + 実データ」として明確に分離する。
SATA 3.84TB (ブートディスク)
LUKS → LVM → ext4。LVMを噛ませることで、rootとログ領域を運用しながら段階的に拡張できる。
| LV | サイズ | 用途 |
|---|---|---|
| / | 2048GB | OS + アプリ定義 (/opt含む) |
| /var/log | 32GB | システムログ (Loki/promtail用に分離) |
| VG Free | ~1.8TB | Snapshot / 将来拡張 |
/var/log を最初から分けておくことで、ログ肥大でroot領域を巻き込む事故を防ぐ。
NVMe (M.2) 3.84TB
LUKS → xfs (LVMなし)。/home に全振りし、サービスごとの実データをここに集約する。
/home/ksh3/
+-- postgres/data # DB本体 (WAL含む)
+-- trino/{spill,exchange,cache}
+-- prometheus/data # TSDB
+-- loki/{data,cache}
+-- qdrant/data # ベクトルインデックス
+-- models/ # LLMモデル群 (vLLM/llama.cpp/ollama)
+-- dagster/{runs,storage,tmp}
+-- workspace/ # VSCode clone repos
+-- obsidian/
暗号化と起動時解除
- SATA: Dropbear-initramfs でリモートSSH LUKS解除
- M.2:
/etc/crypttabの鍵ファイルで、root解除後に自動解除
# /etc/crypttab
dm_crypt-0 UUID=... none luks,discard,initramfs
home-crypt UUID=... /etc/luks-keys/home.key luks
サーバーが手元にない状況でもrootを開けられ、/homeは手動操作不要。
ディレクトリ配置: 3層分離
原則
| 層 | パス | 内容 | ディスク |
|---|---|---|---|
| 定義 | /opt/containers/{app}/ | compose.yml, .env, secrets, systemdテンプレート | SATA |
| 本番データ | /srv/ | gitリポジトリ, Icebergウェアハウス, メディア | SATA |
| 実行時データ | /home/ksh3/{app}/ | DB本体, spill, cache, モデル, ビルド成果物 | NVMe |
/opt/containers (SATA)
/opt/containers/
+-- common/
| +-- networks/create_networks.sh
| +-- systemd/[email protected]
| +-- env/ # 共有.env
+-- postgres/
| +-- compose.yml, conf/, .env, secrets/
+-- trino/
+-- nessie/
+-- prometheus/
+-- loki/
+-- dagster/
+-- vllm/
+-- qdrant/
/srv (SATA)
/srv/
+-- git/bare/ # git --bare init
+-- iceberg/warehouse # データレイク本体
+-- nessie/data # RocksDBストア
+-- media/{raw,derived,thumbs,archive}
NVMe の透過的利用
I/O負荷の高いデータは NVMe 上に配置するが、/etc/fstab の bind マウントで見かけ上のパスを統一する。
# /etc/fstab
/mnt/nvme/postgresql/data /home/postgres/data none bind 0 0
バックアップツールからは /home 配下をスキャンするだけで全データを捕捉できる。
UID/GID 戦略
原則
1サービスにつき1つの専用 UID/GID をホスト側に作成し、コンテナ内 UID と一致させる。
- 予約レンジ: 2001-2999(コンテナアプリ専用)
- 基盤系 (2001-2099): Caddy, Registry, Monitoring
- データ系 (2101-2199): Trino, MinIO, Postgres (公式イメージ UID 999)
- AI/LLM系 (2301-2399): vLLM, llama.cpp
設定例
# Trino ユーザー作成
groupadd -g 2101 trino && useradd -r -u 2101 -g 2101 -m -d /home/trino -s /usr/sbin/nologin trino
install -d -o 2101 -g 2101 -m 0750 /home/trino/{data,logs,scratch}
Podman での明示指定
services:
trino:
image: trinodb/trino:443
user: "2101:2101"
volumes:
- /opt/trino/etc:/etc/trino:ro # 設定は /opt から readonly
- /home/trino/data:/var/trino/data # データは /home へ
- /home/trino/logs:/var/log/trino
- /home/trino/scratch:/var/trino/scratch
tmpfs:
- /tmp:rw,nosuid,nodev,relatime,size=8g
CPU 帯設計
9175F は16コア高クロック。コア固定 + クォータの併用でレイテンシ安定と暴走抑止を両立する。
帯域分離
| プール | コア帯 | 用途 |
|---|---|---|
| 常駐 (apps) | CPU 0-7 | DB, IDE, LLM, 監視 |
| バッチ (jobs) | CPU 8-15 | ELT, 分析, バックアップ |
| 軽量常駐 | CPU 6-7 | exporter群 |
常駐サービス
| サービス | cpuset | cpus | メモリ上限 | 補足 |
|---|---|---|---|---|
| 監視レーン | 1 | 0.5-1.0 | 256MB-2GB | Prometheus/Loki は SATA推奨 |
| PostgreSQL | 2-3 (バースト 2-4) | 2.0 (3.0) | 32-64GB | |
| PgBouncer | 2 | 0.5 | 512MB | 接続圧縮 |
| VSCode Server | 3-4 | 3.0-4.0 | 24-48GB | 体感優先 |
| Ollama | 5-6 | 1.0-2.0 | 16GB + GPU | 短リク優先 |
| Trino | 4-16 | 6-12.0 | 128GB (256GB) | heavy は同時1本 |
| Dagster | 4-16 | 1.0-2.0 | 2-4GB | 実処理は worker |
oneshot ジョブ
| ジョブ | cpuset | cpus | メモリ上限 |
|---|---|---|---|
| DataFusion Worker | 4-16 | 4-8.0 | 64-256GB |
| dbt run | 4-16 | 6-12.0 | 8-32GB |
| PG Load (120GB級) | 2-3 | - | PG: 64GB |
| Iceberg メンテ | 4-16 | 2-6.0 | 8-32GB |
| VACUUM / REINDEX | 2-3 | 2.0 | 2-4GB |
起動パラメータ例
# PostgreSQL (常用)
podman run -d --name pg \
--cpuset-cpus=2-3 --cpus=2.0 --cpu-weight=900 \
--memory=48g -v /opt/postgres:/var/lib/postgresql/data postgres:18
# PostgreSQL (重い日)
podman update --cpuset-cpus=2-4 --cpus=3.0 --memory=64g pg
サービス配置
Linux Server (Backend, Data Engine)
+-- PostgreSQL 18 + pgvector (JIT有効)
+-- Dagster (daemon + sensor + user-code gRPC)
+-- NATS 2.11 (JetStream) ← Desktop PCと同居
+-- multi-bert-inference (Rust + ONNX Runtime)
+-- vLLM / llama.cpp / LM Studio
+-- Vector 0.45 (Rust)
+-- Trino + Nessie (Phase 2, Lakehouse)
+-- Podman rootless
Mac Studio (Frontend, UI Hub)
+-- agent-gateway (Go/Gin)
+-- VSCode.app (Remote-SSH → Linux)
+-- Grafana UI
+-- Dagit UI
+-- Web Browser (Trino Web UI, etc.)
Storage Server (24/7 Observability)
+-- Prometheus
+-- Grafana
+-- Loki
+-- Vector (aggregator)
+-- MinIO
運用ルール
バックアップ
- NAS: 毎日
tar.gz、14世代- 対象:
/home/ksh3/,/srv/iceberg/,/srv/nessie/,/srv/media/, PGバックアップ - 除外:
*/{tmp,cache,spill,exchange}
- 対象:
- R2: 必要に応じて成果物を
rclone sync
掃除
find /home/ksh3/*/cache -type f -mtime +14 -delete
find /home/ksh3/*/tmp -type f -mtime +7 -delete
監視
- Prometheus node_exporter の textfile collector で
/home/ksh3/*/の使用量を監視 pg_stat_activity,wait_event, PSI, Trino query memory / spill を確認- correlation_id 付きの構造化ログで全経路トレーシング
OS 初期化
Ubuntu Server 24.04.3 LTS (minimized) をベースに、以下の依存パッケージを最初から投入。
sudo apt install -y --no-install-recommends \
build-essential curl wget git unzip vim less man-db \
net-tools iputils-ping nmap iperf3 mtr \
htop btop nvtop iftop logrotate rclone \
fzf ripgrep bat \
openssh-server dropbear-initramfs libfido2-1 libu2f-udev fido2-tools \
nvidia-headless-580 nvidia-utils-580 \
podman podman-compose \
dbus-user-session \
locales fonts-noto-cjk fonts-noto-cjk-extra
GPU、コンテナ運用、リモート接続、日本語フォント、CLIツールを一気に揃える。dbus-user-session は rootless Podman と user service の前提。
注意事項
/homeを M.2 に全振りしているため、NVMe 故障時はサービスデータが全滅する。バックアップの世代管理が生命線- rootless Pod では
userns/slirp4netnsを使うため、ホストポート 1024未満のバインドは別途設定が必要 - PostgreSQL 18 の JIT 有効化は
shm_size: 4gbの設定が前提。不足すると OOM kill のリスクがある - CPU ピン留めを YAML と CLI のどちらで持つかは運用ルールとして統一しておかないと、再起動後に設定が消える
最終構成
この設計の上に構築された実行基盤の詳細は以下を参照。
- Go + NATS + Dagster AIオーケストレーション基盤 — devstackコンテナ構成、3ホストトポロジー、NATSイベント設計
