Files
home-network-k3s-impl/platform/longhorn/helmrelease.yaml
T
jakman85andClaude Opus 4.8 6bc4406dca W5 hardening: Longhorn HR timeout 15m + no downgrade-rollback
Root cause of a W5 wedge: default 5m Helm timeout on slow-HDD Longhorn upgrades caused
a rollback attempt after the data had already migrated to the new minor, which the old
manager fatally refuses. Give the upgrade room and never auto-rollback (fail forward).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-30 01:04:15 -05:00

99 lines
5.3 KiB
YAML

---
# Longhorn — distributed replicated block storage across the 3 nodes (ADR-0003).
# Serves R-STATE-01 (state survives destroy/rebuild) and R-STATE-02 (3-way replication).
#
# Dev-appropriate values. Environment differences enter ONLY via ${...} substitution from
# cluster-vars (R-ENV-02); everything else is identical base config for dev and prod.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: longhorn
namespace: longhorn-system
spec:
interval: 30m
# Longhorn upgrades roll every workload + engine image on slow HDD-backed nodes and can exceed the
# 5m Helm default; a short timeout caused a W5 mid-upgrade wedge (data migrated to the new minor but
# workloads rolled back, and the new-version data refuses the old-version manager). Give it room.
timeout: 15m0s
chart:
spec:
chart: longhorn
version: ">=1.12.0 <1.13.0" # W5 upgrade 1.11->1.12 (resolves 1.12.1; supports k8s 1.36). Renovate maintains
sourceRef:
kind: HelmRepository
name: longhorn
namespace: longhorn-system
install:
createNamespace: false # namespace.yaml owns it (GitOps-managed, labelled)
remediation:
retries: 3
upgrade:
remediation:
retries: 3
# NEVER auto-rollback a Longhorn minor upgrade: once the on-disk data migrates to the new minor,
# the older manager fatally refuses it ("downgrading from vX to vY is not supported"). Rolling
# back the workloads while data is already migrated wedges the cluster. Fail forward instead.
remediateLastFailure: false
values:
# We own the StorageClasses (storageclasses.yaml) so the default 3-replica class and the bulk
# tier are explicit and GitOps-managed. Disable the chart's built-in default SC so there is
# exactly one default-class annotation in the cluster.
persistence:
defaultClass: false
defaultSettings:
# --- data path (R-STATE-02) ---
# Dedicated Longhorn data disk (/dev/vdb → ext4) is mounted here by cloud-init on every node
# (infra/cloud-init/user-data.yaml.tftpl). Longhorn stores replicas on this disk only.
defaultDataPath: "/var/lib/longhorn"
# --- replication (R-STATE-02) ---
# Global fallback replica count. The `longhorn` StorageClass sets numberOfReplicas explicitly
# (authoritative); this keeps any volume created without an explicit count consistent.
# Substituted from cluster-vars.longhorn_default_replica_count (=3 in dev).
defaultReplicaCount: ${LONGHORN_DEFAULT_REPLICA_COUNT}
# --- replica anti-affinity across nodes (R-STATE-02 durability guarantee) ---
# HARD anti-affinity: never place two replicas of the same volume on the same node, so a
# single node loss cannot take down more than one replica. On a 3-node / 3-replica cluster
# this yields exactly one replica per node.
replicaSoftAntiAffinity: false
# Single zone (3 mini-PCs, no failure zones): allow replicas within the one zone.
replicaZoneSoftAntiAffinity: true
# When a downed node returns, rebalance replicas back toward one-per-node (best-effort so it
# never blocks scheduling).
replicaAutoBalance: "best-effort"
# --- rebuild I/O throttle for slow disks (etcd WAL fsync protection) ---
# The k3s VM disks sit on HDDs, so a full-replica rebuild's sequential I/O can saturate the
# spindle and starve etcd's WAL fsync — which crash-loops etcd and flaps the kube-vip VIP.
# Cap Longhorn to rebuilding ONE replica per node at a time so a node bounce / replica
# replenishment can never storm the HDDs. Deliberately conservative for this slow-disk cluster
# (Longhorn's chart default is higher). Pair with replica-replenishment-wait-interval=600
# (Longhorn default) so brief node bounces don't trigger replenishment at all.
concurrentReplicaRebuildPerNodeLimit: 1
# --- capacity guardrails (docs/04 "reserve disk headroom") ---
# Reserve 20% of the dedicated data disk so a node never fills to 100% (which wedges the
# kubelet and Longhorn alike). Dev disks are small, so headroom matters.
storageReservedPercentageForDefaultDisk: "20"
# Stop scheduling new replicas onto a disk once free space drops below 25%.
storageMinimalAvailablePercentage: "25"
# Dev: 200% thin over-provisioning — config-only volumes use little actual space, so the small
# 39Gi data disks host the full app catalog. Physical fill risk is low (watch monitoring PVCs).
# Prod-grade alternative: grow the Longhorn data disks and return to 1:1 (WS-STORAGE).
storageOverProvisioningPercentage: "200"
# --- misc dev-appropriate defaults ---
# Keep replicas spread for durability rather than co-locating with the pod.
defaultDataLocality: "disabled"
# Reclaim replica space of deleted/failed volumes promptly in dev.
orphanAutoDeletion: true
# Longhorn's own manager/UI stay in-cluster; UI exposure (if any) is gated behind
# oauth2-proxy forward-auth (ADR-0014) by WS-SEC — not exposed here.
# TODO(WS-STORAGE, Phase 4): off-cluster backup target (defaultSettings.backupTarget +
# backupTargetCredentialSecret) is configured alongside Velero (ADR-0003, R-STATE-03).
# Credentials referenced BY NAME only, SOPS-encrypted (WS-SEC) — never inline here.