Skip to content

Repository files navigation

NodeForge

NodeForge 是一个用 Zig 实现的轻量级 OS Provisioning 平台,面向小型 Linux 集群和实验室环境。它在单个守护进程中提供 DHCPv4、TFTP、HTTP 和本机管理 API,可通过 IPv4 PXE 完成无人值守安装与 diskless 启动。

当前开发基线:v0.4.18 实现工作树(v0.4.16/0.4.17 是横切底层修复;v0.4.18 的部署级验证已在平台任务书闭合) · Zig 0.16 · UEFI x86_64/aarch64 · Rocky Linux / RHEL 系 Kickstart · Ubuntu Autoinstall

实现落地与平台验证分开计量:当前完成的平台验证基线为 v0.4.15;当前候选的功能范围、 最后一次结果和证据入口以 平台验证 runbook §2.0.0/§2.0 为准。v0.4.16/v0.4.17 不建立独立版本验证闸;projection、immutable snapshot、时间权威、 无时钟 plan/receipt 和 restart capability 已附着原流程完成当前候选回归。四个主流程、全部 必需支线、2 h RSS 长稳态、重复波次和 critical-pressure 故障注入均已通过。平台主流程与支线的执行要求、 节点边界和证据入口详见 平台验证 runbook;延期、 保留、未排期、拒绝路径和非目标只以 统一延期、保留与非目标清单 为准。

NodeForge 的管理 API 仅供本机使用,目前不提供鉴权或 TLS。请将管理节点部署在受信任网络,并让 PXE 网卡与其他生产网段保持隔离。

核心能力

  • 单进程 PXE 服务:原生 DHCPv4、TFTP 与 HTTP,无需部署外部 PXE 服务栈。
  • 两种交付模式:支持本地盘无人值守安装,以及基于只读 rootfs + overlay 的 diskless 启动。
  • 分层配置:Profile 保存共享策略,Node 保存机器身份、物理绑定和单机 override,最终编译为 Effective plan。
  • 介质与制品管理:导入 ISO、识别安装源、构建 initrd/rootfs,并通过 Catalog 追踪来源和摘要。
  • 可恢复的长任务:ISO 导入和镜像构建由 daemon 持有;CLI 断开后仍可继续查询和跟踪。
  • 可观测部署:提供 readiness、boot preview、session trace、事件流、drift 和部署 generation。

v0.4 实现进度

v0.4.1–v0.4.18 的设计功能均已进入当前实现工作树。下表只记录实现落地状态;每个功能点的 平台验证入口、最近证据以及当前主流程/支线结论以 平台验证 runbook §2.0 及其对应小节为准。延期、保留、未排期、拒绝路径和非目标的唯一状态来源是 统一延期、保留与非目标清单,不能由平台验证 runbook 自行新增或改写。

版本 已落地功能范围 实现状态
v0.4–v0.4.3 fresh replacement/deployment identity、server-only rootfs、install/diskless 主流程、staging/repository、TFTP reactor、Profile/Node/EffectivePlan、BootRelease 与 asset transaction LANDED
v0.4.4–v0.4.6 agent inspect 与四程序 ABI、from-staging 换核 BootRelease 化、trace/log/debug/脱敏/节点归属 LANDED
v0.4.7–v0.4.8 clonefs 格式、capture/register/inspect、双 token grant、Range、TOCTOU/wipe、restore/postprocess 与本地启动 LANDED
v0.4.9–v0.4.11 OS-layer mode、minimal/full 配方、SoftwareResolution、RepositorySnapshot/PackageIndex、漂移闸、wizard 与 optional LANDED
v0.4.12 restore profile、live identity/plan、BootConfig/session、marker/re-entry、local boot、first-boot 与重新纳管 LANDED
v0.4.13 Bundle 生产路径清理、EffectivePlan/StepPatch/最终 identity、旧入口负向、fresh layout 2/catalog 7 LANDED
v0.4.14 managed-node remote builder、inventory/trust、receipt/grant、pinned SSH、HTTP blob、断点续传、durable publish 与 from-staging replace LANDED
v0.4.15 allocator 分层、service RSS budget/pressure、重型 operation 准入、status/live counters、8 GiB 默认、256 rootfs 下载槽与回滚 LANDED
v0.4.16 Install/Rootfs/Agent/Delivery projection、immutable plan/session pin、delivery drift 分类 LANDED(横切回归附着既有流程)
v0.4.17 control-plane time authority、Restore Plan V2、Receipt V2、server-only TTL、restart capability invalidation LANDED(横切回归附着既有流程)
v0.4.18 operation 归属事件、通用资产移除、测试输出治理 LANDED

LANDED 不等同于所有平台验证项已经 PASS:实现、自动化测试、真实 Linux 短时运行、 专项平台验证和发布级长稳态分别留存证据,当前候选整体裁决始终以 runbook 为准。

工作方式

ISO / repository
       │
       ▼
 Install Source ──► Profile ──► Node ──► Effective Plan
                                      │
                                      ▼
                            DHCP → TFTP → HTTP
                                      │
                         install 或 diskless 启动
程序 职责
nodeforged 承载 DHCP/TFTP/HTTP、Catalog 和运行时状态
nodeforge 通过 loopback 管理 API 操作本机 daemon
nodeforge-initrd diskless initramfs 中的 PID 1,负责下载、校验、overlay 和切根
nodeforge-agent 在目标 rootfs 内执行 pre-init 与 first-boot 阶段
nodeforge-imager 在统一 diskless rootfs 中捕获、检查和恢复 clonefs;仅在 nodeforge-agent 明确进入 restore 分支后由 first-boot 启动,恢复会擦除目标盘
nodeforge-builder 由管理面按需下发到已纳管构建节点;只依据已签 receipt 在节点本机构建或重打包 rootfs,不持有管理 API 凭据

CLI 使用指南

下面的流程足以在一台新的 Linux 管理节点上完成 fresh setup,并部署第一个 install 或 diskless 节点。命令参数、默认值和约束最终以当前二进制的 --help-full 为准。

1. 准备环境与构建

开发机需要 Zig 0.16、Git 和 GNU Make。管理节点需要 Linux、systemd、root 权限、一块连接 PXE 二层网络的专用网卡,以及足够保存 ISO、解包内容和 rootfs 的磁盘空间。示例中的 JSON 处理还使用 jq

# 开发构建;固定 build-time 可避免时间戳使 Zig 内容缓存整体失效。
zig build -Dbuild-time=2026-08-07T00:00:00Z
zig build test -Dbuild-time=2026-08-07T00:00:00Z

# ReleaseSafe 与 Linux 交叉构建。
zig build -Doptimize=ReleaseSafe
zig build -Dtarget=x86_64-linux-gnu -Doptimize=ReleaseSafe
zig build -Dtarget=aarch64-linux-gnu -Doptimize=ReleaseSafe

# 或直接生成发行包。
make dist-linux-amd64
make dist-linux-arm64

构建输出位于 zig-out/bin/。部署管理节点时必须把以下六个程序放在同一目录,且 version、commit、build time 和 dirty/clean provenance 必须一致:

nodeforge
nodeforged
nodeforge-initrd
nodeforge-agent
nodeforge-imager
nodeforge-builder

可在同步到管理节点前检查构建身份:

for program in nodeforge nodeforged nodeforge-initrd nodeforge-agent nodeforge-imager nodeforge-builder; do
  "zig-out/bin/$program" --version
done

2. Fresh setup

以下命令在 Linux 管理节点上以 root 执行。bind-interface 是 NodeForge 服务器连接 PXE 网络的网卡,不是目标节点安装完成后的接口名。DHCP 池应避开网关、管理节点和所有 reservation。

cd /path/to/nodeforge-artifacts

# 先预览;dry-run 不写盘,也不要求 --yes。
./nodeforge --install-root /opt/nodeforge setup \
  --dry-run --non-interactive \
  --service-memory-max 2GiB \
  --bind-interface enp1s0 \
  --server-ip 192.168.50.1 \
  --http-port 18080 \
  --subnet 192.168.50.0/24 \
  --pool-start 192.168.50.100 \
  --pool-end 192.168.50.200

# 初始化安装根、启动配置和 deployment identity。
./nodeforge --install-root /opt/nodeforge setup \
  --non-interactive --yes \
  --service-memory-max 2GiB \
  --bind-interface enp1s0 \
  --server-ip 192.168.50.1 \
  --http-port 18080 \
  --subnet 192.168.50.0/24 \
  --pool-start 192.168.50.100 \
  --pool-end 192.168.50.200

# 生成、安装、enable 并立即启动 systemd unit。
/opt/nodeforge/bin/nodeforge setup \
  --generate-systemd --install --non-interactive --yes

# 当前 shell 立即获得 PATH;新登录 shell 会自动加载。
source /etc/profile.d/nodeforge.sh

# 文件模型检查不要求 daemon;status 检查完整运行面。
nodeforged --check-config
nodeforge config validate
nodeforge catalog validate
nodeforge status

capacity.service_memory_max_bytes 是 daemon 自身 RSS 预算,setup 只接受整数二进制单位 B/KiB/MiB/GiB/TiB,最低为 2GiB。新建配置的交互式 setup 默认使用 8GiB(低内存 主机会给出按主机安全上限取整的默认值);非交互式 setup 在主机安全上限低于 8GiB 时必须 显式传入 --service-memory-max,上例用 2GiB 适配约 4GiB 的管理节点。该字段不是 systemd/cgroup 的 MemoryMax,daemon 会在 status 中报告 RSS、pressure 和重型 operation 准入状态,并在临界压力时拒绝新的重型工作。

指定已纳管节点执行 rootfs 构建时,管理面先冻结输入并下发一次性 work-root;如需在远端 staging 手工修改后覆盖同一输入摘要,必须在 prepare 时显式签发替换授权:

PREPARE=$(nodeforge profile rootfs prepare-remote PROFILE \
  --node NODE --work-root /data/nodeforge-build --output json)
OPERATION_ID=$(printf '%s\n' "$PREPARE" | jq -r '.result.id')

# 在受管节点上执行一次性、receipt-bound builder;builder 不连接 daemon,也不接受管理员 token。
nodeforge-builder rootfs status --work-root /data/nodeforge-build
nodeforge-builder rootfs build --work-root /data/nodeforge-build

# 回到管理节点验收并发布;--operation-id 绑定 prepare 返回的 durable operation。
nodeforge profile rootfs collect-remote PROFILE \
  --node NODE --work-root /data/nodeforge-build --operation-id "$OPERATION_ID"

# 传输/构建较慢时,CLI 超时不会取消 daemon 操作;使用 remote-operation 观察或取消。
nodeforge profile rootfs remote-operation show "$OPERATION_ID"
nodeforge profile rootfs remote-operation follow "$OPERATION_ID" --timeout 1800  # 或 wait
# 仅当 show/follow 仍显示非终态时取消:
nodeforge profile rootfs remote-operation cancel "$OPERATION_ID"

# 仅需要 receipt-bound from-staging 替换时使用:
nodeforge profile rootfs prepare-remote PROFILE --node NODE --work-root /data/nodeforge-build --replace-existing

prepare-remote 会冻结 profile 的 rootfs input、生成 work-root、receipt、签名和一次性 operation;不要手工修改 receipt、meta 或 input。只有 nodeforge-builder rootfs build 返回 state=completed 后才执行 collect-remote。普通构建不能覆盖已有内容;--replace-existing 只授权一次 receipt-bound --from-staging 替换,仍需满足 input digest、receipt、签名和 输出验真。

systemctl is-active 只说明进程已启动,不代表 DHCP、TFTP、HTTP、Catalog 和管理 API 均已 ready;最终应以 nodeforge status 为准。

默认安装布局:

/opt/nodeforge/
├── bin/       # 管理宿主机制品;builder 按需下发,imager 作为统一 diskless rootfs companion 注入目标环境
├── config/    # 启动配置
├── catalog/   # Profile、Node、资源及关系
├── assets/    # ISO、boot、diskless、repo 和 key
├── state/     # lease、session 和部署状态
├── logs/      # 服务日志与事件流
├── work/      # 导入和构建暂存
└── systemd/   # 生成的 unit

3. CLI 约定与命令入口

先区分命令的执行平面:

平面 典型命令 完成语义
本地文件 setupconfig validatecatalog validate 当前进程完成文件校验或事务
Loopback API statusassetsprofilenoderuntime CLI 向本机 daemon 查询或提交变更
Durable operation ISO import、initrd/rootfs build 工作由 daemon 持有;CLI 断开或超时不会取消任务

--install-root 是根级 bootstrap 参数,放在子命令之前:

nodeforge --install-root /srv/nodeforge-a status

人工操作默认使用 human 输出;脚本使用 JSON/JSONL,并检查退出码:

nodeforge --help
nodeforge --help-full
nodeforge profile create --help-full
nodeforge node set --help-full

nodeforge status --output json | jq .
nodeforge node list --output json | jq '.result'
nodeforge node show node-01 --output json >node-01.snapshot.json

--output 当前只接受 humanjsonjsonl 三种值;列表的表格展示属于 human, 不是独立的 table 输出模式。脚本应使用 json/jsonl 并同时检查退出码;传入 --output table 会按 CLI 用法错误拒绝。

常用入口:

入口 用途
setup 安装根、启动配置、systemd、repair/reset/purge
status daemon 端到端 readiness
assets ISO、install source、initrd、kernel/default 和 provision asset
profile install/diskless/restore 共享策略与 rootfs
node 物理节点、override、deploy、session 和 trace
operation 查询和跟踪 daemon 持有的长任务
events / runtime 审计事件、DHCP lease、未知客户端和 TFTP 状态
discovery 观察并认领未知 PXE 客户端
config / catalog 本地校验、导出和查看事实模型

完整命令树、属性约束和退出码以本 README 与当前二进制的 --help-full 为准。

Repository ISO 与安装 ISO 是两个明确的导入模式。仓库介质使用:

nodeforge assets repository import \
  /srv/iso/vendor-repository.iso
nodeforge assets repository list

模式由命令入口显式选择,发行版 tuple 来自盘内 metadata 或 tuple flags;文件名即使被 任意重命名也不参与类型或 tuple 判断,只影响默认 logical source name(可用 --name 固定)。该命令只纳管介质中发现的 RPM repository,不会创建 ISO/kernel/initrd 资产、 InstallSource 或默认 Profile;安装介质仍使用 assets import

若 ISO 通过只读共享目录提供,可直接把共享路径传给 CLI,无需预先复制到节点业务盘:

nodeforge --install-root /opt/nodeforge assets repository import \
  /mnt/shared/vendor-repository.iso \
  --output json

共享目录必须是只读且不在 --purge-all 清理范围内。CLI 为 daemon 协议在 /opt/nodeforge/work/iso-import-<operation>/input.iso 产生的临时 staging 副本是内部实现 步骤,不是手工上传或长期资产;成功、失败、重复或拒绝后都必须确认该副本已清理。带 --distro/--version/--arch 的重复请求会要求 --yes 重新验证媒体 tuple;已识别的 .treeinfo tuple 不允许被错误覆盖。

安装介质导入完成后,RHEL/Anaconda 不再额外保留一份原始 ISO:它只保留展开后的 content-addressed repository tree 和 kernel/initrd/bootloader;InstallSource 保存 media_sha256 用于幂等与替换校验。Ubuntu live-server 因 casper 需要通过 HTTP 下载 完整 ISO,仍会保留对应 ISO asset。CLI 暂存 ISO 不是长期资产,导入收尾后会删除。

外部包仓库、额外软件包、kernel_args 和 RAID 启动盘不属于下面的部署主流程,统一放在 第 6 节“附加配置”。先按流程 A 或流程 B 完成一次基线部署,再按需应用附加配置;附加配置 应用后必须重新执行 catalog validateprofile software shownode software shownode rendernode boot preview。如果附加配置要用于下一次安装或 rootfs 重建,则在对应的 node retry/profile rootfs build 前应用即可,但不要把它混入主流程的固定命令序列。

4. 主流程 A:本地盘无人值守安装

以下示例导入 Rocky Linux ISO,并创建一个 install Profile。导入后的 source 名称必须以 install-source list 输出为准。

SOURCE=rocky-9.7-aarch64-dvd
PROFILE=${SOURCE}-install
NODE=node-01
BOOT_DISK=/dev/nvme0n1  # VMware Fusion ARM 示例;必须替换为目标机实际磁盘

# 导入 ISO;默认跟随 durable operation 到终态。
nodeforge assets import /srv/iso/Rocky-9.7-aarch64-dvd.iso
nodeforge assets install-source list
nodeforge assets install-source show "$SOURCE"
nodeforge catalog validate

# 从完整 install source identity 创建 Profile。
nodeforge profile create "$SOURCE" --kind install
nodeforge profile show "$PROFILE"
nodeforge profile capabilities show "$PROFILE"

# Profile 默认使用单盘、擦除目标盘(wipe=true);RAID、内核参数、额外源和额外包见第 6 节。

# 先以 deploy=false 登记节点;pxe.ip_reservation 是 PXE bootstrap reservation。
nodeforge node add "$NODE" \
  mac=00:50:56:2a:23:db \
  arch=aarch64 \
  profile="$PROFILE" \
  pxe.ip_reservation=192.168.50.110 \
  storage.boot_disk="$BOOT_DISK" \
  deploy=false

# 开放 PXE 前检查 stored/effective 配置、安装答案和启动决策。
nodeforge node show "$NODE"
nodeforge node software show "$NODE"
nodeforge node render "$NODE"
nodeforge node boot preview "$NODE"

# install profile 的首次 PXE 必须由服务端原子开启 deploy 并 arm generation。
# 这里使用 retry,而不是把 node deploy true 当作安装动作。
nodeforge node retry "$NODE"
nodeforge node trace "$NODE" --latest
nodeforge events list --node "$NODE" --limit 100
nodeforge node show "$NODE"

对 install profile,node deploy true 只修改 deploy gate,不会为一个此前以 deploy=false 登记的节点创建 install generation。若节点添加时已经确认配置可直接 部署,也可以改用 node add ... deploy=true,由服务端创建初始 generation;若先以 deploy=false 检查配置,则必须使用上面的 node retry

storage.boot_disk 不能照抄示例:先用目标机/VM 的磁盘事实确认 /dev/nvme0n1/dev/sda 或其他实际路径,再用 node set/node show/node render 确认 Kickstart/Autoinstall 的 clearpart、bootloader 和目标盘一致;磁盘路径错误属于安装配置 失败,不是 PXE 传输失败。

失败后重试、重新安装或显式 rearm generation 时使用:

nodeforge node retry node-01
nodeforge node retry node-01 --force  # 仅在确认要替代冲突中的活动状态时使用
nodeforge node deploy node-01 false   # 关闭后续 PXE 部署

安装器完成后,先用管理面确认 generation 已到 completed 且 plan digest clean;Fusion 或固件侧再关机并把 Startup Disk 切到本地 Hard Disk,启动后在 guest 内验收根盘、systemd 和失败单元。VMware 的开关机、网络适配器和 Startup Disk 选择必须通过 Fusion GUI 完成, CLI 只负责控制面和证据收集:

nodeforge node show node-01 --output json
nodeforge node trace node-01 --latest
nodeforge events list --node node-01 --limit 100
nodeforge node postprocess show node-01 --phase install-post --generation 7 --output json

# Fusion GUI:关机 -> Startup Disk: Hard Disk (NVMe) -> 启动本地盘。
# guest 内:findmnt /;systemctl is-system-running;systemctl --failed --no-legend。

# 本地盘验收结束后,关闭后续 PXE,避免下一次启动再次进入安装器。
nodeforge node deploy node-01 false
nodeforge node session list --output json
nodeforge status --output json | jq '.result | {active_sessions,rootfs_downloads_active,live_counters}'

如果 profile 没有 install_post steps,install-post 不需要单独执行;查询该 generation 应返回 run:null。restore 的 agent 入口则固定由统一 diskless rootfs 的 nodeforge-firstboot.service 调用:rootfs 已切入且 systemd 接管 PID 1 后, nodeforge-agent first-boot 识别 restore BootConfig,才启动 nodeforge-imager;sshd 由发行版 unit 独立启动,不是 first-boot 的排序条件,也不等待 22/tcp。

5. 主流程 B:Diskless build 与启动

Diskless 是独立的部署主流程;如果 InstallSource 尚未存在,先从 ISO 导入并完成能力校验, 然后再构建 NodeForge initrd、发布 source default kernel,并创建内联 boot selection 的 diskless Profile:

SOURCE=rocky-9.7-aarch64-dvd
ISO=/srv/iso/Rocky-9.7-aarch64-dvd.iso
KERNEL_RELEASE=5.14.0-611.5.1.el9_7.aarch64
INITRD=rocky-9.7-nodeforge-initrd
PROFILE=${SOURCE}-diskless
NODE=diskless-01

# 1. 导入并检查安装介质。若 SOURCE 已由其他流程导入,可跳过 import,仅执行 list/show。
nodeforge assets import "$ISO"
nodeforge assets install-source list
nodeforge assets install-source show "$SOURCE"
nodeforge catalog validate

# 2. 在厂商 initrd 上追加 NodeForge overlay。
nodeforge assets initrd build "$INITRD" \
  --from-install-source "$SOURCE" \
  --kernel-release "$KERNEL_RELEASE"

# 3. 发布 InstallSource 的 source_default kernel;启动面由 Profile inline selection 和 BootRelease 管理。
nodeforge assets kernel default set "$SOURCE" "${SOURCE}-kernel"

# 4. 创建带内联 kernel/initrd 选择的 diskless Profile,读取抗漂移 digest,再构建 rootfs。
nodeforge profile create "$SOURCE" --kind diskless --boot-initrd "$INITRD"
DIGEST=$(nodeforge profile rootfs plan "$PROFILE" --output json | \
  jq -r '.result.rootfs_input_digest')
test -n "$DIGEST" && test "$DIGEST" != null
nodeforge profile rootfs build "$PROFILE" --if-input-digest "$DIGEST"
nodeforge profile rootfs status "$PROFILE"

# 5. 登记节点,确认 boot readiness,再开放部署闸。
nodeforge node add "$NODE" \
  mac=00:50:56:2a:23:dc \
  arch=aarch64 \
  profile="$PROFILE" \
  pxe.ip_reservation=192.168.50.111 \
  deploy=false
nodeforge node readiness "$NODE" --stage boot
nodeforge node boot preview "$NODE"
# diskless 也使用统一的生命周期动作;服务端会开启 deploy 并等待下一次 PXE。
nodeforge node retry "$NODE"
nodeforge node session list
nodeforge node trace "$NODE" --latest
nodeforge events list --node "$NODE" --limit 100
nodeforge node show "$NODE"

diskless 没有 install generation gate,单独执行 node deploy "$NODE" true 也能开启 新 PXE;验证文档统一使用 node retry,以避免把 install 与 diskless 的生命周期语义混淆。 启动后用 node traceeventsnode session show/list 确认已到 diskless.running,并在 Fusion/固件侧关机后收敛部署状态:

nodeforge node trace "$NODE" --latest
nodeforge events list --node "$NODE" --limit 100
nodeforge node session list --output json
nodeforge node deploy "$NODE" false
nodeforge node session list --output json
nodeforge status --output json | jq '.result | {active_sessions, rootfs_downloads_active, live_counters}'

需要从访客机采集内核、systemd、磁盘和网络 facts 时,先把运维公钥放进 Profile 的 system.ssh.root_authorized_keys,再重新生成 rootfs;不要依赖每次 diskless 启动临时生成的 SSH host key,也不要把私钥写入 Profile:

nodeforge profile add-values "$PROFILE" system.ssh.root_authorized_keys \
  'ssh-ed25519 AAAA... ops@example'
nodeforge profile rootfs build "$PROFILE" --if-input-digest \
  "$(nodeforge profile rootfs plan "$PROFILE" --output json | jq -r '.result.rootfs_input_digest')"

升级 nodeforge-initrd/nodeforge-agent 后,如果同一 source、kernel 和 initrd identity 已经有 artifact,使用显式 --rebuild 重新生成 diskless initrd。它按内容寻址发布新文件, 旧 artifact 保留;有活动 boot session 时会拒绝,避免改变正在交付的文件。无需升级时重复 执行会返回 already_present,不应为了这个结果强制重建:

nodeforge assets initrd build "$INITRD" \
  --from-install-source "$SOURCE" \
  --kernel-release "$KERNEL_RELEASE" \
  --rebuild --output json

需要进入保留的 rootfs staging 或从树内导入内核时,使用 profile rootfs staging enter|exec|kernels;具体约束见 v0.4.1 设计

v0.4.7–v0.4.12 主流程:Clonefs 捕获、授权与恢复

这条流程用于把一台已经完成安装的源机捕获为 clonefs,登记到管理节点,再恢复到另一台目标机的本地盘。它不是 install/diskless 的替代流程,而是建立在统一 diskless rootfs 之上的敏感数据恢复流程。clonefs 可能包含主机名、machine-id、SSH 主机密钥和网络配置;恢复会隐式擦除目标盘,必须先核对盘路径、容量和身份。

v0.4.12 已提供无人值守恢复入口。可以先通过当前 CLI 创建并校验 kind=restore 的 restore Profile;其中 environment-profile 只需指向同一 install source 下的 diskless Profile, 统一 rootfs 会尽力安装 imager 依赖并自动注入 nodeforge-imagertarget-profile 必须是恢复后本地启动的 install Profile,clonefs 可留空并在 node restore --clonefs 时按本次调用覆盖。然后使用 node restore:管理面会冻结存储事实、武装一次 restore session,目标机下一次 PXE 进入统一 diskless rootfs;切根后由 systemd 接管 PID 1,非阻塞的 first-boot unit 调用 nodeforge-agent 的 restore 分支,再由 agent 启动 nodeforge-imager。sshd 由发行版 自己的 unit 独立启动,不是 imager 的前置 TCP 检查;成功后切换 boot_policy=localdeploy=false 并衔接 first-boot。需要显式审阅 plan 或验证手动恢复边界时,再使用 下面保留的 v0.4.8 grant/plan/restore 路径。

nodeforge profile create <INSTALL_SOURCE> --kind restore \
  --environment-profile <DISKLESS_PROFILE> \
  --target-profile <INSTALL_PROFILE> \
  --clonefs <CLONEFS_DIGEST> \
  --network-inject replace --output json
nodeforge node restore <TARGET_NODE> \
  --profile <RESTORE_PROFILE> \
  --clonefs <CLONEFS_DIGEST> \
  --ttl 3600 --yes --output json
nodeforge node restore-status <TARGET_NODE> --output json
# restore 已经进入 diskless 且 agent 正在等待时,原地重置为 execute_pending;不 PXE、不 SSH:
nodeforge node retry <TARGET_NODE> --output json
# 取消尚未完成的恢复并回退 profile 时:
# nodeforge node restore-cancel <TARGET_NODE> --yes --output json

node restore--clonefs 使用已登记 clonefs 的 128 位 clonefs_digest;目标盘 live identity 不能由 Node 上缓存的设备路径代替。--force-rerestore 只有在明确确认目标盘 上的完成标记、session 和 execution claim 后才可使用。

restore 的节点意图由 node restore 配置,不由 node retry 猜测。node retry 按当前 Profile kind 分派:restore profile 的失败 session 只在当前统一 diskless 环境中 re-arm 同一 restore session;install/diskless profile 才走普通部署重试。restore 成功后服务端把节点切到 target_profileboot_policy=localdeploy=false,后续不会自动再次进入 diskless 或 install;运维若明确执行 node retry,才表示一次显式的 install retry。完成事件只有在这次 节点意图收敛成功后才提交;若 catalog 写入/发布失败,session 保持可重放状态,不会误重启 到下一次 PXE 继续 restore。

管理节点、源机和目标机必须使用同一版本、commit、target triple 的 nodeforge-imager。目标机的捕获/恢复环境必须从统一 diskless Profile 启动;rootfs 构建自动注入 imager 并尽力补充 restore 工具依赖。可选包不完整时,rootfs 仍可启动,但 restore preflight 会明确报告缺失项。restore 完成后节点默认回到本地启动且关闭 deploy;再次进入 diskless/install 必须由运维显式执行。源盘不能在捕获或恢复时作为当前 root、/var/tmp 或 clonefs 源文件所在盘。

MGMT_URL=http://192.168.50.1:18080
SOURCE=rocky-10.2-aarch64-dvd
INITRD=rocky-10.2-nodeforge-initrd
CAPTURE_PROFILE=${SOURCE}-diskless
CAPTURE_NODE=imager-source-01
TARGET_NODE=restore-target-01
SOURCE_BOOT_DISK=/dev/nvme0n1
TARGET_BOOT_DISK=/dev/nvme0n1
CLONEFS_OUT=/var/tmp/rocky-10.2-${CAPTURE_NODE}.clonefs
PLAN=/var/tmp/${TARGET_NODE}.restore-plan.json
GRANT=/var/tmp/${TARGET_NODE}.restore-grant.json
  1. 准备统一 diskless 捕获环境。rootfs 构建会自动注入 nodeforge-imager,并尽力安装其依赖;构建完成后让源机以 PXE/diskless 方式启动,并确认 SSH BatchMode 可用。
nodeforge assets install-source show "$SOURCE"
nodeforge assets initrd build "$INITRD" \
  --from-install-source "$SOURCE" \
  --kernel-release 6.12.0-211.16.1.el10_2.0.1.aarch64
nodeforge assets kernel default set "$SOURCE" "${SOURCE}-kernel"
nodeforge profile create "$SOURCE" --kind diskless \
  --boot-initrd "$INITRD"

ROOTFS_INPUT=$(nodeforge profile rootfs plan "$CAPTURE_PROFILE" --output json | \
  jq -r '.result.rootfs_input_digest')
nodeforge profile rootfs build "$CAPTURE_PROFILE" --if-input-digest "$ROOTFS_INPUT"
nodeforge profile rootfs status "$CAPTURE_PROFILE"

nodeforge node add "$CAPTURE_NODE" \
  mac=00:50:56:aa:bb:cc \
  arch=aarch64 profile="$CAPTURE_PROFILE" \
  pxe.ip_reservation=192.168.50.111 deploy=false
nodeforge node render "$CAPTURE_NODE"
nodeforge node readiness "$CAPTURE_NODE" --stage boot
nodeforge node retry "$CAPTURE_NODE"

# 目标机也必须以同一 diskless Profile 启动;MAC、IP 和 node 名替换为目标机事实。
nodeforge node add "$TARGET_NODE" \
  mac=00:50:56:dd:ee:ff \
  arch=aarch64 profile="$CAPTURE_PROFILE" \
  pxe.ip_reservation=192.168.50.112 deploy=false
nodeforge node render "$TARGET_NODE"
nodeforge node readiness "$TARGET_NODE" --stage boot
nodeforge node retry "$TARGET_NODE"
  1. 在 diskless 源机上捕获并在管理节点登记。默认 --out 使用原子 .part 文件和本地 clonefs 校验;--stdout 只适合管道,stdout 是二进制,诊断仍在 stderr,不能和 --output json 混用。

多盘 clonefs 重复传入 --disk;恢复时对每个成员盘重复传入 --confirm--boot-disk 会作为 member0,其余成员按 disks[] 的 role/identity 进入 plan,不能只确认 boot disk。

nodeforge imager capture \
  --node "$CAPTURE_NODE" \
  --boot-disk "$SOURCE_BOOT_DISK" \
  --out "$CLONEFS_OUT" \
  --output json

nodeforge-imager inspect "$CLONEFS_OUT" --output json
nodeforge imager register "$CLONEFS_OUT" \
  --name "${SOURCE}-${CAPTURE_NODE}" --output json
nodeforge imager list --output json
nodeforge imager show "${SOURCE}-${CAPTURE_NODE}" --output json

CLONEFS_DIGEST=$(nodeforge imager show "${SOURCE}-${CAPTURE_NODE}" --output json | \
  jq -r '.result.clonefs_digest')
test "${#CLONEFS_DIGEST}" -eq 128

# 若不使用管理 wrapper,可在源 diskless 节点直接调用;stdout 仅为二进制 clonefs。
# ssh "$CAPTURE_NODE" nodeforge-imager clone \
#   --boot-disk "$SOURCE_BOOT_DISK" --stdout > "$CLONEFS_OUT"
  1. 先用一次性 download grant 在目标统一 diskless rootfs 上执行非破坏性 dry-run。此步骤会读取目标盘的 live identity;输出中的 result.disks[] 是后续 restore plan 唯一合法的盘身份来源。

如果管理面已有明确的 diskless/imager session,可在 grantgrant-restore 上追加同一个 --session SESSION_ID,把授权绑定到该 session;不绑定时仍会按 node、digest 和 TTL 校验。

nodeforge imager grant "$CLONEFS_DIGEST" \
  --node "$TARGET_NODE" --ttl 3600 --output json > /var/tmp/${TARGET_NODE}.download-grant.json
jq -r '.result.url' /var/tmp/${TARGET_NODE}.download-grant.json > /var/tmp/${TARGET_NODE}.clonefs-url
jq -r '.result.token' /var/tmp/${TARGET_NODE}.download-grant.json > /var/tmp/${TARGET_NODE}.download.token
chmod 600 /var/tmp/${TARGET_NODE}.download.token
scp /var/tmp/${TARGET_NODE}.download.token "$TARGET_NODE:/var/tmp/"
CLONEFS_URL=$(cat /var/tmp/${TARGET_NODE}.clonefs-url)

# 以下命令在目标机统一 diskless shell 中执行;先设置同名的 CLONEFS_URL、CLONEFS_DIGEST、TARGET_BOOT_DISK。
# --dry-run 不会 begin、wipe 或写盘。
/usr/sbin/nodeforge-imager restore \
  --source "$CLONEFS_URL" \
  --expected-digest "$CLONEFS_DIGEST" \
  --token-file /var/tmp/${TARGET_NODE}.download.token \
  --boot-disk "$TARGET_BOOT_DISK" \
  --confirm "$TARGET_BOOT_DISK" \
  --dry-run --quiet 2> /var/tmp/${TARGET_NODE}.restore-dry-run.json
  1. 这是显式 Restore Plan V2 的手动路径:用 dry-run 快照生成恢复计划。v0.4.12 的 node restore 自动路径会由管理面消费 live identity 并生成同一语义的 V2 plan;手动路径这里用 jq 把目标机返回的 disks[] 原样嵌入计划。V2 plan 不包含 issued_at/expires_at;node、digest、URL 或 postprocess 被改动后,必须重新 dry-run 并重新授权。grant 的 TTL 是 nodeforged 的 server-side envelope,目标机不得用本地时间判断 plan 是否过期。
jq \
  --arg node "$TARGET_NODE" \
  --arg digest "$CLONEFS_DIGEST" \
  --arg url "$CLONEFS_URL" \
  --arg hostname "restored-${TARGET_NODE}" \
  '{schema_version: 2,
    kind: "nodeforge.restore_plan",
    node_id: $node,
    clonefs_digest: $digest,
    source: {url: $url},
    disks: .result.disks,
    postprocess: {
      hostname: $hostname,
      network_file: null,
      regen: ["ssh-host-keys", "machine-id"]
    }}' \
  /var/tmp/${TARGET_NODE}.restore-dry-run.json > "$PLAN"

nodeforge imager grant-restore \
  --plan "$PLAN" --node "$TARGET_NODE" --ttl 3600 \
  --output json > "$GRANT"
jq -r '.result.download_token' "$GRANT" > /var/tmp/${TARGET_NODE}.download.token
jq -r '.result.execution_token' "$GRANT" > /var/tmp/${TARGET_NODE}.execution.token
chmod 600 /var/tmp/${TARGET_NODE}.{download,execution}.token

# 通过受信任的 SSH 通道把 plan 和 token 文件送到目标统一 diskless 环境;不要把 token 展开到命令行。
scp "$PLAN" \
  /var/tmp/${TARGET_NODE}.{download,execution}.token \
  "$TARGET_NODE:/var/tmp/"
ssh "$TARGET_NODE" chmod 600 \
  /var/tmp/${TARGET_NODE}.{restore-plan.json,download.token,execution.token}

grant-restore 只接受指向该节点 nodeforged clonefs endpoint 的 source.url;管理面自动生成的 plan 还带有 source seal。修改 URL、node 或磁盘 identity 后必须重新生成 plan 并重新授权。

  1. 在同一个目标统一 diskless 环境中先验证计划,再执行真实恢复。计划路径强制在线访问管理节点;download_token 只用于 clonefs 下载,execution_token 只用于 verify/begin,不能互换,也不能写入 plan 或命令行参数。
# 计划 dry-run:会在线 verify,但不会 begin 或写盘。
/usr/sbin/nodeforge-imager restore \
  --plan "$PLAN" \
  --execution-token-file /var/tmp/${TARGET_NODE}.execution.token \
  --download-token-file /var/tmp/${TARGET_NODE}.download.token \
  --management-url "$MGMT_URL" \
  --dry-run

# 人工复核上一步输出后,去掉 --dry-run 才会 begin → wipe → 分区/格式化 → 写入 payload → postprocess。
/usr/sbin/nodeforge-imager restore \
  --plan "$PLAN" \
  --execution-token-file /var/tmp/${TARGET_NODE}.execution.token \
  --download-token-file /var/tmp/${TARGET_NODE}.download.token \
  --management-url "$MGMT_URL"

本地文件源也可不经过 grant,使用 --source /absolute/file.clonefs --expected-digest HEX--confirm /dev/...;计划路径仍必须在线 verifypostprocess 是已恢复 root 的独立工具,只有在不使用 plan 的手动/测试场景才单独调用:

/usr/sbin/nodeforge-imager postprocess \
  --target /mnt/restored-root \
  --hostname restored-target-01 \
  --regen ssh-host-keys,machine-id \
  --network-file /root/target.nmconnection
  1. 恢复后从目标本地盘启动,再做验收和清理。VMware Fusion 的开机、关机和选择本地盘启动属于平台 GUI/固件动作,不由本 CLI 代替;启动完成后使用 CLI 检查管理面状态。
# 目标机内核/根盘/网络/服务验收。
cat /etc/hostname
cat /etc/machine-id
findmnt / /boot /boot/efi
systemctl is-system-running
systemctl --failed --no-legend
nmcli -t -f NAME,DEVICE,STATE connection show --active

# 管理面收尾:不保留 diskless session,也不让下一次 PXE 重写本地盘。
nodeforge node session list --output json
nodeforge node deploy "$TARGET_NODE" false
nodeforge node show "$TARGET_NODE" --output json

安全和重试规则:目标身份变化、容量不足、verify/begin 不可达、401/403、409 或 begin 响应不确定时均 fail closed,不会安全地“继续写”。一旦 begin 成功后进程失败或结果不确定,先停止/隔离目标机并确认恢复进程已退出,再由管理面显式处理旧 attempt;不能复用旧 execution token,必须重新生成 plan 和 grant-restore。任何 clonefs .part、token 文件和包含 token 的 grant JSON 都应在验收后按站点保留策略清理。

6. 附加配置:主流程完成后的 Profile 与 Node 修改

本节不是安装/无盘主流程的固定步骤。先完整执行一个主流程,并确认 node shownode tracenode eventsprofile rootfs status 已达到预期,再按需应用内核参数、存储拓扑、额外 软件包和外部仓库。附加配置不改变 install/diskless 的生命周期骨架:如果配置要用于下一次 安装、重装或 rootfs 重建,可以在下一次 node retry/profile rootfs build 之前应用;不要 把这些可选项插入主流程的必需命令中。

配置命令按数据类型分开,混用会返回用法错误:

类型 命令 示例
scalar set / unset node set node-01 storage.boot_disk=/dev/sda
scalar collection add-values / remove-values / replace-values / clear-values profile add-values p kernel_args iommu=pt
structured collection item / replace-items / clear-items provisioning step、mount、route 等对象

先用 profile set --help-fullnode set --help-full 查询当前版本支持的 key、类型、owner 与约束。下面的变量假定 $PROFILE$NODE 已由前述安装或无盘流程创建:

PROFILE=rocky-9.7-aarch64-dvd-install
NODE=node-01
nodeforge profile set --help-full
nodeforge node set --help-full

6.1 静态网络与拓扑

目标系统静态网络只影响安装完成后的系统配置,PXE bootstrap 仍使用 DHCP:

nodeforge node set "$NODE" \
  network.mode=static \
  network.address=192.168.50.110 \
  network.prefix_len=24 \
  network.gateway=192.168.50.1
nodeforge node add-values "$NODE" network.dns 192.168.50.1 8.8.8.8

已设置 pxe.ip_reservation 时,静态 network.address 必须与它一致。多网卡、bond、VLAN 和 route 使用 topology JSON,可在提交前纯校验:

nodeforge node topology validate \
  --network-json topology.json \
  --bootstrap-mac 00:50:56:2a:23:db \
  --deploy

6.2 内核参数

kernel_args 是集合,Profile 使用 replace-values 整体替换,Node 使用 overrides.kernel_args.add/remove 做节点级增量:

nodeforge profile replace-values "$PROFILE" \
  kernel_args iommu=pt amd_iommu=on intremap=on
nodeforge node add-values "$NODE" \
  overrides.kernel_args.add hugepages=4

nodeforge profile show "$PROFILE"
nodeforge node render "$NODE"
nodeforge node boot preview "$NODE"

6.3 RAID 启动盘

RAID 启动盘只在确认目标机磁盘事实后配置。选择容量和总线相近的两块空盘,把第一块设为 storage.boot_disk,第二块追加到 storage.additional_disks;不要把第三块盘或仍有数据的 盘混入 effective members:

RAID_BOOT_DISK=/dev/sda  # 替换为目标机实际设备
RAID_MEMBER_DISK=/dev/sdb  # 替换为第二块容量/总线相近的空盘
nodeforge node set "$NODE" storage.boot_disk="$RAID_BOOT_DISK"
nodeforge node add-values "$NODE" storage.additional_disks "$RAID_MEMBER_DISK"
nodeforge node set "$NODE" overrides.install.storage.mode=raid1

nodeforge node show "$NODE"
nodeforge node render "$NODE"
nodeforge node boot preview "$NODE"

当前 Kickstart RAID1 渲染会在主盘放置 EFI 分区,并在每个从盘预留同尺寸的未挂载 EFI 分区, 再让 /boot/ 从两个成员组成 RAID1(默认 md 设备名为 md1md2)。安装前的 %pre 只清理 effective members 上的旧分区和 mdadm 签名;因此必须在 node render 中确认 members、clearpart、bootloader 和目标盘都符合预期。其他 RAID 模式和自定义分区也沿用同一 个 effective storage 入口,成员数量及分区约束以 --help-full 和 render 结果为准。

6.4 自定义分区

自定义分区使用 install.storage.partitions 结构化条目。空列表本身表示“使用自动布局”; 第一次对 espbootroot 执行 item set 时,CLI 会先把这三个隐式默认项物化, 然后再用 item add 增加普通分区。filesystem 省略时,普通分区(plainbootroot)默认使用 ext4;只有显式填写 filesystem=xfs 等值时才会使用其他格式。 ESP 和 swap 仍分别使用 EFI/FAT 和 swap 的专用默认值。

这里不能使用 profile add-valuesadd-values 只适用于字符串/标量集合,例如 kernel_argssoftware.repositoriessoftware.packages.include;分区条目包含 idkindmountsize_mibgrow 等字段,并且有明确顺序,必须使用 profile item add|set|move,或使用 profile replace-items 整体替换列表。

下面的例子在 Profile 中设置 1 GiB EFI、2 GiB /boot、4 GiB /var,并把剩余空间交给 根分区。var 没有指定 filesystem,渲染结果应为 ext4:

# 物化并调整自动的 esp/boot/root;普通分区 filesystem 故意省略。
nodeforge profile item set "$PROFILE" install.storage.partitions \
  esp mount=/boot/efi size_mib=1024 grow=false
nodeforge profile item set "$PROFILE" install.storage.partitions \
  boot mount=/boot size_mib=2048 grow=false
nodeforge profile item set "$PROFILE" install.storage.partitions \
  root mount=/ size_mib=1 grow=true
nodeforge profile item add "$PROFILE" install.storage.partitions \
  id=var kind=plain mount=/var size_mib=4096 grow=false
nodeforge profile item move "$PROFILE" install.storage.partitions var --before root

nodeforge catalog validate
nodeforge profile show "$PROFILE"
nodeforge node render "$NODE"
nodeforge node boot preview "$NODE"

启用 RAID1 时,仍然通过 storage.boot_diskstorage.additional_disks 选择成员:

nodeforge node set "$NODE" storage.boot_disk=/dev/sdb
nodeforge node replace-values "$NODE" storage.additional_disks /dev/sdc
nodeforge node set "$NODE" overrides.install.storage.mode=raid1
nodeforge node show "$NODE"
nodeforge node render "$NODE"

Kickstart/Anaconda 不把 EFI 放进 mdX:UEFI 固件通常不能直接读取 md RAID。主盘的 ESP 挂载为 /boot/efi,其他 RAID 成员创建同尺寸但不挂载的 ESP;/boot/var/ 等 普通分区才组成 md RAID。这与自动分区 RAID 的布局一致,也为以后向备用盘复制 EFI 引导文件 保留了空间。提交安装前必须检查 node render 中的 ignorediskclearpart、每个 --ondiskraid 行,确认没有把数据盘误纳入 effective members。

6.5 额外软件包

额外包可以作为 Profile 基线,也可以只追加到一个 Node。若包不在导入介质中,先按 6.6 登记并选择外部仓库,再把包加入计划:

# Profile 基线:集合操作不会重复追加同名包。
nodeforge profile add-values "$PROFILE" \
  software.packages.include tmux vim-enhanced

# 仅节点追加:不改变共享 Profile。
nodeforge node add-values "$NODE" \
  overrides.software.packages.include.add tmux
nodeforge node add-values "$NODE" \
  overrides.software.packages.exclude.add telnet

6.6 外部 APT/DNF/YUM/FTP 仓库

assets repository add 只把稳定 URL、distro tuple 和包管理器元数据登记进 catalog,不下载 或镜像站点。对应的 distro/version/arch 必须已经由前述安装介质导入流程的 assets import 建立能力; 登记后还要用 profile/node add-valuesreplace-values 选择仓库名称,仓库才会进入 effective install、rootfs、diskless AgentPlan 和 first-boot 配置。

# Ubuntu/Debian:--manager 可省略,daemon 会根据 tuple 推导 apt。
nodeforge assets repository add \
  --name ubuntu-jammy-main \
  --distro ubuntu --version 22.04 --arch aarch64 \
  --url https://mirrors.example.test/ubuntu \
  --suite jammy --components main,universe

# Rocky/RHEL/YUM:--manager 可省略,daemon 会根据 tuple 推导 dnf;不要填写
# APT 专用的 --suite/--components。FTP 是直接仓库根 URL,通常应包含 repodata/repomd.xml。
nodeforge assets repository add \
  --name rocky-10-2-appstream-ftp \
  --distro rocky --version 10.2 --arch aarch64 \
  --url ftp://mirror.example.local/rocky/10.2/AppStream

# 选择外部源;replace-values 替换整个集合,add-values 只追加唯一值。
nodeforge profile add-values "$PROFILE" \
  software.repositories rocky-10-2-appstream-ftp
nodeforge node add-values "$NODE" \
  overrides.software.repositories.add rocky-10-2-appstream-ftp

# 如果只保留指定源,可替换 Profile 的整个集合:
nodeforge profile replace-values "$PROFILE" \
  software.repositories rocky-10-2-appstream-ftp

# 检查自动推导的 manager(Rocky 应为 dnf,Ubuntu 应为 apt)。
nodeforge assets repository show rocky-10-2-appstream-ftp --output json | \
  jq '.result.repository.manager'

--manager 默认省略时,daemon 根据已注册的 distro/version/arch 能力自动选择 apt (Ubuntu/Debian)或 dnf(Rocky/RHEL/YUM 兼容仓库);显式填写 --manager apt|dnf 仍会 与同一 tuple 做一致性校验。外部 URL 支持直接的 http://https://ftp://,不支持 动态 mirrorlist/metalink,也不接受凭据、query、fragment、file:// 或 NodeForge 自有 /artifacts/repositories/ 路径。DNF/YUM 不使用 --suite/--components;APT 可指定这两项, 省略时 Ubuntu 版本会推导 suite,并使用 main universe restricted multiverse

--replace 只能替换同名 operator-owned repository,不能覆盖 ISO 导入生成的 content-addressed repository;ISO 内容替换仍使用对应的 import 命令和 --yes。启用 --gpg-check 时必须同时提供 catalog 中已有的 --gpg-key asset。不要把外部 URL 手工写入 catalog;登记后统一执行下面的检查,并在需要重新部署时显式 node retry

nodeforge catalog validate
nodeforge profile software show "$PROFILE" --output json
nodeforge node software show "$NODE" --output json
nodeforge node render "$NODE"
nodeforge node boot preview "$NODE"

# install/diskless 下一次应用配置时再执行;它会按当前 effective plan 重新武装生命周期。
nodeforge node retry "$NODE"

--force 只表示允许终止或替代冲突中的活动状态,不会绕过 schema、引用完整性或安全校验。 Profile、Node、Hosts、网络、存储、软件和内核参数的当前用法以本 README 和 --help-full 为准。

7. Durable operation

ISO import、initrd build、rootfs build 等工作由 daemon 持有。默认 CLI 会跟随;长耗时任务可加 --detach,保存返回的 operation id:

nodeforge assets initrd build rocky-9.7-nodeforge-initrd \
  --from-install-source rocky-9.7-aarch64-dvd \
  --kernel-release 5.14.0-611.5.1.el9_7.aarch64 \
  --detach --output json

OPERATION_ID='替换为返回的-id'
nodeforge operation show "$OPERATION_ID"
nodeforge operation follow "$OPERATION_ID" --timeout 1800
nodeforge operation wait "$OPERATION_ID" --timeout 1800
nodeforge operation list

只有 succeeded 才表示完成。CLI timeout、SSH 断开或终端关闭不是失败证据;failedinterrupted 时结合 operation detail、events 和 daemon 日志定位。

8. 状态、观测与排障

# 服务整体 readiness。
nodeforge status
nodeforge status --output json

# v0.4.15 RSS/准入与 rootfs 下载槽观测(status JSON 的 result 字段)。
nodeforge status --output json | jq '.result | {service_memory_max_bytes, rss_bytes, memory_pressure, heavy_work_rejected, rootfs_downloads_active, rootfs_downloads_limit, live_counters}'

# 只允许在字段存在且服务健康时继续自动化;rss_bytes 在平台无法采样时可能为 null。
nodeforge status --output json | jq -e '.ok and .result.service_memory_max_bytes != null and .result.memory_pressure != null'

# Desired / Effective / Runtime / drift。
nodeforge node show node-01
nodeforge profile show rocky-9.7-aarch64-dvd-install

# PXE 决策与 session 时间线。
nodeforge node boot preview node-01
nodeforge node trace node-01 --latest
nodeforge node session list

# 事件与协议运行态。
nodeforge events list --node node-01 --limit 100
nodeforge events follow --node node-01
nodeforge runtime dhcp-leases
nodeforge runtime dhcp-unknown
nodeforge runtime tftp-sessions
nodeforge runtime tftp-counters

# systemd 与文件日志。
journalctl -u nodeforged.service -f
tail -f /opt/nodeforge/logs/nodeforged.log

推荐顺序:statusnode showboot previewtraceeventsoperation show(若涉及构建)→ daemon 日志。boot preview 不创建 session 或 token,可以安全重复执行。

9. Reconfigure、reset 与恢复

setup --reconfigure 会校验并重新发布 config/unit,但不会自动 reload 或 restart 服务:

nodeforge setup --reconfigure --log-level info --non-interactive --yes
systemctl daemon-reload
systemctl restart nodeforged.service
nodeforge status

任何 reset/purge 先运行相同参数的 --dry-run,并确认安装根与 unit。常用范围:

操作 范围
--repair-dirs 校验并修复受管目录和权限
--reset-state 备份并清空 runtime,保留 config/catalog/assets/work/logs
--reset-all 另重新生成启动配置,默认保留 catalog/assets
--reset-all --purge-data 再删除 catalog/assets
--reset-all --purge-all 删除全部受管数据,形成不可恢复的 fresh replacement
# 只预览。
nodeforge setup --reset-all --purge-all --reconfigure \
  --dry-run --non-interactive

# 仅清理运行态。
systemctl stop nodeforged.service
nodeforge setup --reset-state --non-interactive --yes
nodeforged --check-config
systemctl start nodeforged.service
nodeforge status

setup 发布中断时会保留 fail-closed marker;重新执行 setup 触发恢复。不要手工删除 marker、transaction 目录或拼接安装根外路径。

支持边界

维度 当前支持
启动方式 UEFI IPv4 PXE
架构 x86_64、aarch64
安装适配器 Rocky Linux / RHEL 系 Kickstart、Ubuntu 22.04+ Autoinstall
存储 single、LVM、RAID 0/1/5/6/10、RAID 上 LVM
运行方式 Linux 管理节点,systemd 服务

BIOS PXELINUX、DHCP-less static PXE 与 ram_rootfs 仍不在当前实现边界;IPv6 和按 by-id/serial/WWN 选择磁盘也不在项目目标内。

仓库结构

NodeForge/
├── src/                 # daemon、CLI、协议服务和领域实现
├── tests/               # 单元、契约、集成和实机辅助脚本
├── docs/
│   ├── USER_MANUAL.md   # 稳定使用边界;命令以本 README 为准
│   ├── design/          # 里程碑、已实现版本设计与在制版本设计
│   └── PLATFORM_VALIDATION_RUNBOOK.md  # 唯一平台验证流程
├── vendor/zli/          # 编译时使用的 CLI 框架
├── vendor/dhcp/         # 仅作协议参考,不参与编译
├── build.zig            # 构建入口与产品版本事实源
└── config.example.json  # 站点启动配置示例

文档入口

需要了解 入口
稳定使用边界 使用手册
CLI 完整操作流程 CLI 中文指南
v0.1 之前的里程碑 M0–M4.12 实现态
v0.1–v0.3 实现态 v0.1 / v0.2 / v0.3
v0.4 全部实现态设计 v0.4 路线图功能点验证矩阵v0.4 / v0.4.1 / v0.4.2 / v0.4.3 / v0.4.4 / v0.4.5 / v0.4.6 / v0.4.7 / v0.4.8 / v0.4.9 / v0.4.10 / v0.4.11 / v0.4.12 / v0.4.13 / v0.4.14 / v0.4.15

| agent/builder/imager 入口 | nodeforge-agent inspect --output jsonnodeforge-builder rootfs …、管理面 nodeforge imager capture/register/... 与目标机 nodeforge-imager clone/inspect/restore/restore-auto/postprocess;版本、ABI、token 和安全边界见对应 v0.4 设计与 runbook | | 平台验证流程 | 平台验证 runbook | | 文档规则 | docs/README |

节点上以 root 身份使用 nodeforge-agent inspect --output json 可只读查看本机 diskless 部署摘要;agent 生命周期入口使用 pre-initfirst-bootinstall-first-boot 子命令。与 nodeforge/nodeforged 一致,-v/--version-h/--help 是根级参数,-d/--debug 输出详细诊断到 stderr;version/help 不属于子命令。

当前行为发生冲突时,以当前代码和契约测试为首要证据,其次是本 README 与当前二进制的 --help-full,再参考版本设计和平台验证流程。完整平台流程没有全部执行并留存证据前, 不得新增版本验证结论;局部附加验证只能单独标记,不能替代主流程结论。

许可证

MIT

About

一个基于 Zig 实现的轻量级 OS Provisioning 平台,面向小型 Linux 集群,内置 DHCP/TFTP/HTTP 服务。

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages