Node disks: storage classes, etcd disk & auto-expansion¶
Three node-disk features, all configured on the machine spec (i.e. on a VCOMachineTemplate):
- storage classes, to pin a machine's disks to a named backend class instead of taking the platform default;
- a dedicated physical disk for etcd, so the control plane's write-heavy database isn't fighting everything else for I/O; and
- automatic disk growth, so a node doesn't wedge on a full root disk at 3 a.m.
None of them is on by default. This is separate from CSI persistent volumes — that's for your workloads; this is for the nodes themselves.
Storage classes¶
whitesky.cloud storage classes say where a disk physically lives. CAPVCO can pin a machine's disks to a named class instead of accepting the platform default.
spec:
template:
spec:
storageClass: whitesky-sds # boot disk + every dataDisks entry
etcdDisk:
sizeGiB: 20
storageClass: physical-storage # its own class — see below
List what a location offers:
Typical answer: physical-storage (local, low latency) and whitesky-sds (software-defined), plus
whatever else that location exposes.
Rules worth knowing before you set it¶
physical-storagecannot hold a boot disk. It does not support templates, and a machine is created from an image. CAPVCO refuses the combination up front (FailureReason: UnsupportedStorageClass) and names the classes that can, rather than passing it through and relaying a raw400.- A class the location does not offer is terminal, not a fallback.
UnknownStorageClass, with the available classes listed. Quietly substituting the default would produce a machine that looks provisioned and sits on storage you did not choose — the kind of thing you discover from a latency graph months later. Note the catalog lists only classes that are usable now, so a class that is disabled or still syncing its images reads the same as one that does not exist. - One class per VM. The VM-create API takes a single
storage_classcovering the boot disk and alldataDisks, so per-data-disk classes are not expressible. spec.storageClassdoes not reach the etcd disk. That disk is created by its own API call and keeps its own setting. Letting a machine-wide default move etcd off local storage would undo the one placement decision a dedicated etcd disk exists to make.etcdDisk.typestill works and is now sugar over the class model:physical→physical-storage,data→whitesky-sds.etcdDisk.storageClasswins when both are set.
storageClass and the old disk_type are alternatives, not a pair
The backend resolves a requested class and then forces disk_type to match it, so there is no
"try the class, fall back to the type" path — sending both just means one is discarded. CAPVCO
sends one model or the other per call.
CSI PersistentVolumes do not use this yet
This covers the machine's own disks. Volumes provisioned by the CSI driver still use the
driver's diskType parameter: the driver has no storage_class support, so the shipped
StorageClasses cannot target a backend class. Tracked separately.
Dedicated etcd disk¶
etcd is latency-sensitive: it fsyncs its write-ahead log on every commit. On a busy cluster, sharing
the boot disk's I/O makes etcd slow and flaky. Giving it a dedicated physical disk mounted at
/var/lib/etcd fixes that.
Set etcdDisk on the control-plane VCOMachineTemplate (it's ignored on workers):
The base and geo templates carry this as a commented-out block (VCO_CP_ETCD_DISK_GIB,
default 20) — uncomment it to enable.
type |
Backing | When |
|---|---|---|
physical (default) |
Location-scoped physical disk on low-latency local storage | The point of the feature — use this |
data |
Software-defined (SDS) data disk | Fallback for locations without spare physical capacity |
What the controller does¶
Because a physical disk can't be requested inline at VM-create, the controller sequences it carefully:
- create the physical disk in the machine's location,
- create the VM stopped,
- attach the disk,
- start the VM — so
/var/lib/etcdexists before kubeadm runs.
On teardown it detaches and deletes the disk. Status records etcdDiskId and etcdDiskAttached for
idempotency.
Reboot-safe mounting¶
The disk is formatted once and mounted by filesystem LABEL (etcd-data), not by device name
— because /dev/vdX ordering isn't stable across reboots. A bootcmd (which runs on every boot,
before kubeadm) formats-if-needed, writes an fstab entry with nofail, and mounts. Your etcd data
survives reboots and device reordering.
It just works, but know the shape
You don't configure any of the mount mechanics. If you're debugging a control-plane node,
/var/lib/etcd being its own mount (check mount | grep etcd) confirms the physical disk is in
play.
Automatic disk expansion¶
Turn this on and the controller grows a node's disk before it fills up — and grows the in-node filesystem to match, online, no reboot.
Set diskGrowthPolicy on a VCOMachineTemplate (control-plane or worker):
spec:
template:
spec:
# …sizing…
diskGrowthPolicy:
enabled: true
thresholdPercent: 80 # grow at/above this usage (default 80)
incrementGiB: 10 # grow by this much per step (default 10)
maxBootDiskGiB: 200 # cap; 0 disables boot-disk growth
maxEtcdDiskGiB: 100 # cap; 0 disables etcd-disk growth
How it decides¶
The controller reads each node's real filesystem usage (root fs via the kubelet Summary API; the
dedicated etcd disk via cadvisor metrics, matched by capacity) and the DiskPressure condition.
When usage is at/above thresholdPercent (or DiskPressure fires — always a hard backstop), and the
disk is under its cap, and a cooldown has elapsed since the last growth, it:
- grows the whitesky.cloud vDisk by one
incrementGiBstep, then - an injected
growfssystemd timer grows the partition + filesystem inside the node (online).
It records the observed sizes (status.bootDiskGiB / status.etcdDiskGiB) and
status.lastDiskGrowthTime, and reports a DiskHealthy condition
(BelowThreshold / Grew / AtCap / CoolingDown).
Caps and cooldown are your safety rails
The per-step increment, the hard caps, and the cooldown between steps exist so a runaway
log-spewing pod can't balloon a disk without bound. Set maxBootDiskGiB / maxEtcdDiskGiB to
sane ceilings. A cap of 0 disables growth for that disk.
Best-effort by design
If the controller can't read a node's usage (node down, kubeconfig not ready), it simply skips —
disk growth never blocks machine reconciliation. DiskPressure remains the backstop.
Edge case: same-size disks¶
The etcd disk is matched among the node's filesystems by capacity. If the boot disk and etcd disk are the same size, that match is ambiguous. Give the etcd disk a different size than the boot disk (they usually differ anyway) to keep growth targeting unambiguous.
Next: expose your Services to the world → Load balancing & Services.