بنقرة واحدة
linstor-bench
LINSTOR/DRBD ストレージベンチマーク。VM上でfioを実行し、thin/thick-stripe構成の性能を計測する。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
LINSTOR/DRBD ストレージベンチマーク。VM上でfioを実行し、thin/thick-stripe構成の性能を計測する。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
別の tmux ペインで動いている Claude Code インスタンスを、このセッションから tmux send-keys / capture-pane で駆動するオーケストレーション。プランモード投入 → プラン再点検 (1 回目の検証) → ユーザ承認ゲート → auto mode 実装 → レポート未記載追記 → レポート矛盾再チェック (2 回目の検証) の定型フロー。長時間タスクをもう 1 体の claude に「ダブルチェック付きで」実行させ、要所だけ自分が監督したいときに使う。
PXE/netboot 経由で Debian + HW RAID10 を自動インストール (OpenWrt ローカル TFTP + embed ipxe.efi + IPv4 リテラル mirror)。BMC USB redirector が累積劣化した機種 (TX1320 M3 等) や、 cross-site 拠点の install に使う。phase 19 で training-tx1320 で完遂、 物理操作なしで自力 SSH まで到達。
Fujitsu iRMC S4 (TX1320 M3 等) の電源・BIOS・RAID 操作 + BIOS 設定リファレンス。Redfish (HTTPS + SECLEVEL=0) 経由の電源/ブート操作 + iRMC Web UI/KVM 経由の手動 BIOS/RAID 操作手順 + 全 BIOS 設定項目の網羅リファレンス (bios/)。training-tx1320 対応。
Debian + Proxmox VE OS自動セットアップ。BMC VirtualMedia経由でpreseedインストール、PVEインストールまでを実行する。
Supermicro X11DPU/X10DRT-P BIOS Setup 操作。KVM スクリーンショット + キーストロークで BIOS メニューを操作する。BIOS 設定変更、確認、保存を行う。4-6号機 + 10号機対応。
iDRAC7 基本操作。SSH 経由で racadm コマンドを実行し、サーバ管理を行う。
استنادا إلى تصنيف SOC المهني
| name | linstor-bench |
| description | LINSTOR/DRBD ストレージベンチマーク。VM上でfioを実行し、thin/thick-stripe構成の性能を計測する。 |
| argument-hint | <storage_type: thin|thick-stripe> [region: region-a|region-b] |
LINSTOR/DRBD ストレージ上に VM を作成し、fio でベンチマークを実行する。
引数 storage_type で thin (LVM thin pool) または thick-stripe (LVM 4本ストライプ) を選択する。
オプションで region を指定して特定リージョンでベンチマークを実行する。
| リージョン | ノード | DRBD通信 | 特徴 |
|---|---|---|---|
| region-a | 4号機 + 5号機 + 6号機 | IPoIB 192.168.100.x | InfiniBand 高速通信 |
| region-b | 7号機 + 8号機 + 9号機 | IPoIB 192.168.100.x | InfiniBand 高速通信 |
region 指定時は、そのリージョンの最初のノードに VM を配置し、同リージョン内の2ノードで DRBD レプリケーションを行う。
config/linstor.yml の regions セクションからノード構成を読み取る。
pvecm status で Quorate)linstor node list で全ノード Online)ssh -F ssh/config pve$N "ip link show type ipoib" で ibp* が UP であることscp + ssh ib-setup-remote.sh --persist で設定 (詳細は linstor-node-ops スキル N7 参照)config/linstor.yml の benchmark.cloud_image)ssh-pwauth.yml) が PVE ホスト上に作成済み (後述)config/linstor.yml の benchmark: セクションが設定済み| スクリプト | 用途 |
|---|---|
./scripts/linstor-bench-preflight.sh | ディスク SMART ヘルスチェック (SCP で PVE ノードに転送して実行) |
YQ="${PROJECT_DIR}/bin/yq"
CONFIG="config/linstor.yml"
# リージョン指定時のノード選択
# region-a: nodes[0]=4号機, nodes[1]=5号機
# region-b: nodes[2]=6号機, nodes[3]=7号機
# リージョン未指定時はデフォルトで region-a (nodes[0], nodes[1])
REGION="${2:-region-a}" # 第2引数
if [ "$REGION" = "region-b" ]; then
NODE1=$("$YQ" '.regions.region-b[0]' "$CONFIG")
NODE2=$("$YQ" '.regions.region-b[1]' "$CONFIG")
else
NODE1=$("$YQ" '.regions.region-a[0]' "$CONFIG")
NODE2=$("$YQ" '.regions.region-a[1]' "$CONFIG")
fi
# ノード名からIPを取得
NODE1_IP=$("$YQ" ".nodes[] | select(.name == \"$NODE1\") | .ip" "$CONFIG")
NODE2_IP=$("$YQ" ".nodes[] | select(.name == \"$NODE2\") | .ip" "$CONFIG")
DISKS=$("$YQ" ".nodes[] | select(.name == \"$NODE1\") | .storage_disks[]" "$CONFIG")
# ストレージ
VG_NAME=$("$YQ" '.vg_name' "$CONFIG")
RG_NAME=$("$YQ" '.resource_group' "$CONFIG")
PLACE_COUNT=$("$YQ" '.place_count' "$CONFIG")
# ベンチマーク
VM_ID=$("$YQ" '.benchmark.vm_id' "$CONFIG")
VM_NAME=$("$YQ" '.benchmark.vm_name' "$CONFIG")
VM_CORES=$("$YQ" '.benchmark.vm_cores' "$CONFIG")
VM_MEMORY=$("$YQ" '.benchmark.vm_memory' "$CONFIG")
VM_DISK_SIZE=$("$YQ" '.benchmark.vm_disk_size' "$CONFIG")
VM_BRIDGE=$("$YQ" '.benchmark.vm_bridge' "$CONFIG")
VM_MGMT_BRIDGE=$("$YQ" '.benchmark.vm_mgmt_bridge' "$CONFIG")
VM_MGMT_IP=$("$YQ" '.benchmark.vm_mgmt_ip' "$CONFIG")
VM_MGMT_PREFIX=$("$YQ" '.benchmark.vm_mgmt_prefix' "$CONFIG")
VM_USER=$("$YQ" '.benchmark.vm_user' "$CONFIG")
VM_PASS=$("$YQ" '.benchmark.vm_password' "$CONFIG")
CLOUD_IMAGE=$("$YQ" '.benchmark.cloud_image' "$CONFIG")
FIO_RUNTIME=$("$YQ" '.benchmark.fio_runtime' "$CONFIG")
FIO_SIZE=$("$YQ" '.benchmark.fio_size' "$CONFIG")
FIO_SEQ_SIZE=$("$YQ" '.benchmark.fio_seq_size' "$CONFIG")
前回のベンチマーク実行で発生した失敗パターン。各フェーズでこの対策を適用する。
| ID | 失敗 | フェーズ | 検出方法 | 対策 |
|---|---|---|---|---|
| F1 | ディスク不良セクタ | Phase 0 | SMART Current_Pending_Sector > 0 | ゼロ書きで強制再割り当て |
| F2 | LINSTOR リソース名が vm-100-disk-0 ではなく pm-XXXX | Phase 3 | qm importdisk 出力に pm-XXXX と表示 | importdisk 出力をパースしてリソース名を取得 |
| F3 | cloud-init SSH パスワード認証無効 | Phase 3 | Permission denied (publickey) | vendor snippet ssh_pwauth: true + --cicustom |
| F4 | VM MAC アドレス不一致で IP 検出失敗 | Phase 4 | IP 検出タイムアウト | 管理用 IP (10.x) が config で設定済みのため不要。DHCP のみの場合は qm config から MAC を動的取得 |
| F5 | Thick LVM で DRBD 全領域同期 | Phase 4 | peer-disk:Inconsistent が長時間 | ポーリングで待機 (thin は ~30秒) |
| F6 | linstor --output-format json 不存在 | Phase 4 | unrecognized arguments エラー | linstor -m resource list を使う |
| F7 | citype デフォルト (configdrive2) が Debian に非対応 | Phase 3 | cloud-init 設定が VM に反映されない | --citype nocloud を初回起動前に設定 |
| F8 | LINSTOR Auto-eviction がノードオフライン時にリソースを自動退去 | Phase 4-5 | linstor node list で "Auto-eviction at ..." 表示 | テスト前に無効化 |
| F9 | fio JSON 出力ディレクトリが未作成 | Phase 5 | No such file or directory | ローカルの mkdir -p を先頭に追加 |
| F10 | --boot order=scsi0 をディスクインポート前に設定 | Phase 3 | device 'scsi0' does not exist | importdisk → scsi0 接続の後に boot order を設定 |
| F11 | DRBD 9 の同期確認に /proc/drbd (DRBD 8形式) を使用 | Phase 4 | タイムアウトまたは空出力 | drbdsetup status <res> --verbose を使用 |
| F12 | sshpass が非コントローラ PVE ホストに未インストール | Phase 4 | sshpass: command not found | 公開鍵認証 + 10.x 直接 SSH を使用するため sshpass 不要 |
| F13 | VM 再作成後に SSH known_hosts のホスト鍵が不一致 | Phase 4 | REMOTE HOST IDENTIFICATION HAS CHANGED | ssh-keygen -R <vm_ip> -f ssh/known_hosts で事前クリア |
| F14 | Satellite ノードで linstor コマンドが localhost controller に接続不可 | Phase 4 | Connection refused | 全 linstor コマンドは Controller ノード (4号機) で実行 |
| F15 | cloud-init DHCP でアドレス取得失敗 → SSH 不能 | Phase 3 | IP 検出タイムアウト | DHCP 取得を最大5分待機。管理用 SSH は ipconfig1 の static 10.x で行うため DHCP 完了前でもアクセス可能 |
| F16 | vendor snippet ssh_pwauth=true でもパスワード認証不可 | Phase 3-4 | Permission denied (publickey) | SSH 公開鍵認証を優先: qm set --sshkeys <pubkey_file> |
| F17 | Debian 13 (OpenSSH 10.0) で RSA 鍵が無効化 | Phase 3-4 | Permission denied (publickey) | Ed25519 鍵 (id_ed25519.pub) を使用すること |
| F18 | DRBD non-UpToDate 中に qm resize が失敗 | Phase 3 | LINSTOR API 500 エラー | DRBD 同期完了後に resize する。resize 後に VM 内で growpart + resize2fs も必要 |
| F19 | PrefNic 変更後に既存 DRBD リソースが自動再接続されることを知らない | Phase 2-4 | 手動 drbdadm adjust を実行してしまう | PrefNic 変更すると LINSTOR が自動的に drbdadm adjust 相当を実行し、既存リソースが IB 経由に切り替わる。手動 drbdadm adjust は不要 |
SMART の Current_Pending_Sector が 0 より大きいディスクには不良セクタがあり、I/O エラーの原因になる。
ゼロ書きで該当セクタに書き込むと、ディスクが代替セクタに再割り当てを行う。
# 不良セクタの強制再割り当て (対象ディスクに対して実行)
ssh root@<node_ip> 'dd if=/dev/zero of=/dev/sdX bs=1M count=100 oflag=direct'
ssh root@<node_ip> 'dd if=/dev/zero of=/dev/sdX bs=1M count=100 oflag=direct seek=$(($(blockdev --getsize64 /dev/sdX)/1048576 - 100))'
# 再チェック
ssh root@<node_ip> 'smartctl -A /dev/sdX | grep -E "Current_Pending|Reallocated|Offline_Uncorrectable"'
qm importdisk が LINSTOR ストレージにディスクをインポートすると、リソース名が pm-XXXX (ランダム) になる。
vm-100-disk-0 のような標準名にはならないため、importdisk の出力からリソース名をパースする必要がある。
# importdisk 実行結果の例:
# unused0: successfully imported disk 'linstor-storage:pm-bfea68ea_100'
# → パースして 'pm-bfea68ea_100' を取得し、qm set --scsi0 に使用
# 注意: リソース名は pm-<hex>_<vmid> 形式。[0-9]* ではなく [a-z0-9_]+ でマッチすること
IMPORT_OUTPUT=$(ssh root@$NODE1_IP "qm importdisk $VM_ID $CLOUD_IMAGE linstor-storage" 2>&1)
RESOURCE_NAME=$(echo "$IMPORT_OUTPUT" | grep -o "linstor-storage:pm-[a-z0-9_]*" | sed 's/linstor-storage://')
ssh root@$NODE1_IP "qm set $VM_ID --scsi0 linstor-storage:$RESOURCE_NAME,discard=on,iothread=1"
Debian cloud image はデフォルトで SSH パスワード認証が無効。--cipassword を設定しても SSH できない。
vendor snippet で ssh_pwauth: true を設定する必要がある。
# vendor snippet を PVE ホストに作成 (snippets ディレクトリが必要)
ssh root@$NODE1_IP 'mkdir -p /var/lib/vz/snippets'
ssh root@$NODE1_IP 'printf "#cloud-config\nssh_pwauth: true\n" > /var/lib/vz/snippets/ssh-pwauth.yml'
# VM に適用
ssh root@$NODE1_IP "qm set $VM_ID --cicustom 'vendor=local:snippets/ssh-pwauth.yml'"
注: 管理用 IP (10.x) が config で設定済み (
vm_mgmt_ip) の場合、MAC アドレス検出・nmap スキャン・ARP 参照は不要。VM_IP=$VM_MGMT_IPで直接 SSH 接続できる。
以下は DHCP のみの構成で IP 検出が必要な場合の参考手順:
MAC=$(ssh root@$NODE1_IP "qm config $VM_ID" | grep '^net0' | grep -o 'virtio=[0-9A-Fa-f:]*' | cut -d= -f2)
ssh root@$NODE1_IP "ip neigh | grep -i '$MAC' | awk '{print \$1}'"
Debian の cloud-init は nocloud データソースを使う。PVE のデフォルト configdrive2 では設定が反映されない。
ssh root@$NODE1_IP "qm set $VM_ID --citype nocloud"
ノードがオフラインになると LINSTOR は約60分後に Auto-eviction を発動し、リソースを自動退去させる。 ベンチマーク中にノード障害やリブートが発生すると、意図しないリソース再配置が起きる。
検出:
ssh root@$NODE1_IP "linstor node list"
# "Auto-eviction at 2026-XX-XX XX:XX:XX" が表示される
対策 (テスト前に各ノードで無効化):
ssh root@$NODE1_IP "linstor node set-property $NODE1 DrbdOptions/AutoEvictAllowEviction false"
ssh root@$NODE1_IP "linstor node set-property $NODE2 DrbdOptions/AutoEvictAllowEviction false"
テスト終了後に再有効化:
ssh root@$NODE1_IP "linstor node set-property $NODE1 DrbdOptions/AutoEvictAllowEviction"
ssh root@$NODE1_IP "linstor node set-property $NODE2 DrbdOptions/AutoEvictAllowEviction"
fio の JSON 出力を SSH 経由で stdout リダイレクトする場合、出力先ディレクトリはローカルホスト側 (Claude Code 実行マシン) に必要。
# Phase 5 の先頭で実行
mkdir -p tmp/<session-id>/fio-results/
qm importdisk → qm set --scsi0 → qm set --boot order=scsi0 の順で実行すること。
importdisk 前や scsi0 接続前に boot order を設定すると、scsi0 デバイスが存在しないためエラーになる。
DRBD 9 では /proc/drbd は空または DRBD 8 と形式が異なる。同期状態の確認には以下を使用:
# 推奨: 全リソースのステータス
ssh root@$CONTROLLER_IP "drbdsetup status --verbose"
# 特定リソース
ssh root@$CONTROLLER_IP "drbdsetup status <resource> --verbose"
# peer-disk で UpToDate を確認
# drbdadm status も使用可能 (ANSI カラーコード付き)
公開鍵認証 + 10.x 管理用 IP への直接 SSH を使用するため、sshpass は不要。
VM への SSH は ssh ${VM_USER}@${VM_MGMT_IP} で直接接続する。
VM を再作成すると SSH ホスト鍵が変わるため、known_hosts に古い鍵が残っているとエラーになる。
# VM の IP に対してホスト鍵をクリア
ssh-keygen -R <vm_ip> -f ssh/known_hosts
# PVE ホスト側でも同様
ssh root@$NODE1_IP "ssh-keygen -R <vm_ip> -f ssh/known_hosts"
Satellite ノードには linstor-controller が動いていないため、linstor コマンドがデフォルトの localhost:3370 に接続しようとして失敗する。全 linstor コマンドは Controller ノード (4号機) 経由で実行すること。
VM にはデュアル NIC を構成し、SSH は 10.x 管理用 IP に直接接続する:
net0: vmbr1 (192.168.39.0/24, DHCP, インターネット用)net1: vmbr0 (10.0.0.0/8, static, SSH 管理用)注意 (F17): Debian 13 cloud image (OpenSSH 10.0) は RSA 鍵が無効化されている。
/root/.ssh/id_rsa.pubではなく Ed25519 鍵 (/root/.ssh/id_ed25519.pub) を使うこと。
qm set --sshkeys で設定:
scp -F ssh/config ~/.ssh/id_ed25519.pub root@$NODE1_IP:/tmp/local_ed25519.pub
ssh root@$NODE1_IP "qm set $VM_ID --sshkeys /tmp/local_ed25519.pub"
ssh -o StrictHostKeyChecking=no debian@$VM_MGMT_IP でローカルから直接接続 (10.x 経由、sshpass 不要)ssh ${VM_USER}@${VM_MGMT_IP} 'cloud-init status --wait'
注意: 192.168.39.0/24 に静的 IP を割り当てないこと (DHCP 衝突の危険)。
全7テスト。各テストは --runtime=$FIO_RUNTIME (デフォルト 60秒) で実行。
| # | テスト名 | rw | bs | iodepth | size |
|---|---|---|---|---|---|
| 1 | randread-4k-qd1 | randread | 4k | 1 | $FIO_SIZE |
| 2 | randread-4k-qd32 | randread | 4k | 32 | $FIO_SIZE |
| 3 | randwrite-4k-qd1 | randwrite | 4k | 1 | $FIO_SIZE |
| 4 | randwrite-4k-qd32 | randwrite | 4k | 32 | $FIO_SIZE |
| 5 | seqread-1m-qd32 | read | 1m | 32 | $FIO_SEQ_SIZE |
| 6 | seqwrite-1m-qd32 | write | 1m | 32 | $FIO_SEQ_SIZE |
| 7 | mixed-rw-4k-qd32 | randrw (70/30) | 4k | 32 | $FIO_SIZE |
共通オプション: --ioengine=libaio --direct=1 --numjobs=1 --time_based --group_reporting --output-format=json
pve-lock: 不要
各ノードの各ストレージディスクの SMART をチェックし、不良セクタを検出する。
preflight スクリプトを各ノードに転送:
scp -F ssh/config ./scripts/linstor-bench-preflight.sh root@$NODE1_IP:/tmp/
scp -F ssh/config ./scripts/linstor-bench-preflight.sh root@$NODE2_IP:/tmp/
各ノードで実行:
ssh root@$NODE1_IP 'sh /tmp/linstor-bench-preflight.sh /dev/sda /dev/sdb /dev/sdc /dev/sdd'
ssh root@$NODE2_IP 'sh /tmp/linstor-bench-preflight.sh /dev/sda /dev/sdb /dev/sdc /dev/sdd'
不良セクタ検出時 (exit 1):
Current_Pending_Sector = 0 を確認pve-lock: 必須
既存の VM、LINSTOR リソース、ストレージプール、LVM を解体する。 存在しないリソースの削除はエラーになるので、各ステップで存在確認してからスキップまたは削除する。
削除順序: VM → LINSTOR リソース → リソースグループ → ストレージプール → PVE ストレージ → LVM
VM 停止・削除 (存在する場合):
ssh root@$NODE1_IP "qm status $VM_ID" 2>/dev/null && \
ssh root@$NODE1_IP "qm stop $VM_ID" && \
ssh root@$NODE1_IP "qm destroy $VM_ID --purge"
LINSTOR リソース削除 (存在する場合):
# リソース定義一覧を確認
ssh root@$NODE1_IP "linstor resource-definition list"
# 各リソース定義を削除 (--async でタイムアウト回避)
ssh root@$NODE1_IP "linstor resource-definition delete <resource-name>"
PVE ストレージ削除:
ssh root@$NODE1_IP "pvesm status" | grep linstor-storage && \
ssh root@$NODE1_IP "pvesm remove linstor-storage"
リソースグループ削除:
ssh root@$NODE1_IP "linstor volume-group delete $RG_NAME 0" 2>/dev/null
ssh root@$NODE1_IP "linstor resource-group delete $RG_NAME" 2>/dev/null
ストレージプール削除:
ssh root@$NODE1_IP "linstor storage-pool delete $NODE1 <pool-name>"
ssh root@$NODE1_IP "linstor storage-pool delete $NODE2 <pool-name>"
各ノードで LVM 解体:
ssh root@$NODE1_IP "vgs $VG_NAME" 2>/dev/null && \
ssh root@$NODE1_IP "lvremove -f $VG_NAME 2>/dev/null; vgremove -f $VG_NAME; pvremove /dev/sda /dev/sdb /dev/sdc /dev/sdd"
ssh root@$NODE2_IP "vgs $VG_NAME" 2>/dev/null && \
ssh root@$NODE2_IP "lvremove -f $VG_NAME 2>/dev/null; vgremove -f $VG_NAME; pvremove /dev/sda /dev/sdb /dev/sdc /dev/sdd"
検証:
ssh root@$NODE1_IP "linstor storage-pool list"
ssh root@$NODE1_IP "linstor resource-definition list"
ssh root@$NODE1_IP "pvesm status"
pve-lock: 必須
引数 storage_type に応じてストレージを構成する。
各ノードで VG を作成する:
ssh root@$NODE1_IP "wipefs -af /dev/sda /dev/sdb /dev/sdc /dev/sdd && pvcreate /dev/sda /dev/sdb /dev/sdc /dev/sdd && vgcreate $VG_NAME /dev/sda /dev/sdb /dev/sdc /dev/sdd"
ssh root@$NODE2_IP "wipefs -af /dev/sda /dev/sdb /dev/sdc /dev/sdd && pvcreate /dev/sda /dev/sdb /dev/sdc /dev/sdd && vgcreate $VG_NAME /dev/sda /dev/sdb /dev/sdc /dev/sdd"
# Thin pool 作成 (各ノード)
ssh root@$NODE1_IP "lvcreate -l 95%FREE -T ${VG_NAME}/thinpool"
ssh root@$NODE2_IP "lvcreate -l 95%FREE -T ${VG_NAME}/thinpool"
# LINSTOR ストレージプール (lvmthin)
POOL_NAME="thinpool"
ssh root@$NODE1_IP "linstor storage-pool create lvmthin $NODE1 $POOL_NAME ${VG_NAME}/thinpool"
ssh root@$NODE1_IP "linstor storage-pool create lvmthin $NODE2 $POOL_NAME ${VG_NAME}/thinpool"
# LINSTOR ストレージプール (lvm)
POOL_NAME="striped-pool"
ssh root@$NODE1_IP "linstor storage-pool create lvm $NODE1 $POOL_NAME $VG_NAME"
ssh root@$NODE1_IP "linstor storage-pool create lvm $NODE2 $POOL_NAME $VG_NAME"
# ストライピングオプション設定 (-i4: 4本, -I64: 64KiB)
# ダッシュ引数が承認プロンプトに引っかかるため scp + ssh パターンで実行
# tmp/<session-id>/lvcreate-opts.sh に以下を Write (ノード名を変数で展開):
# linstor storage-pool set-property <NODE> <POOL_NAME> StorDriver/LvcreateOptions -- '-i4 -I64'
scp -F ssh/config tmp/<session-id>/lvcreate-opts-node1.sh root@$NODE1_IP:/tmp/lvcreate-opts.sh
ssh root@$NODE1_IP sh /tmp/lvcreate-opts.sh
scp -F ssh/config tmp/<session-id>/lvcreate-opts-node2.sh root@$NODE1_IP:/tmp/lvcreate-opts.sh
ssh root@$NODE1_IP sh /tmp/lvcreate-opts.sh
# リソースグループ
ssh root@$NODE1_IP "linstor resource-group create $RG_NAME --place-count $PLACE_COUNT --storage-pool $POOL_NAME"
ssh root@$NODE1_IP "linstor volume-group create $RG_NAME"
# DRBD オプション
ssh root@$NODE1_IP "linstor resource-group drbd-options --protocol C $RG_NAME"
ssh root@$NODE1_IP "linstor resource-group drbd-options --quorum off $RG_NAME"
ssh root@$NODE1_IP "linstor resource-group drbd-options --auto-promote yes $RG_NAME"
# PVE ストレージ
ssh root@$NODE1_IP "pvesm add drbd linstor-storage --resourcegroup $RG_NAME --content images,rootdir --controller $NODE1_IP"
ssh root@$NODE1_IP "linstor storage-pool list"
ssh root@$NODE1_IP "pvesm status"
pve-lock: 必須
注意: cloud-init は初回起動時のみ実行される。すべての設定 (cicustom, citype, ciuser, cipassword, ipconfig0, ipconfig1) を
qm start前に完了すること。
ブリッジ検証 (★ 7号機 NIC 逆順対策):
# vmbr0 が 10.x (管理)、vmbr1 が 192.168.39.x (DHCP) であることを確認
ssh root@$NODE1_IP "ip -4 addr show vmbr0" # → 10.10.10.x が表示されるべき
ssh root@$NODE1_IP "ip -4 addr show vmbr1" # → 192.168.39.x が表示されるべき
# 逆の場合は VM_BRIDGE と VM_MGMT_BRIDGE を入れ替える
VM 作成 (★ デュアル NIC: net0=インターネット用, net1=管理用):
ssh root@$NODE1_IP "qm create $VM_ID --name $VM_NAME --memory $VM_MEMORY --cores $VM_CORES --cpu host --net0 virtio,bridge=$VM_BRIDGE --net1 virtio,bridge=$VM_MGMT_BRIDGE --ostype l26 --scsihw virtio-scsi-single"
ディスクインポート (★ F2 対策: リソース名をパース):
IMPORT_OUTPUT=$(ssh root@$NODE1_IP "qm importdisk $VM_ID $CLOUD_IMAGE linstor-storage" 2>&1)
RESOURCE_NAME=$(echo "$IMPORT_OUTPUT" | grep "successfully imported" | grep -o 'pm-[a-z0-9_]*')
RESOURCE_NAME が空の場合は出力全体を確認しエラー対処successfully imported 行のみ対象にすること。pm-<hex> と pm-<hex>_<vmid> の両方にマッチするため、正しい行から抽出しないと誤ったリソース名になるディスク接続 (パースしたリソース名を使用):
ssh root@$NODE1_IP "qm set $VM_ID --scsi0 linstor-storage:${RESOURCE_NAME},discard=on,iothread=1"
ブート順序 + cloudinit ドライブ (★ F10: scsi0 接続後に設定すること):
ssh root@$NODE1_IP "qm set $VM_ID --boot order=scsi0"
ssh root@$NODE1_IP "qm set $VM_ID --ide2 linstor-storage:cloudinit"
vendor snippet 作成・適用 (★ F3 対策):
ssh root@$NODE1_IP 'mkdir -p /var/lib/vz/snippets'
ssh root@$NODE1_IP 'printf "#cloud-config\nssh_pwauth: true\n" > /var/lib/vz/snippets/ssh-pwauth.yml'
ssh root@$NODE1_IP "qm set $VM_ID --cicustom 'vendor=local:snippets/ssh-pwauth.yml'"
citype 設定 (★ F7 対策):
ssh root@$NODE1_IP "qm set $VM_ID --citype nocloud"
SSH 公開鍵設定 (★ F16/F17 対策: Ed25519 公開鍵認証を使用):
# ローカルマシン (Claude Code ホスト) の Ed25519 公開鍵を PVE ホストにコピー
scp -F ssh/config ~/.ssh/id_ed25519.pub root@$NODE1_IP:/tmp/local_ed25519.pub
ssh root@$NODE1_IP "qm set $VM_ID --sshkeys /tmp/local_ed25519.pub"
cloud-init ユーザ・ネットワーク設定 (★ デュアル NIC: DHCP + 管理用 static):
ssh root@$NODE1_IP "qm set $VM_ID --ciuser $VM_USER --cipassword $VM_PASS --ipconfig0 ip=dhcp --ipconfig1 ip=$VM_MGMT_IP/$VM_MGMT_PREFIX"
ipconfig0: vmbr1 (DHCP, インターネット用)ipconfig1: vmbr0 (static 10.x, SSH 管理用, ゲートウェイ不要)VM 起動 (★ resize は DRBD 同期完了後に行うため先に起動):
ssh root@$NODE1_IP "qm start $VM_ID"
pve-lock: 不要
thick-stripe の場合、全領域の DRBD 初期同期が発生する。thin の場合は使用済み領域のみで ~30秒。
実測同期レート (SATA HDD over IPoIB):
| ディスクサイズ | 所要時間 | レート |
|---|---|---|
| 32G | ~9分 | — |
| 518 GiB | ~92分 | ~96 MiB/s |
ポーリング間隔: 30秒 (drbdsetup status)
# 同期状態のポーリング (30秒間隔) (★ F11: drbdsetup を使用、/proc/drbd は DRBD 9 で空)
ssh root@$CONTROLLER_IP "drbdsetup status --verbose"
# 出力例 (同期中): peer-disk:Inconsistent
# 出力例 (完了): peer-disk:UpToDate
# ★ F14: linstor/DRBD コマンドは Controller ノード (4号機) で実行すること
# ★ 接続先アドレスの確認には `drbdsetup show <resource>` を使う (IP 表示あり)。
# `drbdsetup status --verbose` では接続先 IP が表示されない
UpToDate/UpToDate になるまでポーリングで待機する。
ssh root@$NODE1_IP "qm resize $VM_ID scsi0 $VM_DISK_SIZE"
# VM 内でパーティション + ファイルシステム拡張
ssh ${VM_USER}@${VM_MGMT_IP} "sudo growpart /dev/sda 1"
ssh ${VM_USER}@${VM_MGMT_IP} "sudo resize2fs /dev/sda1"
VM の管理用 IP は config から既知 (10.x static)。MAC アドレス検出・nmap スキャン・ARP テーブル参照は不要。
VM_IP=$VM_MGMT_IP
注: F4 (MAC アドレス不一致) は管理用 IP が config で設定済みのため不要。 DHCP の 192.168.39.x IP はインターネット通信専用で、SSH 接続には使用しない。
# ★ F13: VM 再作成時はホスト鍵をクリア
ssh-keygen -R $VM_IP -f ssh/known_hosts
# 10.x 管理用 IP に直接 SSH (公開鍵認証、Phase 3 で --sshkeys 設定済み)
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${VM_USER}@${VM_IP} 'uname -a'
DHCP (vmbr1) は最大5分かかる場合がある。SSH は 10.x (static) 経由なので DHCP 完了前でもアクセス可能。 ただし fio インストール (apt) にはインターネットが必要なので、cloud-init 完了を待つ。
ssh ${VM_USER}@${VM_IP} 'cloud-init status --wait'
ssh ${VM_USER}@${VM_IP} 'sudo apt-get update && sudo apt-get install -y fio'
注意 (F6): LINSTOR の JSON 出力は
linstor --output-format jsonではなくlinstor -m(machine-readable) を使う。--output-formatオプションは存在しない。
pve-lock: 不要
7テストを順番に実行する。各テスト結果は JSON で tmp/<session-id>/fio-results/ に保存する。
# ★ F9: 出力ディレクトリをローカル側に事前作成
mkdir -p tmp/<session-id>/fio-results
各テストの実行 (10.x 管理 IP に公開鍵認証で SSH 接続):
ssh ${VM_USER}@${VM_IP} "sudo fio \
--name=<test-name> \
--ioengine=libaio --direct=1 \
--rw=<rw-pattern> --bs=<block-size> \
--iodepth=<queue-depth> --numjobs=1 \
--size=<size> \
--runtime=$FIO_RUNTIME --time_based \
--group_reporting --output-format=json" \
> tmp/<session-id>/fio-results/<test-name>.json
テスト一覧 (fio テスト定義セクション参照):
randread-4k-qd1: --rw=randread --bs=4k --iodepth=1 --size=$FIO_SIZErandread-4k-qd32: --rw=randread --bs=4k --iodepth=32 --size=$FIO_SIZErandwrite-4k-qd1: --rw=randwrite --bs=4k --iodepth=1 --size=$FIO_SIZErandwrite-4k-qd32: --rw=randwrite --bs=4k --iodepth=32 --size=$FIO_SIZEseqread-1m-qd32: --rw=read --bs=1m --iodepth=32 --size=$FIO_SEQ_SIZEseqwrite-1m-qd32: --rw=write --bs=1m --iodepth=32 --size=$FIO_SEQ_SIZEmixed-rw-4k-qd32: --rw=randrw --rwmixread=70 --bs=4k --iodepth=32 --size=$FIO_SIZEpve-lock: 不要
fio JSON から主要メトリクスを抽出:
Python スクリプトで JSON をパース:
# tmp/<session-id>/extract-fio.py を作成して実行
python3 tmp/<session-id>/extract-fio.py tmp/<session-id>/fio-results/
fio JSON の構造:
jobs[0].read.iops, jobs[0].read.bw (KiB/s), jobs[0].read.clat_ns.mean (ns), jobs[0].read.clat_ns.percentile["99.000000"] (ns)
jobs[0].write.iops, jobs[0].write.bw (KiB/s), jobs[0].write.clat_ns.mean (ns), jobs[0].write.clat_ns.percentile["99.000000"] (ns)
report/ にレポート生成 (REPORT.md フォーマット準拠):
状態変更操作は ./oplog.sh で記録する:
読み取り専用操作 (linstor storage-pool list, drbdadm status, fio 実行) は oplog 不要。
Phase 1〜3 では状態変更操作に ./pve-lock.sh を使用する:
./pve-lock.sh run <command...> # 即座に実行(ロック中ならエラー)
./pve-lock.sh wait <command...> # ロック待ち→実行
ロック中の場合は別の課題に着手し、ロック解放後に再開する。