ADR 3 — Software RAID scope
Status: accepted
Context
Issue #5 asked for a way to
declare a software RAID array from the corium: block, with the explicit
caveat that it should only be added if it earns its place over a plain
cloud-init disk setup.
There are two separate problems behind the one request.
Spare disks. Extra disks on a node, to be mirrored or striped and mounted somewhere. This can be done at first boot.
The root filesystem. By the time anything reads the corium: block, the
root filesystem is deployed, mounted, and running the process doing the
reading. Nothing at that point can move it onto an array. A redundant root has
to be arranged by whatever laid the disk down, which means the installer.
Decision
raid[] covers spare disks. Arrays are created by corium-agent at first boot,
before k0s is installed.
A RAID root filesystem is documented as an install-time Kickstart procedure, verified on hardware, and is not modelled in the schema.
Why spare-disk RAID earns its place
cloud-init has no RAID support. disk_setup does partitions and filesystems,
and the documentation has said for years that mdadm support is anticipated. The
honest alternative is therefore a runcmd block calling mdadm, and three
things make that worse than a field:
- Ordering.
runcmdraces the kubelet. Losing that race means containerd writes its state into the directory on the root disk that the array is then mounted over. The data is invisible afterwards while still consuming the root disk, and the node reports a full disk with no apparent cause. Corium builds arrays before k0s is installed and writesRequiresMountsForinto the k0s unit, so Kubernetes refuses to start rather than run without its storage. - Refusing to destroy data. A device already carrying a filesystem, a
partition table, or another array’s metadata stops the bootstrap.
mdadm --createby hand does not ask. - Idempotency. A second bootstrap adopts an existing array rather than rebuilding it, and rewrites its own fstab line rather than appending another.
Why a RAID root is not a schema field
Not because it does not work. It does: a bootc install onto RAID1 / and
RAID1 /boot was verified on a two-disk machine, including detaching the first
disk and booting from the second, with the arrays degraded and serving.
It is not a field because a field would be a lie about when the decision is
taken. The corium: block is read by corium-agent at first boot, by which
point the root filesystem is deployed, mounted, and running the process reading
the block. There is nothing a field could do at that moment.
Three things additionally have to be done by hand at install time, none of which
Anaconda does for you, and all of which are documented in docs/raid.md:
- Build a second ESP on the mirror disk and register it with the firmware. Anaconda creates one ESP and warns that a drive failure will make the system unbootable; it is right.
- Do that in
%postusing disk-level tools only —%postwrites into the deployment’s/etcand/vardo not survive a bootc install. - Add
nofailto the/boot/efiline in/etc/fstab. Without it the mount isRequiredBy=local-fs.target, so losing the first disk drops the machine into emergency mode with perfectly healthy mirrored filesystems.
Fedora CoreOS collapses all three into Ignition’s boot_device.mirror, which
builds the array in the initramfs and replicates /boot per disk. bootc has no
equivalent. Until it does, this belongs in a procedure, not a field.
Consequences
A node whose root disk dies is reprovisioned rather than repaired. That is the model the rest of Corium is built around: the OS is an image, the node is cattle, and a replacement boots the same digest.
Spare-disk arrays cover the case that is actually expensive — local state that is slow to rebuild — while the cluster covers the case of losing a whole node.
If bootc grows a declarative equivalent of boot_device.mirror, this decision
should be revisited; the schema has room for it.
Alternatives considered
Documentation only, no field. Ship the mdadm recipe and add no permanent key. Rejected: it leaves the ordering problem unsolved, and the ordering problem is the one that silently corrupts a node.
Support a RAID root via Kickstart as a product feature. Rejected for now,
but on narrower grounds than “it does not work”. It does work, and the procedure
is documented. What it is not is declarative: it needs a hand-written
kickstart, a %post block, and a post-install fstab edit, and it applies only
to the ISO path. Wrapping that in a Corium field would promise a uniformity the
install paths do not have.
bootc-generic-growpart.service also fails permanently on an md root — it reads
/sys/class/block/<name>/partition, which an md device lacks. Harmless, but it
leaves every such node reporting degraded, which is a poor thing to ship as a
supported configuration.
If bootc grows a declarative equivalent of boot_device.mirror, revisit both
this and the decision above.