# 佈建流程

本頁逐階段說明 `POST /api/vm/install` 從收到請求到 VM 可 SSH 登入之間做了什麼。

## 階段總覽

每個步驟的 SSE 事件 `step` 欄位格式為 `<階段> > <步驟>`。

| 階段 | 步驟 | 動作 |
|---|---|---|
| `preparation` | `checking VMID` | 未指定 `id` 時執行 [VMID 配置](/zh/vmid-ip-allocation)；指定時直接使用，不檢查是否已被占用 |
| | `assigning IP` | `GATEWAY` 前三段 + VMID，加 `/24` |
| | `validating CPU and RAM` | CPU ≤ 0 → 1；超過 `VM_MAX_CPU` 則壓回。RAM < 512 → 512；超過 `VM_MAX_RAM` 則壓回 |
| | `validating disk size` | 去掉結尾 `G` 解析數字；< 16 → `16G`；≥ `VM_MAX_DISK` → 上限值 |
| | `setting default config values` | 名稱加上 `-<vmid>`、填入預設使用者與密碼 |
| | `checking storage pool` | `pvesm status` 確認 `ASSIGN_STORAGE` 啟用中且類型為 `dir`／`zfspool`／`lvmthin`／`nfs` |
| `OS preparation` | `getting OS image` | 依 `os`／`version` 決定映像網址 |
| | `validating OS image URL` | 以 `HEAD` 請求確認 200，逾時 15 秒 |
| | `downloading OS image` | 下載到 `/tmp`；檔案已存在則略過（`using OS image`） |
| `SSH preparation` | `checking SSH key` | 主節點找不到公鑰時以 `ssh-keygen -t ed25519` 產生 |
| `VM creation` | `creating VM` | `qm create`，CPU 類型取 [叢集基準](/zh/cpu-baseline)，偵測失敗時用 `kvm64` |
| | `importing disk image` | `qm importdisk` 到 `ASSIGN_STORAGE` |
| `VM initialization` | `initializing configuration` | 依序 `qm set`：SSH 金鑰、密碼、`--ciupgrade 0`、OS tag、`scsi0`、Cloud-Init 磁碟、開機順序，`qm resize` 至目標大小（失敗重試 3 次、間隔 5 秒），最後設定 `ipconfig0` |
| | `migrating VM` | 請求帶 `node` 時才執行 `qm migrate --with-local-disks` |
| | `waiting for ready` | 開機後每 5 秒嘗試 SSH，最多 60 次 |
| | `SSH initialization` | 在 VM 內執行 [OS 初始化腳本](/zh/os-init-scripts)，輸出逐行轉成 SSE |
| | `rebooting VM` | `qm reboot` 後再次等待 SSH 就緒 |
| | `finalizing` | 輸出總耗時、VMID、IP、使用者 |

## `qm create` 參數

| 參數 | 值 |
|---|---|
| `--cores` | 請求的 `cpu`（已套用上下限） |
| `--cpu` | 叢集基準，或 `kvm64` |
| `--memory` | 請求的 `ram`（已套用上下限） |
| `--scsihw` | `virtio-scsi-pci` |
| `--ostype` | `l26` |
| `--agent` | `1` |
| `--net0` | `virtio,bridge=vmbr0` |
| `--serial0` | `socket` |
| `--numa` / `--balloon` | 依 [Balloon 規則](/zh/configuration#balloon-規則) |

`--ciupgrade 0` 關閉 Cloud-Init 開機時的套件升級，避免與初始化腳本搶套件鎖。

## 失敗處理

| 失敗位置 | 處理 |
|---|---|
| 準備、映像、SSH 金鑰、`qm create` | 送出 `error` 事件後結束，不留下 VM（尚未建立或建立失敗） |
| `importing disk image`、`initializing configuration`、`SSH initialization` | 強制停止並 `qm destroy --purge --skiplock` 清除半成品 VM |
| 遷移、開機、重開指令失敗 | 直接結束，**VM 保留**在叢集中 |
| 等待 SSH 逾時 | 送出 `error` 事件後結束，**VM 保留**，可登入節點排查 |

不論成功或失敗，Handler 最後都會送出 `event: close`。

## 映像快取

下載檔案以版本命名存放在 `/tmp`（例如 `/tmp/debian-12-generic-amd64.qcow2`），之後相同版本的安裝直接重用。上游映像更新後若要取得新版，需手動刪除該檔。
