labsim etcd harness: the real fix -- protect-kernel-defaults sysctls
Some checks failed
CI/CD / lint (push) Failing after 8s
CI/CD / test (push) Failing after 9s
CI/CD / typecheck (push) Failing after 25s
CI/CD / build (push) Has been skipped
CI/CD / publish-rpm (push) Has been skipped
CI/CD / publish-deb (push) Has been skipped

The crash-loop was NOT etcd starvation (my earlier diagnosis was wrong). The
generated config sets `protect-kernel-defaults: true`, which makes the kubelet
refuse to start unless vm.overcommit_memory=1, kernel.panic=10 and
kernel.panic_on_oops=1 are set:

  Failed to start ContainerManager err="invalid kernel flag: vm/overcommit_memory
  expected 1 actual 0, kernel/panic expected 10 actual 0, ..."

k3s then exited 1 and restart-looped, which downstream looked exactly like etcd
re-initialising and the apiserver flapping -- so it read as a CPU/etcd problem
when it was a missing-sysctl problem. Production sets these via install.ks.ts +
sysctl.ts (applyCisHardening); the sim's sysctl.d was missing them. Added the
byte-for-byte CIS set.

Result: fresh build, all three servers k3s=active with 0 restarts, 3-node
embedded-etcd cluster formed and stable, apiserver responsive. NotReady is
expected (no CNI yet). The etcd timer tuning stays as cheap nested-virt
insurance but was not the fix; its comment is corrected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMVzWZgiKW2wquf5z8S1yH
This commit is contained in:
Michal
2026-09-08 11:07:51 +01:00
parent 2048515578
commit 97ae6dea89

View File

@@ -146,12 +146,26 @@ $audit_ind
content: | content: |
br_netfilter br_netfilter
overlay overlay
- path: /etc/sysctl.d/99-k8s.conf # The CIS sysctls the generated config's `protect-kernel-defaults: true`
# REQUIRES -- byte-for-byte from labctl's sysctl.ts (applyCisHardening), plus
# v6 forwarding. Without vm.overcommit_memory=1 / kernel.panic=10 /
# kernel.panic_on_oops=1 the kubelet REFUSES to start ("invalid kernel flag"),
# k3s exits 1 and crash-loops -- which presents downstream as etcd
# re-initialising and the apiserver flapping, i.e. it looks like an etcd/CPU
# problem when it is not. In production these come from install.ks.ts + this
# operation; the sim must set them too.
- path: /etc/sysctl.d/90-k3s-cis.conf
content: | content: |
net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1
net.ipv6.conf.all.forwarding = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
vm.panic_on_oom = 0
vm.overcommit_memory = 1
kernel.panic = 10
kernel.panic_on_oops = 1
fs.inotify.max_user_instances = 524288
fs.inotify.max_user_watches = 524288
runcmd: runcmd:
- [ modprobe, br_netfilter ] - [ modprobe, br_netfilter ]
@@ -171,15 +185,13 @@ runcmd:
# exactly as production. The token is passed via env for the join; on node 1 # exactly as production. The token is passed via env for the join; on node 1
# it seeds the cluster token. # it seeds the cluster token.
# #
# The etcd-arg tuning is LAB-ONLY and deliberately NOT in the production # The etcd-arg tuning is LAB-ONLY (not in production's config): relaxed
# config.yaml/k3s-config.ts: three embedded-etcd servers on a busy labsim # heartbeat/election timers so etcd tolerates nested-virt scheduling jitter.
# host starve etcd of CPU, and stock etcd timers (100ms heartbeat / 1s # NB: this was NOT what fixed the first run's failure -- that was the missing
# election) then trigger a constant leader-re-election storm that never # protect-kernel-defaults sysctls above, which crash-looped the kubelet and
# settles ("storage is (re)initializing", k3s stuck activating). Relaxing # only LOOKED like etcd instability. The tuning is kept as cheap defensive
# the timers lets etcd tolerate the lab's scheduling latency. It changes # insurance for a busy host; it changes nothing the conversion test
# NOTHING the dual-stack conversion exercises (config.yaml CIDR handling, # exercises.
# ServiceCIDR pickup, quorum across a restart) -- it just keeps etcd alive
# under nested-virt jitter. See dualstack-evidence/etcd-harness-2026-09-07.md.
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --etcd-arg=heartbeat-interval=500 --etcd-arg=election-timeout=5000" K3S_TOKEN="$TOKEN" sh - curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --etcd-arg=heartbeat-interval=500 --etcd-arg=election-timeout=5000" K3S_TOKEN="$TOKEN" sh -
EOF EOF