Kubernetes

Clusters, workloads, storage and the ways they fail.

Series

Three etcd members, one NVMe

A consumer NVMe under every etcd member and storage replica, and what fixing it bought.

  1. Three etcd members, one consumer NVMe
  2. After the NVMe: what the fixes bought, and what the rolling restart cost

Posts

  • Progress on the homelab cluster over the last ten days: InfluxDB and the document archive moved in, the old Compose host runs nothing, the node disks grew from 20 to 50 GiB the hard way, and a few things moved out again.

  • The kubelet evicted pods because the node disks were tiny, and the cure was obviously bigger disks. Getting there took a trim of the thin pool, three reboots, and a night in which Home Assistant was down on and off for ninety minutes and etcd timed out because of one shared NVMe.

  • Applying a new machine config to a three-node cluster rebooted each node in turn. One came back on a different IP, because its DHCP reservation had been wrong for months and the node had never rebooted to notice.

  • A Prometheus volume looked almost full while retentionSize was set. The real story: a single corrupt WAL segment had stopped every checkpoint for weeks, the WAL grew to 48.7 GiB, and the size cap spent the difference by deleting old blocks.

  • A follow-up on the cluster whose etcd shared one consumer drive with every storage replica. Flush latency fell from 23 ms to 3 ms, a filtered copy shrank a 9.9 GB SQLite database to 3.8 GB in ten minutes, and turning on discard cost more in replica rebuilds than in reboots.

  • A scale-down that never happened led me to leader elections lost on every node, and from there to a single drive holding every etcd member and every storage replica. Three replicas on one disk mostly just triple the writes.

  • Kubernetes has no notion of an active/passive Service. MetalLB in L2 mode with externalTrafficPolicy: Local is one, and I had been running it with a single replica the whole time.

  • The backup command I was asked to schedule did nothing on the current version: it printed usage text, exited 0 and wrote no file. So the job now checks the file it produces and refuses to upload anything that is not a real archive.

Notes