Repository navigation
v0.5.0
[0.5.0] - 2026-10-15
Upgrade notes / operator action required
-
beep must be the cluster's only L4 load balancer. Disable k3s
servicelb
and do not run MetalLB, kube-vip or a cloud LB controller alongside; two
controllers would writestatus.loadBalancer.ingresson the same Services.
kube-proxy must run in iptables or nftables mode. Seedeploy/README.md
("Required per-cluster configuration & gotchas"). -
The loader refuses to start on kernels older than 6.6 (TCX attach).
Upgrade nodes to Linux 6.6 or newer before rolling out 0.5.0. -
New
beep-configConfigMap (deploy/configmap.yaml) holds the sizing
caps. Apply it beforedeploy/daemonset.yaml, which now loads it with
envFrom(a missing ConfigMap fails the pod).BEEP_MAX_FRONTS(1024) and
BEEP_ENDPOINTS_PER_FRONT(4) take effect onkubectl rollout restart ds/servicelb-controller; the front maps are recreated with their entries
carried over and established flows survive. Every option, its default and
memory cost is indeploy/README.md("Configuration"). -
Controllers now keep a per-node Lease and prune dead nodes from Service
ingress. Apply the updateddeploy/rbac.yaml(newbeep-leases
Namespace holding the Role forcoordination.k8s.ioleases, kept out of
kube-systemso beep cannot touch control-plane leader election; with a
CSR kubeconfig also bind it inbeep-leases, seedeploy/README.md) anddeploy/daemonset.yaml(NODE_NAME
env). The controller requires--node-name(envNODE_NAME). A node whose
beep pod stops renewing (40 s duration, 10 s renew) is removed from every
Service'sstatus.loadBalancer.ingresswithin about a minute. -
Pinned maps are recreated on upgrade, flushing conntrack. The loader
deletes and recreates any pinned map whose type, key size, value size or
max_entriesdiffer from the new definition (one log line per map). This
release changesFLOW_TABLEvalues from 36 to 40 bytes andFRONT_META
from 8 to 12 bytes. Established flows are reset once. -
FRONT_METAandFRONT_ENDPOINTSare recreated on upgrade. The loader
now also compares a pinned map'sBPF_F_NO_PREALLOCflag, and both front
maps change from preallocated to no-prealloc (andFRONT_METAfrom 4096 to
1024 default entries). A pin whose type, key size and value size are
unchanged (flag ormax_entrieschange only, e.g. this upgrade or a changed
--max-fronts/--endpoints-per-front) has its entries copied into the new
map before any program is attached, so established flows see no gap; if a
cap shrink does not fit, whole fronts are kept in key order, the loss is
logged with counts, and the controller reconciles the rest. A layout change
(key/value size) recreates the map empty, and established flows through
fronts reset until the controller's first reconcile. Entries the previous
loader's programs write after the snapshot (new flows) are not carried over. -
Breaking flag change.
--front-meta-max-entriesand
--front-endpoints-max-entriesare removed frombeepand
beep-controller. Use--max-fronts(default 1024, envBEEP_MAX_FRONTS)
and--endpoints-per-front(default 4, envBEEP_ENDPOINTS_PER_FRONT):
FRONT_METAholdsmax-frontsrows andFRONT_ENDPOINTS
max-fronts x endpoints-per-front x 2(the 2 is swap headroom).
Fixed
- A Service that leaves
type: LoadBalancer(e.g. patched toClusterIP) now
has beep's entries removed fromstatus.loadBalancer.ingressand its fronts
removed from the dataplane; previously the stale ingress stayed. - The controller's
MapRestoreFailedWarning Event (a failed map restore at
upgrade) is verified live on k3s, and posting has an overall deadline so a
slow apiserver cannot stall the controller.BEEP_FAULT_FAIL_RESTORE_WRITE=1
makes the first restore write fail withENOMEMto exercise it; test hook,
never set it in production. - New connections were refused (dropped at the backend node) once 16 closed
connections had left stale remap state behind: two Services sharing a pod
and a client reusing one source port across both remap the second flow's
backend-facing source port, and the reverse/memo rows of finished flows were
only ever freed by LRU eviction. A TCP flow's rows are now marked closed on
RST, or once a FIN was seen in both directions (a half-closed connection
stays live). A closed row keeps forwarding, so a retransmitted FIN or final
ACK still finds it, and a new connection may reuse its remap slot; the short
(UDP-interval) idle sweep then reclaims it with its port-remap memo, and the
sweeps reclaim the rest.
Added
-
Idle
FLOW_TABLEGC, swept by the controller: flow rows carry a parity bit
that the dataplane copies from a global parity (CONFIG) when a packet
matches; every sweep interval the controller deletes the rows whose bit
differs (with their port-remap memos), then flips the parity. Idle UDP flows
go after 1-2--udp-idle-sweep-secs(envBEEP_UDP_IDLE_SWEEP_SECS, default
120); TCP flows that vanish without a close go after 1-2
--tcp-idle-sweep-secs(envBEEP_TCP_IDLE_SWEEP_SECS, default 43200 = 12 h).
Both are in thebeep-configConfigMap, as isBEEP_FLOW_TABLE_MAX_ENTRIES
(--flow-table-max-entries, default 16384); seedeploy/README.md
("Configuration"). The sweep runs on wall-clock interval boundaries and the
parity survives a controller restart. -
Front-map write failures are reported by cause:
E2BIG(capacity),ENOMEM
(kernel out of memory allocating a map element, transient) and other, each
with its errno text. A front update that would not fit under the pinned
map'smax_entriesis refused whole before any row is written, with a
capacity error naming the map; fronts are applied shrinking first, then in
key order, so the front that starves under pressure is deterministic. The
front is left on its previous endpoints in every case. -
Node-local endpoint preference:
FRONT_METAcarries alocal_count, and
selection prefers local endpoints when it is non-zero. Inert until the
controller setslocal_count. -
The controller emits every ready, non-terminating, same-family endpoint of a
Service (previously one), with its target port and anIS_LOCALflag, and
honours Service policy:externalTrafficPolicy: Clusterwith nospec.trafficDistribution
spreads uniformly over all ready endpoints.spec.trafficDistribution: PreferSameNodeprefers local endpoints and
falls back to all;PreferCloseandPreferSameZoneare treated the same
(beep has no zone model); an unknown value is logged and spreads uniformly.externalTrafficPolicy: Localuses only endpoints on the ingress node
(ignoringtrafficDistribution); with no ready local pod it uses
serving-but-terminating local pods, and with none at all the front is
owned at count 0.- Each node's controller writes only its own address to
status.loadBalancer.ingress; underexternalTrafficPolicy: Localit
removes it while the node has no usable local pod. Switching policy does
not evict pinned flows; they drain naturally.
Changed
- Owned fronts with no ready endpoints (count 0: eTP=Local with no usable
local pod, 0-backend Services) now silently drop new flows instead of
answering with a TCP RST / ICMP(v6) port unreachable, matching kube-proxy's
hardened edge posture. Established and pending pinned flows still forward.
A front with noFRONT_METArow still passes to the host. The reject path
(beep_common::reject) and itsREJECT_BUCKETrate limiter are removed;
the loader deletes a leftoverREJECT_BUCKETpin on start. - The forward Geneve option is now 24 bytes and carries the target port.
Decap reads the port from the option and no longer consults the front maps. FRONT_METAandFRONT_ENDPOINTSareBPF_F_NO_PREALLOC: memory is a
bucket array (~16 B per slot ofmax_entries) plus ~96 B per front and
~128 B per endpoint row in use, instead of the full cap. At the 11-front
smoke scale the two maps drop from 1.54 MB to ~0.15 MB.- The previous endpoint generation is deleted about 2 s after a swap commits
(it was kept until the next swap). Only a packet that loaded the old
FRONT_METAmicroseconds before the commit can still want it; established
flows read their conntrack pin. Steady state holds one generation per front. - The memory-smoke tripwire is 1.15x of the expected usage (bucket floors +
entries x per-entry + the preallocated maps) instead of a flat 6 MiB, which
stays as the gross ceiling. - The controller reads its kubeconfig through the
politeiacrate (git
dependency pinned to v0.1.1); the in-treebeep-kubeconfigcrate is gone.
It parses only an X.509 client-cert kubeconfig file; seedeploy/README.md
("Kubeconfig").
Known limitations
- kube-proxy IPVS mode is unsupported: kube-proxy binds the published ingress
addresses (including node addresses) tokube-ipvs0, which breaks node
connectivity. Use iptables or nftables mode. - Minimum kernel is Linux 6.6; the loader fails fast on older kernels.
- Supported Kubernetes: the latest two minor versions (currently 1.36 and
1.37), rolling forward with upstream. Older versions are unsupported.