Nspawn 回归:OCI 枢纽、新镜像与 nspawn 1.0.0
摘要
nspawn 工具以 Rust 重写的 1.0.0 版本重新登场,其新增的 OCI 注册中心枢纽可用于管理 Linux 容器镜像。
<p><a href="https://lobste.rs/s/o74pht/nspawn_comeback_oci_hub_new_images_nspawn">评论</a></p>
查看缓存全文
缓存时间: 2026/09/24 11:00
# nspawn回归:OCI中心、新镜像与nspawn 1.0.0
来源:https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/
## 回顾起点
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#where-we-left-it
如果你曾使用nspawn,可能记得它曾是一个轻量级Bash脚本。运行 `nspawn -i //tar` 后,脚本会读取 `https://hub.nspawn.org/storage/list.txt`,选取一个压缩包,并使用我们主密钥签名的校验和(已导入到 `/etc/systemd/import-pubring.gpg`)执行 `machinectl pull-tar`。这套方案虽然可用,但最后一个版本(0.6)发布于2022年。期间曾在分支进行过Rust重写但未合并,镜像重建也已停止,到2024年一切陷入沉寂。本博客最后一篇文章也是关于WiFi适配器的2024年1月内容。
上周我重新拾起这个项目。截至今日的成果如下:
- hub.nspawn.org 现已支持 OCI 注册表功能。
- 镜像由 GitHub Actions 上的 mkosi 构建并推送至中心。
- nspawn 1.0.0 正式发布:采用 Rust 重写,支持拉取 OCI 镜像,并通过 D-Bus 与 systemd-machined 和 systemd 交互来管理虚拟机。
- nspawn.org 文档已全面更新。
## 为何不直接沿用旧方案
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#why-not-just-revive-the-old-setup
最初的想法确实是恢复旧流程:重新构建压缩包并继续使用 `machinectl pull-tar`。但几个因素表明这并非良策:
- `machinectl pull-*` 已在 systemd 256 中迁移至 `importctl`,旧脚本无论如何都需要重写。
- 解析 `machinectl` 或 `importctl` 输出十分脆弱(非稳定API),所有实用功能(拉取进度、`OpenMachineShell`、临时单元)均通过总线实现。
- 虽然 `importctl pull-oci` 已存在,但它为 OCI 镜像写入 `Boot=no`,这对以 systemd 为入口点的镜像是错误的。
我曾尝试在 OBS(build.opensuse.org)构建 `tar.xz` 格式镜像,但效果不佳:mkosi配方无法生成 OCI 输出,CentOS Stream 缺乏基础项目,Alma 或 Rocky 的 mkosi 配置不可用,且下载 CDN 在提供最新 `SHA256SUMS` 的同时仍返回旧版压缩包,导致 `importctl` 报错 "DOWNLOAD INVALID"。最终放弃了 OBS。
因此我们决定:采用 OCI 镜像格式、搭建真正的注册表,并让客户端自行完成拉取与组装。
## hub.nspawn.org 现支持 OCI 注册表
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#hubnspawnorg-is-an-oci-registry-now
该中心运行着 [zot](https://zotregistry.dev/)。我也评估过 Distribution、Harbor 和 Forgejo 的包管理器:
- Distribution 仅支持 htpasswd 全有或全无认证,实现匿名拉取需额外搭建令牌服务器。
- Harbor 功能过重,超出需求。
- Forgejo 缺少 `nspawn hub ls` 依赖的 `/v2/_catalog` 接口。
zot 开箱即用支持匿名拉取与认证推送,具有 blob 去重、在线垃圾回收及简易 Web UI,可直接在 [hub.nspawn.org](https://hub.nspawn.org/) 浏览镜像。作为标准注册表,任何 OCI 客户端或 `curl` 均可访问:
```
$ curl -s https://hub.nspawn.org/v2/fedora/tags/list | jq -c .tags
["43","43-20260922","43-20260923","44","44-20260922","44-20260923","latest","rawhide","rawhide-20260922","rawhide-20260923"]
```
移动标签(如 `44`、`latest`)始终指向最新构建,日期标签(如 `44-20260923`)固定不变,每个仓库保留最近10个版本,便于锁定特定构建。
## mkosi 定义文件
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#mkosi-definitions
镜像源自 [nspawn/mkosi-definitions](https://github.com/nspawn/mkosi-definitions) 仓库,该仓库按照 systemd 和 ParticleOS 的 mkosi 目录结构重新组织。根目录 `mkosi.conf` 定义了所有镜像的共享配置:
```
[Build]
ToolsTree=default
ToolsTreeDistribution=fedora
ToolsTreeRelease=44
Incremental=yes
[Output]
Format=oci
CompressOutput=zstd
ImageId=%d
ManifestFormat=json
Output=%i_%v_%a
[Content]
Bootable=no
Initrds=
Hostname=%d-%r
```
`mkosi.conf.d/` 目录包含各发行版的独立配置(所有镜像均配备 systemd-networkd 和 systemd-resolved,以此在 nspawn 桥接网络中获得地址),`mkosi.profiles/` 存放服务与开发镜像。`mkosi.bump` 脚本仅执行 `date -u +%Y%m%d` 作为镜像版本与日期标签。
构建列表在 `images.json` 中定义(即 CI 矩阵):
```
$ jq -c '.[] | select(.repo == "fedora" or .repo == "nginx")' images.json
{"distro":"fedora","release":"44","repo":"fedora","tags":"44 latest"}
{"distro":"fedora","release":"43","repo":"fedora","tags":"43"}
{"distro":"fedora","release":"rawhide","repo":"fedora","tags":"rawhide"}
{"distro":"debian","release":"trixie","profile":"nginx","repo":"nginx","versionof":"nginx"}
```
服务镜像(如 `nginx`)的标签取自 mkosi 清单中的软件包版本(如 `1.26.3`、`1.26`、`latest`)。目前共50个条目:
- 19个发行版镜像:Arch、Debian、Ubuntu、Fedora、CentOS Stream、AlmaLinux、Rocky、openSUSE 和 Kali。
- 23个服务镜像:涵盖 nginx、PostgreSQL 至 Forgejo、Grafana。
- 4个工具箱与5个语言镜像。
拉取请求时,`.github/select-images.py` 会分析差异,仅构建受影响的镜像(例如修改 nginx 配置文件仅构建 nginx 镜像)。合并时通过 `skopeo copy` 推送镜像,每周日全量重建以获取更新。
以下踩坑经验供参考:
- `History=yes` 会导致后续调用复用上次构建的发行版配置,忽略 `-d` 参数(在多发行版单树场景中无用)。
- `Output=` 中的 `%i` 变量在发行版配置设置 `ImageId=` 前就已展开,导致名为 `archlinux` 的镜像输出为 `arch_*`。在 `ImageId=` 之后重新声明 `Output=` 可解决此问题。
- Kali 的密钥环不在 Fedora 工具树中,需放置于 `mkosi.conf.d/kali/mkosi.sandbox/`(包含 apt 源),mkosi 会自动识别。
- `alma.conf` 排序优先级高于 `centos/` 目录导致配置丢失,现改用共享的 `el/` 目录搭配独立 `el-*.conf`。
## nspawn 1.0.0
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#nspawn-100
客户端是全新程序:采用 Rust 编写,不依赖 `machinectl` 或 `importctl`,直接与注册表通信,并通过 D-Bus(zbus)与 systemd-machined 和 systemd 交互。实际操作由系统总线服务 `org.nspawn` 执行,polkit 负责权限控制,命令行客户端与其交互模式类似于 `machinectl` 与 machined 的关系。
演示会话如下(与封面图一致):
```
$ nspawn search arch
SOURCE NAME DESCRIPTION
hub.nspawn.org archlinux tags: latest, rolling, rolling-20260922, rolling-20260923
hub.nspawn.org archlinux-devel tags: latest, rolling, rolling-20260923
Docker Hub docker.io/library/archlinux Arch Linux is a simple, lightweight Linux distribution
ai... ...
$ nspawn pull archlinux:latest
hub.nspawn.org/archlinux:latest: manifest b6703a43db52 with 1 layer(s), assembling as mstack
blob 7b6bfaeb7a4d: downloading
blob a12d80caae9c: downloading
image archlinux (boot image) is ready: nspawn start archlinux
$ nspawn start archlinux
started archlinux
$ nspawn exec archlinux pacman -V
# 返回码与 docker exec 类似
$ nspawn shell archlinux
# 通过 machined 实现登录会话
```
技术细节说明:
- **专属存储**。层、blob 和 manifest 存储于 `/var/lib/nspawn`(非 `/var/lib/machines`),避免 machined 将每层误识别为镜像或被 `machinectl clean` 删除。层由多镜像共享,无引用时自动垃圾回收。
- **三种后端**。`overlay`(启用 `metacopy=on` 的 overlayfs)、`flat`(直接复制)和 `mstack`(通过 nsresourced 和 mountfsd 使用 systemd 261 的托管用户命名空间)。上述示例因宿主机运行 systemd 261 选择了 mstack。
- **机器与应用**。入口点为 init 系统的镜像直接启动,其他镜像(包括 Docker Hub 镜像)在 nspawn 伪 init 下作为 PID 2 运行,遵循 OCI 配置的入口点、环境与用户:
```
$ sudo nspawn pull docker.io/library/nginx:latest --name web
$ sudo nspawn start web -p 8080:80
```
- **网络管理**。nspawn 独立管理 `nspawn0` 桥接网络(默认 10.99.0.0/24),包含 NAT 与端口映射的 nftables 表。宿主机无需 systemd-networkd 或 NetworkManager,可与 firewalld、docker 及 ufw 共存。
- **单元钩子**。每个机器的 systemd 单元均配置 drop-in 脚本,在生命周期各阶段调用 nspawn,使 `machinectl start`、开机自启单元或崩溃恢复都能像 `nspawn start` 一样管理网络。
- **构建与推送**。`nspawn build -t team/app:1 ./app` 对目录执行 `mkosi --format=oci` 并导入结果,`nspawn push` 会跳过注册表已有层进行上传。
需要 systemd 255 或更高版本(已测试 255、259 和 261)。Fedora 包含 SELinux 策略子包 `nspawn-selinux`,另有 Debian/Ubuntu 及 Arch(AUR 提供 `nspawn` 和 `nspawn-git`)适配,全部附属于 [发布页面](https://github.com/nspawn/nspawn/releases/tag/1.0.0)。
## nspawn.org
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#nspawnorg
网站基于 Hugo 和 Docsy 重建,包含 1.0.0 文档:入门指南、镜像管理、虚拟机操作、网络配置、自建镜像及命令参考。采用自托管方式(与 hub 共同部署),因隐私页面承诺不记录 IP、用户代理和时间戳——这只有在自主运营的服务器上才能保证。
## 待完善事项
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#what-is-missing
1.0.0 已可用,但部分功能尚未实现:
- 尚未验证 manifest 签名(仅校验 blob 的 sha256 摘要,且注册表仅支持 HTTPS 访问)。计划在 CI 中集成 cosign 进行镜像签名。
- 仅支持 x86-64 架构镜像。
- 桥接网络不支持 IPv6。
- 1.1.0 计划实现:读取 OCI 配置标签、重启策略、资源限制、`--json` 输出、`cp` 命令及免删镜像的虚拟机移除功能。
如遇问题或需要 hub 中未提供的镜像,请在以下仓库提交 issue 或 pull request。添加镜像通常只需修改 `images.json` 条目,服务镜像还需配套 profile 文件。
特别感谢 Christian Rebischke 开创此项目,以及 Septatrix 在 2025 年我们不便期间重构了 mkosi-definitions。
## 参考资料
https://blog.nspawn.org/posts/nspawn-comeback-an-oci-hub-new-images-and-nspawn-1-0-0/#references
- https://nspawn.org
- https://hub.nspawn.org
- https://github.com/nspawn/nspawn
- https://github.com/nspawn/nspawn/releases/tag/1.0.0
- https://github.com/nspawn/mkosi-definitions
- https://github.com/systemd/mkosi
相似文章
Show HN: Nub – 类似 Bun 的用于 Node.js 的一体化工具包
Nub 是一个快速的一体化工具包,用于 Node.js,提供类似 Bun 的开发者体验,包括运行 TypeScript 文件、管理依赖项和 Node 版本,所有这些都集中在一个用 Rust 编写的 CLI 工具中。
用地道 Rust 重写的 Linux 0.11,可在 QEMU 中启动
一个用现代 Rust 重写的 Linux 0.11 内核,可在 i386 上的 QEMU 中启动,包含完整的用户空间(shell 和 coreutils),支持一键构建镜像和 devcontainer 环境。
从 Proxmox 迁移到 NixOS 和 Incus
作者描述了将其家庭实验室从 Proxmox 迁移到使用 Incus 的 NixOS 的过程,强调了声明式配置和可重现性相对于命令式系统的优势。
Moxnox 1.0:运行于 Btrfs 之上的类 NT、兼容 POSIX 的操作系统
Moxnox 1.0 是一个类似 NT、兼容 POSIX 的业余操作系统,运行于 btrfs 文件系统之上,由约 130,000 行 C++ 代码编写而成,支持 amd64 和 aarch64 架构,并且能够自举。
Show HN: machine0 – 可通过 CLI 控制的持久化 NixOS 虚拟机
machine0 是一个 CLI 工具,用于配置持久化的 NixOS 和 Ubuntu 虚拟机,提供专用资源、静态 IP、按分钟计费,以及挂起/恢复和黄金镜像等功能。