Cơ Chế Chống Boot Loop & Máy Trạng Thái (Anti-Boot-Loop & State Machine)
Tài liệu này giải thích chi tiết nghịch lý cốt lõi trong mô hình Zero-Touch Provisioning (ZTP), nguyên lý hoạt động của máy trạng thái (State Machine) trong dự án, kỹ thuật nhường quyền điều khiển phần cứng (Hardware Handoff qua sanboot), và cơ chế khóa vòng lặp cài đặt vĩnh viễn thông qua Phone-Home Webhook.
1. Nghịch Lý Boot Loop Trong Cài Đặt Tự Động (The ZTP Boot Dilemma)
Để đạt được mục tiêu Zero-Touch Provisioning (ZTP) (cắm điện máy mới là tự động cài đặt hoàn chỉnh từ A đến Z mà không cần con người can thiệp vào bàn phím hay màn hình):
- Bắt buộc: Máy chủ (Bare-metal) hoặc máy ảo (VM) phải được cấu hình thứ tự khởi động trong BIOS/UEFI là Network Boot (PXE) ưu tiên số 1.
- Hệ quả nghịch lý: Sau khi hệ điều hành (Ubuntu, Talos...) cài đặt thành công 100% vào ổ cứng và thực hiện lệnh khởi động lại (
reboot), bo mạch chủ lại tiếp tục ưu tiên boot qua card mạng! - Thảm họa: Nếu máy chủ iPXE không có bộ nhớ nhận diện trạng thái, nó sẽ tiếp tục gửi kịch bản cài đặt OS. Máy tính sẽ bị format lại ổ đĩa và lọt vào vòng lặp cài đặt vô tận (Infinite Reinstall Loop)!
2. Kỹ Thuật Nhường Quyền Điều Khiển Phần Cứng (Hardware Handoff)
Khi Bun server xác định node đã hoàn tất cài đặt, nó không trả về menu cài đặt mà trả về kịch bản iPXE đặc biệt sau (định nghĩa tại [src/routes/ipxe.ts](file:///Users/timi/lab/lab-ipxe-os/src/routes/ipxe.ts)):
#!ipxe
echo ==========================================================
echo Node [k3s-single-node] (bc:24:11:00:24:33) is ALREADY INSTALLED.
echo Bypassing network installation. Booting local disk...
echo (To reinstall, trigger API: POST http://192.168.250.202:3000/api/reset?mac=bc:24:11:00:24:33)
echo ==========================================================
sleep 2
sanboot --no-describe --drive 0x80 || exit 12.1. Lệnh sanboot --no-describe --drive 0x80 hoạt động như thế nào?
0x80(Hệ thập lục phân) đại diện cho ổ cứng vật lý thứ nhất (First Hard Drive) trong kiến trúc BIOS chuẩn ngắtINT 13h.- Trong môi trường UEFI hiện đại, iPXE ánh xạ
0x80tới thiết bị lưu trữ cục bộ đầu tiên theo giao thức UEFI Block I/O Protocol. - Cờ
--no-describechỉ thị cho iPXE không tạo thêm các bảng mô tả thiết bị SAN ảo phức tạp trong bộ nhớ ACPI, giúp quá trình chuyển giao quyền điều khiển CPU sang Master Boot Record (MBR) hoặc EFI System Partition (ESP) trên ổ cứng diễn ra ngay lập tức mà không làm gián đoạn kernel boot.
2.2. Cơ chế chuyển giao điều khiển UEFI (iseq ${platform} efi)
- Trong môi trường BIOS Legacy, iPXE sử dụng
sanboot --no-describe --drive 0x80 || exit 1. - Trong môi trường UEFI x86_64 hiện đại (như UEFI AMI trên bo mạch chủ ASUS B760/B660/Z790),
sanboot --drive 0x80không tương thích với cấu trúc EFI Device Path và sẽ bị lỗi. Nếu để lệnh rơi vàoexit 1(trả về mã lỗi cho firmware), một số firmware UEFI có cơ chế tự phục hồi lỗi boot (Auto Boot Recovery) sẽ hiểu nhầm card mạng bị hỏng và tự ý hoán đổiBootOrder, đưa SSD lên vị trí #1. - Vì vậy, script iPXE tự động phát hiện nền tảng:ipxeLệnh
iseq ${platform} efi && goto uefi_boot || goto bios_boot :bios_boot sanboot --no-describe --drive 0x80 || exit 1 :uefi_boot exit 0 || exit 1exit 0trả về trạng thái thành công cho UEFI Boot Manager, cho phép firmware chuyển quyền điều khiển sang mục boot kế tiếp trong danh sách (ubuntutrên SSD) một cách êm ái.
2.3. Cơ chế bảo vệ thứ tự khởi động UEFI NVRAM (ipxe-boot-order.service)
Trên các hệ thống Bare-metal, quá trình cài đặt OS mặc định sẽ gọi grub-install, tự chèn ubuntu lên vị trí số 1 của BootOrder trong NVRAM. Ngoài ra một số firmware bo mạch chủ tự động đưa thiết bị vừa boot thành công lên vị trí ưu tiên.
Để bảo toàn mô hình ZTP vĩnh viễn trên mọi bo mạch chủ, dự án áp dụng cơ chế 2 lớp:
- Thiết lập tức thì trong cài đặt:
late-commands(Ubuntu) /combustion(openSUSE) chạy/usr/local/bin/ensure-pxe-boot-order.shngay trước khi reboot lần đầu để đưa card mạng về #1. - Khoá vĩnh viễn bằng Systemd Service (
ipxe-boot-order.service): Tự động kích hoạt ở mỗi lần hệ điều hành khởi động. Service này kiểm traBootOrder, và nếu mục đầu tiên chưa phải là PXE thì mới ghi lại vào NVRAM (giúp bảo vệ tuổi thọ chip nhớ NVRAM Flash). - Lợi ích kép:
- Khi iPXE Server online: Node luôn boot vào iPXE để Server quyết định (re-install hoặc chạy vào ổ cứng).
- Khi iPXE Server offline / Rút dây mạng: BIOS tự động fallback sang mục #2 (
ubuntutrên SSD), đảm bảo hệ thống không bao giờ bị gián đoạn.
3. Kiến Trúc State Machine Của Bun Server
Toàn bộ logic quản lý trạng thái cài đặt được tập trung trong lớp StateManager tại [src/core/state.ts](file:///Users/timi/lab/lab-ipxe-os/src/core/state.ts).
3.1. Cấu Trúc Bảng hosts trong data/state.db (SQLite)
Trạng thái vòng đời và cấu hình máy chủ được lưu trữ bền vững (persistent) trong cơ sở dữ liệu SQLite chuẩn ACID (PRAGMA journal_mode = WAL):
SELECT mac, hostname, os, status, ip, installed_at, updated_at FROM hosts;Ví dụ một bản ghi hoàn tất cài đặt:
{
"mac": "bc:24:11:00:24:33",
"hostname": "k3s-single-node",
"os": "ubuntu",
"status": "INSTALLED",
"ip": "192.168.250.33",
"installed_at": "2026-09-14T07:25:39.124Z",
"updated_at": "2026-09-14T07:25:39.124Z"
}3.2. Chu Kỳ Chuyển Đổi Trạng Thái (State Transitions)
[ UNCONFIGURED / NEW_NODE ]
│
▼ (Tạo qua Web UI "+ Add Node", REST API, hoặc Seed từ hosts.yaml)
[ REGISTERED (PENDING) ]
│
▼ (Client kéo boot.ipxe & bắt đầu tải Kernel/Initrd)
[ PROVISIONING ]
│
▼ (Subiquity / Combustion hoàn thành cài đặt -> Gửi Webhook Phone-Home)
[ INSTALLED ] ◄── (Khóa chặn Boot Loop vĩnh viễn)
│
├── (Mỗi lần Reboot sau đó: Thực thi sanboot boot ổ cứng)
│
▼ (Quản trị viên muốn cài lại: Bấm nút "Reset" trên UI hoặc gọi API Reset)
[ PENDING ] ──> Cho phép cài đặt lại từ đầu mà không mất cấu hình máy!4. Cơ Chế Phone-Home Webhook
Quá trình "xác nhận hoàn tất" diễn ra tự động 100% từ chính bên trong máy tính vừa được cài đặt.
Tại thời điểm Subiquity hoàn thành việc ghi dữ liệu vào ổ cứng, nó kích hoạt danh sách lệnh late-commands bên trong chroot của máy đích (định nghĩa tại [src/providers/ubuntu/profiles/index.ts](file:///Users/timi/lab/lab-ipxe-os/src/providers/ubuntu/profiles/index.ts)):
curtin in-target --target=/target -- curl -s -X POST \
"http://192.168.250.202:3000/api/installed?mac=bc%3A24%3A11%3A00%3A24%3A33&hostname=k3s-single-node&os=ubuntu"Khi máy chủ Bun nhận được request tại route /api/installed ([src/routes/api.ts](file:///Users/timi/lab/lab-ipxe-os/src/routes/api.ts)):
- Chuẩn hóa địa chỉ MAC (chuyển chữ thường, thay
-bằng:, bỏ prefix0x). - Cập nhật record trong
data/state.dbchuyển trạng thái sangINSTALLEDvà ghi nhận thời điểminstalled_at. - Trả về mã phản hồi HTTP
200 OK. - Kể từ thời khắc này, mọi truy vấn iPXE từ địa chỉ MAC này sẽ chỉ nhận được lệnh
sanboot 0x80.
5. Quy Trình Cài Đặt Lại Máy (Re-installation Workflow)
Khi bạn muốn cài đặt lại hệ điều hành cho một máy đã được đánh dấu INSTALLED, bạn có 4 cách linh hoạt:
Cách 1: Bấm nút "Reset" trên Web UI Dashboard (Nhanh nhất)
Tại trang chủ http://localhost:3000/, bấm nút Reset tương ứng với hàng của máy đó:
- Trạng thái ngay lập tức chuyển thành
PENDING(hiển thị màu xanh dương nhạt). - Toàn bộ cấu hình mạng, profile, ổ đĩa của node vẫn được bảo toàn nguyên vẹn trong SQLite.
Cách 2: Gọi API Quản Trị Reset qua cURL
Gửi lệnh HTTP POST tới endpoint /api/reset:
curl -X POST "http://192.168.250.202:3000/api/reset?mac=bc:24:11:00:24:33"Kết quả: Máy chủ cập nhật status của node về PENDING trong data/state.db. Lần khởi động tiếp theo, máy sẽ lại hiện menu cài đặt.
Cách 3: Bật cờ force_install qua Web UI Edit hoặc REST API
Bấm nút Edit trên Dashboard hoặc gọi PUT /api/hosts/:mac với "force_install": true.
Cách 4: Nạp URL Boot kèm tham số ?force=true
Trong iPXE CLI hoặc trên Router:
chain --autofree http://192.168.250.202:3000/boot.ipxe?mac=${net0/mac}&force=true6. Xử Lý Các Kịch Bản Ngoại Lệ (Edge Cases)
| Tình Huống Ngoại Lệ | Hành Vi Của Hệ Thống | Giải Pháp Xử Lý |
|---|---|---|
| Mất điện hoặc lỗi mạng giữa chừng khi đang cài đặt | Vì lỗi xảy ra TRƯỚC bước late-commands, webhook chưa được kích hoạt -> Node vẫn ở trạng thái PROVISIONING hoặc PENDING. Khi có điện lại, máy sẽ tự động khởi động lại quá trình cài đặt từ đầu một cách an toàn. | Hệ thống tự phục hồi tự động, không cần thao tác thủ công. |
| Webhook thất bại do Router bị nghẽn gói | Cờ || true trong late-command đảm bảo quá trình cài đặt của Subiquity không bị dừng đột ngột. Tuy nhiên lần boot sau máy sẽ bị cài lại. | Kiểm tra firewall/ACL giữa subnet máy đích và cổng 3000 của Bun Server. |
| Máy chủ Bun bị restart hoặc sập nguồn | bun:sqlite hoạt động ở chế độ Write-Ahead Logging (WAL) đảm bảo tính toàn vẹn dữ liệu (ACID). Khi server khởi động lại, trạng thái node được giữ nguyên vẹn. | Không bị mất trạng thái của các node đã cài. |
7. Khuyến Nghị Bảo Mật Cho Môi Trường Mạng (Security Hardening)
- Giới hạn dải mạng truy cập API:
- Các Endpoint quản trị và bảo mật như
/api/installed,/api/reset,/api/hosts, và/api/kubeconfigchỉ nên cho phép các IP thuộc mạng nội bộ Homelab/Data Center (192.168.250.0/24) truy cập. Tuyệt đối không mở cổng 3000 ra Internet công cộng mà không có reverse proxy xác thực.
- Các Endpoint quản trị và bảo mật như
- Khóa State bằng API Token (Khuyến nghị cho Enterprise):
- Có thể bổ sung thêm biến môi trường
ADMIN_API_TOKENvào file.env. - Các lệnh
/api/reset,/api/hosts, hoặc/api/installedbắt buộc phải kèm HeaderAuthorization: Bearer <TOKEN>để ngăn chặn việc người dùng trái phép gửi request giả mạo địa chỉ MAC làm gián đoạn máy chủ.
- Có thể bổ sung thêm biến môi trường