lb: LoadBalancer Services from addresses nodes hold, client address kept
images / images (push) Successful in 14m46s

A Service of type LoadBalancer with loadBalancerClass
reweave.levk.dev/node-address names its address in spec.loadBalancerIP.
Each node's weave-npc publishes the IPv4 addresses on its host interfaces
and its weave bridge (Node annotations reweave.levk.dev/addresses and
reweave.levk.dev/weave). The one node that holds a Service's address is
its holder: it alone writes status.loadBalancer (ipMode Proxy, so
kube-proxy installs nothing for the address) and DNATs the address's ports
to ready endpoints (EndpointSlices), without SNAT, from nat PREROUTING
ahead of kube-proxy. Every other node marks the connections the holder
forwards to its pods (new, from the holder's bridge MAC, from outside the
pod network) and routes their replies back to the holder's bridge through
a per-holder policy rule and table, so the holder's conntrack undoes the
DNAT and the reply leaves from the address the client connected to.

Fail closed: an address no node holds, or that several nodes hold, is
served by no node, and its status is cleared (by its holder, or by the
first node by name if none holds it). Compute is the policy as a pure
function; Datapath replaces exactly the chains it owns with
iptables-restore --noflush and keeps its rules and tables exact.

weave-npc runs it unless --lb=false; the ClusterRole gains services
(watch), services/status (patch), endpointslices (watch), nodes (patch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHXzGrsxfkGk8LmwrBbHNZ
This commit is contained in:
levaliciousandClaude Opus 5.5 committed 2026-10-13 12:45:54 -04:00
1 parent 4cf88d674c
commit 9bc3415111
7 files changed
+1149

No files matched your search

@@ -48,6 +48,36 @@ items:
verbs:
- patch
- update
# weave-npc's LoadBalancer agent (lb): Services of class reweave.levk.dev/node-address
- apiGroups:
- ''
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- ''
resources:
- services/status
verbs:
- patch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- get
- list
- watch
# each node publishes the addresses it holds and its weave bridge (annotations)
- apiGroups:
- ''
resources:
- nodes
verbs:
- patch
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
+8
View File
@@ -21,6 +21,7 @@ import (
"github.com/rajch/weave/common"
"github.com/rajch/weave/common/chains"
"github.com/rajch/weave/lb"
"github.com/rajch/weave/net"
"github.com/rajch/weave/net/ipset"
"github.com/rajch/weave/npc"
@@ -36,6 +37,7 @@ var (
nodeName string
maxList int
bridgePortName string
serveLB bool
)
func handleError(err error) {
@@ -337,6 +339,11 @@ func root(cmd *cobra.Command, args []string) {
go podController.Run(wait.NeverStop)
go npController.Run(wait.NeverStop)
if serveLB {
// LoadBalancer Services of class lb.Class: addresses this node holds, client address kept
go lb.NewController(nodeName, client, lb.NewDatapath(ipt)).Run(context.Background())
}
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
common.Log.Fatalf("Exiting: %v", <-signals)
@@ -354,6 +361,7 @@ func main() {
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
rootCmd.PersistentFlags().IntVar(&maxList, "max-list-size", 1024, "maximum size of ipset list (for namespaces)")
rootCmd.PersistentFlags().StringVar(&bridgePortName, "bridge-port-name", "vethwe-bridge", "name of the brige port on which packets are received and sent")
rootCmd.PersistentFlags().BoolVar(&serveLB, "lb", true, "serve LoadBalancer Services of class "+lb.Class+" from addresses nodes hold")
handleFatal(rootCmd.Execute())
}