Name update
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
# Runbook: Boot a Linux machine from an ISO over the network
|
# Runbook: Boot a Linux machine from an ISO over the network
|
||||||
|
|
||||||
End-to-end walkthrough: spin up PXEForge, load an Ubuntu (or any
|
End-to-end walkthrough: spin up OpenPXE, load an Ubuntu (or any
|
||||||
Linux) ISO into it, target a specific bare-metal or VM client by its
|
Linux) ISO into it, target a specific bare-metal or VM client by its
|
||||||
MAC address, and have that machine PXE-boot the installer over the
|
MAC address, and have that machine PXE-boot the installer over the
|
||||||
LAN — no USB stick, no console babysitting.
|
LAN — no USB stick, no console babysitting.
|
||||||
|
|
||||||
This runbook assumes:
|
This runbook assumes:
|
||||||
|
|
||||||
- You have **one Linux host** to run the PXEForge container (any
|
- You have **one Linux host** to run the OpenPXE container (any
|
||||||
distro with Docker / Podman; 2 GB RAM, ~50 GB disk for the ISO
|
distro with Docker / Podman; 2 GB RAM, ~50 GB disk for the ISO
|
||||||
library).
|
library).
|
||||||
- That host sits on the **same broadcast domain / VLAN** as the
|
- That host sits on the **same broadcast domain / VLAN** as the
|
||||||
@@ -15,7 +15,7 @@ This runbook assumes:
|
|||||||
networks need a DHCP relay and are out of scope here.
|
networks need a DHCP relay and are out of scope here.
|
||||||
- An **existing DHCP server** is already handing out IP leases on
|
- An **existing DHCP server** is already handing out IP leases on
|
||||||
that VLAN (your home router, OPNsense, Windows Server, etc.).
|
that VLAN (your home router, OPNsense, Windows Server, etc.).
|
||||||
PXEForge runs as a *DHCP proxy* — it never leases IPs, it only
|
OpenPXE runs as a *DHCP proxy* — it never leases IPs, it only
|
||||||
layers the boot information on top of the existing DHCP exchange.
|
layers the boot information on top of the existing DHCP exchange.
|
||||||
- The target client is configured to **PXE-boot** in BIOS/UEFI
|
- The target client is configured to **PXE-boot** in BIOS/UEFI
|
||||||
firmware (usually `F12` boot menu → Network, or set as first boot
|
firmware (usually `F12` boot menu → Network, or set as first boot
|
||||||
@@ -28,7 +28,7 @@ or [docs/architecture.md](../docs/architecture.md) first.
|
|||||||
|
|
||||||
## 0. Pick your host’s LAN IP
|
## 0. Pick your host’s LAN IP
|
||||||
|
|
||||||
You need the IPv4 address PXEForge will advertise to clients. From
|
You need the IPv4 address OpenPXE will advertise to clients. From
|
||||||
the host:
|
the host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -40,33 +40,33 @@ example `10.0.0.5/24` on `eno1`. From here on we call it
|
|||||||
`PXE_HOST_IP`.
|
`PXE_HOST_IP`.
|
||||||
|
|
||||||
> **Why this matters.** Every URL handed to clients (TFTP server,
|
> **Why this matters.** Every URL handed to clients (TFTP server,
|
||||||
> iPXE chain URL, ISO URL) is built from this IP. If PXEForge
|
> iPXE chain URL, ISO URL) is built from this IP. If OpenPXE
|
||||||
> auto-detects the wrong interface or loopback, clients will fetch
|
> auto-detects the wrong interface or loopback, clients will fetch
|
||||||
> from an unreachable address and silently fail. The startup will
|
> from an unreachable address and silently fail. The startup will
|
||||||
> *fail loudly* if it can only auto-detect a loopback address.
|
> *fail loudly* if it can only auto-detect a loopback address.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Run PXEForge
|
## 1. Run OpenPXE
|
||||||
|
|
||||||
The MVP path is a single `docker run` against the published image,
|
The MVP path is a single `docker run` against the published image,
|
||||||
with `--network host` so the container can see DHCP broadcasts on
|
with `--network host` so the container can see DHCP broadcasts on
|
||||||
the LAN.
|
the LAN.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/pxeforge/isos ~/pxeforge/work
|
mkdir -p ~/openpxe/isos ~/openpxe/work
|
||||||
|
|
||||||
docker run -d --name pxeforge \
|
docker run -d --name openpxe \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
--network host \
|
--network host \
|
||||||
-e PXEFORGE_PUBLIC_IP=10.0.0.5 \
|
-e OPENPXE_PUBLIC_IP=10.0.0.5 \
|
||||||
-e PXEFORGE_DHCP_MODE=proxy \
|
-e OPENPXE_DHCP_MODE=proxy \
|
||||||
-v ~/pxeforge/isos:/var/lib/pxeforge/isos \
|
-v ~/openpxe/isos:/var/lib/openpxe/isos \
|
||||||
-v ~/pxeforge/work:/var/lib/pxeforge/work \
|
-v ~/openpxe/work:/var/lib/openpxe/work \
|
||||||
ghcr.io/YOUR-ORG/pxeforge:0.2.0
|
ghcr.io/YOUR-ORG/openpxe:0.2.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Substitute your `PXEFORGE_PUBLIC_IP`, of course. If you’re building
|
Substitute your `OPENPXE_PUBLIC_IP`, of course. If you’re building
|
||||||
from this repo instead of pulling, see the
|
from this repo instead of pulling, see the
|
||||||
[README quick start](../README.md#quick-start--mvp-container-recommended).
|
[README quick start](../README.md#quick-start--mvp-container-recommended).
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ otherwise. See [README — Container health probes](../README.md#container-healt
|
|||||||
|
|
||||||
### Check the listening ports
|
### Check the listening ports
|
||||||
|
|
||||||
PXEForge holds three privileged UDP/TCP ports. From another shell on
|
OpenPXE holds three privileged UDP/TCP ports. From another shell on
|
||||||
the host:
|
the host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -93,7 +93,7 @@ sudo ss -lntp | grep ':80\b' # HTTP UI / boot scripts
|
|||||||
```
|
```
|
||||||
|
|
||||||
All four should be present. If port 67 is taken by `dnsmasq` or the
|
All four should be present. If port 67 is taken by `dnsmasq` or the
|
||||||
host’s own DHCP, stop that service or run PXEForge on a separate box —
|
host’s own DHCP, stop that service or run OpenPXE on a separate box —
|
||||||
two listeners on `:67` will fight.
|
two listeners on `:67` will fight.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -114,7 +114,7 @@ Two options. Pick one.
|
|||||||
- File size and SHA-256
|
- File size and SHA-256
|
||||||
|
|
||||||
Big ISOs stream — there is no 2 GB limit, but expect upload to be
|
Big ISOs stream — there is no 2 GB limit, but expect upload to be
|
||||||
gated by your browser ↔ host link. The UI shows a progress bar; the
|
throttled by your browser ↔ host link. The UI shows a progress bar; the
|
||||||
animated anvil on the Dashboard tab fires up while imaging is in
|
animated anvil on the Dashboard tab fires up while imaging is in
|
||||||
flight.
|
flight.
|
||||||
|
|
||||||
@@ -124,17 +124,17 @@ If you already have a folder of ISOs on the host, skip the browser:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Dry run first — see what would be imported, no writes:
|
# Dry run first — see what would be imported, no writes:
|
||||||
docker exec pxeforge pxeforge seed \
|
docker exec openpxe openpxe seed \
|
||||||
--from /seed \
|
--from /seed \
|
||||||
--dry-run
|
--dry-run
|
||||||
|
|
||||||
# For real, mount the source dir read-only into the container:
|
# For real, mount the source dir read-only into the container:
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v /my/iso-library:/seed:ro \
|
-v /my/iso-library:/seed:ro \
|
||||||
-v ~/pxeforge/isos:/var/lib/pxeforge/isos \
|
-v ~/openpxe/isos:/var/lib/openpxe/isos \
|
||||||
-v ~/pxeforge/work:/var/lib/pxeforge/work \
|
-v ~/openpxe/work:/var/lib/openpxe/work \
|
||||||
-e PXEFORGE_PUBLIC_IP=10.0.0.5 \
|
-e OPENPXE_PUBLIC_IP=10.0.0.5 \
|
||||||
ghcr.io/YOUR-ORG/pxeforge:0.2.0 seed --from /seed
|
ghcr.io/YOUR-ORG/openpxe:0.2.0 seed --from /seed
|
||||||
```
|
```
|
||||||
|
|
||||||
Each `*.iso` in `/seed` runs through the same upload pipeline as the
|
Each `*.iso` in `/seed` runs through the same upload pipeline as the
|
||||||
@@ -182,13 +182,13 @@ Most BIOS/UEFI screens display the NIC MAC during the network-boot
|
|||||||
attempt — usually as `MAC: AA-BB-CC-DD-EE-FF` flashing on the splash
|
attempt — usually as `MAC: AA-BB-CC-DD-EE-FF` flashing on the splash
|
||||||
right before "PXE-E53: No boot filename received". Write it down.
|
right before "PXE-E53: No boot filename received". Write it down.
|
||||||
|
|
||||||
### 3c. By letting it boot once and watching PXEForge
|
### 3c. By letting it boot once and watching OpenPXE
|
||||||
|
|
||||||
Easiest if the box is in front of you:
|
Easiest if the box is in front of you:
|
||||||
|
|
||||||
1. Power on, hit `F12`, pick **Network boot**.
|
1. Power on, hit `F12`, pick **Network boot**.
|
||||||
2. Without any binding configured, the client will land on the
|
2. Without any binding configured, the client will land on the
|
||||||
PXEForge menu (Default / Installers / Tools / Gated Deployment).
|
OpenPXE menu (Default / Installers / Tools / Queued Deployment).
|
||||||
3. Don’t pick anything. On your laptop:
|
3. Don’t pick anything. On your laptop:
|
||||||
```bash
|
```bash
|
||||||
curl -fsS http://10.0.0.5/api/clients | jq .
|
curl -fsS http://10.0.0.5/api/clients | jq .
|
||||||
@@ -196,7 +196,7 @@ Easiest if the box is in front of you:
|
|||||||
4. The most-recent entry is your target. Copy its `mac`.
|
4. The most-recent entry is your target. Copy its `mac`.
|
||||||
|
|
||||||
From here on we call this MAC `TARGET_MAC` (e.g. `aa:bb:cc:dd:ee:ff`).
|
From here on we call this MAC `TARGET_MAC` (e.g. `aa:bb:cc:dd:ee:ff`).
|
||||||
Hyphens vs colons, upper vs lower case — PXEForge normalizes both.
|
Hyphens vs colons, upper vs lower case — OpenPXE normalizes both.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ curl -fsS -X POST http://10.0.0.5/api/hosts \
|
|||||||
}' | jq .
|
}' | jq .
|
||||||
```
|
```
|
||||||
|
|
||||||
The binding is persisted to `~/pxeforge/work/hosts.json` and survives
|
The binding is persisted to `~/openpxe/work/hosts.json` and survives
|
||||||
container restart.
|
container restart.
|
||||||
|
|
||||||
### Confirm
|
### Confirm
|
||||||
@@ -262,7 +262,7 @@ In order, with timing:
|
|||||||
| iPXE banner | ~1 s | The blue iPXE splash, version string |
|
| iPXE banner | ~1 s | The blue iPXE splash, version string |
|
||||||
| iPXE second-stage DHCP | ~1 s | `Configuring (net0 …)` then `ok` |
|
| iPXE second-stage DHCP | ~1 s | `Configuring (net0 …)` then `ok` |
|
||||||
| HTTP boot script fetch | <1 s | `http://10.0.0.5/boot.ipxe?mac=…` |
|
| HTTP boot script fetch | <1 s | `http://10.0.0.5/boot.ipxe?mac=…` |
|
||||||
| Per-MAC chain | <1 s | `PXEForge: per-MAC binding -> ubuntu-24-04-1-…` |
|
| Per-MAC chain | <1 s | `OpenPXE: per-MAC binding -> ubuntu-24-04-1-…` |
|
||||||
| Kernel + initrd HTTP | 5–30 s | Two 200-OK fetches against `/iso/<id>/casper/vmlinuz` and `…/initrd` |
|
| Kernel + initrd HTTP | 5–30 s | Two 200-OK fetches against `/iso/<id>/casper/vmlinuz` and `…/initrd` |
|
||||||
| Kernel boot | 5–10 s | Kernel banner, then the Ubuntu/cloud-init splash |
|
| Kernel boot | 5–10 s | Kernel banner, then the Ubuntu/cloud-init splash |
|
||||||
| Installer comes up | 30–60 s | The distro’s normal Live/installer environment |
|
| Installer comes up | 30–60 s | The distro’s normal Live/installer environment |
|
||||||
@@ -281,12 +281,12 @@ curl -N http://10.0.0.5/api/log/stream
|
|||||||
You’ll see each protocol step as it happens:
|
You’ll see each protocol step as it happens:
|
||||||
|
|
||||||
```
|
```
|
||||||
INFO pxeforge::dhcp: reply mac=aa:bb:cc:dd:ee:ff arch=X8664Uefi target=tftp/snponly.efi
|
INFO openpxe::dhcp: reply mac=aa:bb:cc:dd:ee:ff arch=X8664Uefi target=tftp/snponly.efi
|
||||||
INFO pxeforge::tftp: RRQ snponly.efi blksize=1468 windowsize=8 → 982 KiB in 412 ms
|
INFO openpxe::tftp: RRQ snponly.efi blksize=1468 windowsize=8 → 982 KiB in 412 ms
|
||||||
INFO pxeforge::dhcp: reply mac=aa:bb:cc:dd:ee:ff (iPXE) target=http/boot.ipxe
|
INFO openpxe::dhcp: reply mac=aa:bb:cc:dd:ee:ff (iPXE) target=http/boot.ipxe
|
||||||
INFO pxeforge::http: GET /boot.ipxe?mac=aa:bb:cc:dd:ee:ff → host binding hit
|
INFO openpxe::http: GET /boot.ipxe?mac=aa:bb:cc:dd:ee:ff → host binding hit
|
||||||
INFO pxeforge::http: GET /iso/ubuntu-…/casper/vmlinuz Range=bytes=0- 200 OK 14 MiB
|
INFO openpxe::http: GET /iso/ubuntu-…/casper/vmlinuz Range=bytes=0- 200 OK 14 MiB
|
||||||
INFO pxeforge::http: GET /iso/ubuntu-…/casper/initrd Range=bytes=0- 200 OK 75 MiB
|
INFO openpxe::http: GET /iso/ubuntu-…/casper/initrd Range=bytes=0- 200 OK 75 MiB
|
||||||
```
|
```
|
||||||
|
|
||||||
The **Terminal** tab in the web UI shows the same thing live, plus a
|
The **Terminal** tab in the web UI shows the same thing live, plus a
|
||||||
@@ -300,14 +300,14 @@ boots from your LAN, but the underlying ISO can come from anywhere
|
|||||||
your *host* can reach:
|
your *host* can reach:
|
||||||
|
|
||||||
- **Direct upload** from a remote workstation via the web UI (HTTPS
|
- **Direct upload** from a remote workstation via the web UI (HTTPS
|
||||||
reverse-proxied if you put PXEForge behind nginx/Caddy).
|
reverse-proxied if you put OpenPXE behind nginx/Caddy).
|
||||||
- **NFS mount** of a remote share — Sidebar → **Storage** → **NFS** →
|
- **NFS mount** of a remote share — Sidebar → **Storage** → **NFS** →
|
||||||
`nfs://files.lab.example.com/exports/isos`. Mounted ISOs show up in
|
`nfs://files.lab.example.com/exports/isos`. Mounted ISOs show up in
|
||||||
the same list and are PXE-bootable directly without copying.
|
the same list and are PXE-bootable directly without copying.
|
||||||
- **Pre-seed** from a CI job that `curl`s a vendor mirror and runs
|
- **Pre-seed** from a CI job that `curl`s a vendor mirror and runs
|
||||||
`pxeforge seed --from`.
|
`openpxe seed --from`.
|
||||||
|
|
||||||
PXEForge itself never reaches out to the internet at boot time — all
|
OpenPXE itself never reaches out to the internet at boot time — all
|
||||||
client traffic stays on the LAN, served from the host.
|
client traffic stays on the LAN, served from the host.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -337,21 +337,21 @@ curl -fsS -X POST http://10.0.0.5/api/hosts \
|
|||||||
-d '{ "mac": "aa:bb:cc:dd:ee:ff", "target": "_local", "label": "lab-rack3-node07 (installed)" }'
|
-d '{ "mac": "aa:bb:cc:dd:ee:ff", "target": "_local", "label": "lab-rack3-node07 (installed)" }'
|
||||||
```
|
```
|
||||||
|
|
||||||
Now if anyone hits `F12 → Network` by accident, PXEForge replies
|
Now if anyone hits `F12 → Network` by accident, OpenPXE replies
|
||||||
with a script that says *"chain back to local HDD"* and the box
|
with a script that says *"chain back to local HDD"* and the box
|
||||||
boots its real OS instead of re-imaging itself. This is the safest
|
boots its real OS instead of re-imaging itself. This is the safest
|
||||||
default for production hardware.
|
default for production hardware.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Re-imaging — the “Gated Deployment” flow
|
## 7. Re-imaging — the “Queued Deployment” flow
|
||||||
|
|
||||||
Different scenario: you have **a rack of 30 servers** to image
|
Different scenario: you have **a rack of 30 servers** to image
|
||||||
identically, all at once. Don’t bind 30 MACs by hand. Use the gate.
|
identically, all at once. Don’t bind 30 MACs by hand. Use the gate.
|
||||||
|
|
||||||
1. **Don’t** create host bindings.
|
1. **Don’t** create host bindings.
|
||||||
2. PXE-boot every machine. They land on the menu.
|
2. PXE-boot every machine. They land on the menu.
|
||||||
3. On each: select **Gated Deployment**. They get position #1, #2,
|
3. On each: select **Queued Deployment**. They get position #1, #2,
|
||||||
…, #30 and start long-polling.
|
…, #30 and start long-polling.
|
||||||
4. In the UI: **Forge Gate** tab shows all 30 lined up. Pick the
|
4. In the UI: **Forge Gate** tab shows all 30 lined up. Pick the
|
||||||
ISO, click **Assign to all waiting**.
|
ISO, click **Assign to all waiting**.
|
||||||
@@ -376,7 +376,7 @@ still in the kernel-fetch phase.
|
|||||||
| Release binding | `DELETE /api/hosts/<mac>` |
|
| Release binding | `DELETE /api/hosts/<mac>` |
|
||||||
| Live log | `curl -N http://$IP/api/log/stream` |
|
| Live log | `curl -N http://$IP/api/log/stream` |
|
||||||
| Prometheus metrics | `curl http://$IP/metrics` |
|
| Prometheus metrics | `curl http://$IP/metrics` |
|
||||||
| Bulk import folder | `pxeforge seed --from /path` |
|
| Bulk import folder | `openpxe seed --from /path` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ still in the kernel-fetch phase.
|
|||||||
`--network host`, check the host firewall on UDP 67/69/4011.
|
`--network host`, check the host firewall on UDP 67/69/4011.
|
||||||
- **iPXE shows `No more network devices`** — firmware NIC isn’t in
|
- **iPXE shows `No more network devices`** — firmware NIC isn’t in
|
||||||
PXE mode, or VLAN tagging is wrong.
|
PXE mode, or VLAN tagging is wrong.
|
||||||
- **iPXE prints `Connection timed out (http://…)`** — `PXEFORGE_PUBLIC_IP`
|
- **iPXE prints `Connection timed out (http://…)`** — `OPENPXE_PUBLIC_IP`
|
||||||
is wrong. Clients can’t reach that IP. Check `/api/status` →
|
is wrong. Clients can’t reach that IP. Check `/api/status` →
|
||||||
`public_base_url` and `ping` it from the client subnet.
|
`public_base_url` and `ping` it from the client subnet.
|
||||||
- **Kernel panics during initrd load** — corrupt ISO upload. Check
|
- **Kernel panics during initrd load** — corrupt ISO upload. Check
|
||||||
|
|||||||
Reference in New Issue
Block a user