mirror of
https://github.com/hanzoai/ingress.git
synced 2026-10-20 15:07:40 +00:00
ingress: expose the load-balancer strategy on Ingress annotations
The strategy was already honoured by dynamic config and by every other provider; only the Ingress provider had no way to ask for it, so a plain Ingress could never select anything but wrr. That gap is why api.lux.network cannot pin a client to one backend. Sticky is cookie-based, and a cross-origin JSON-RPC client — a wallet, a dapp on another domain, curl — never sends the cookie, so cookie stickiness cannot pin the clients that most need pinning. hrw hashes the client address instead: one client stays on one server, and distinct clients spread over every server. ingress.kubernetes.io/service.strategy: hrw Absent stays empty rather than defaulting here — the empty value is what service.go maps to wrr, so stamping a value at this layer would silently change every existing Ingress. NOTE hrw.New hardcodes ip.RemoteAddrStrategy, so behind a SNATing cloud LB RemoteAddr is the LB, not the client, and hrw would hash one value for everyone. PROXY protocol is therefore a prerequisite, not an optimisation.
This commit is contained in:
@@ -50,6 +50,17 @@ type ServiceIng struct {
|
||||
Sticky *dynamic.Sticky `json:"sticky,omitempty" label:"allowEmpty"`
|
||||
NativeLB *bool `json:"nativeLB,omitempty"`
|
||||
NodePortLB bool `json:"nodePortLB,omitempty"`
|
||||
// Strategy selects the load-balancing algorithm across the service's
|
||||
// servers: wrr (default), p2c, hrw or leasttime.
|
||||
//
|
||||
// hrw is the reason this is exposed. Sticky is cookie-based, and a
|
||||
// cross-origin JSON-RPC client — a wallet, a dapp on another domain, curl —
|
||||
// does not send cookies, so cookie stickiness cannot pin the clients that
|
||||
// most need pinning. hrw hashes the client address instead, which both pins
|
||||
// one client to one server AND spreads distinct clients over every server.
|
||||
// The strategy was already honoured by the dynamic configuration and by
|
||||
// every other provider; only the Ingress provider had no way to ask for it.
|
||||
Strategy dynamic.BalancerStrategy `json:"strategy,omitempty"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
|
||||
@@ -283,3 +283,44 @@ func Test_convertAnnotations(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServiceConfig_Strategy(t *testing.T) {
|
||||
// hrw is why this annotation exists: it hashes the client address, so one
|
||||
// client stays on one server while distinct clients spread over all of
|
||||
// them. Sticky cannot do that for cross-origin JSON-RPC clients, which
|
||||
// never send the cookie.
|
||||
testCases := []struct {
|
||||
desc string
|
||||
annotations map[string]string
|
||||
expected dynamic.BalancerStrategy
|
||||
}{
|
||||
{
|
||||
desc: "hrw",
|
||||
annotations: map[string]string{"ingress.kubernetes.io/service.strategy": "hrw"},
|
||||
expected: dynamic.BalancerStrategyHRW,
|
||||
},
|
||||
{
|
||||
desc: "p2c",
|
||||
annotations: map[string]string{"ingress.kubernetes.io/service.strategy": "p2c"},
|
||||
expected: dynamic.BalancerStrategyP2C,
|
||||
},
|
||||
{
|
||||
// Absent must stay empty, NOT default to something here: the empty
|
||||
// value is what service.go maps to wrr, and stamping a value at this
|
||||
// layer would silently change every existing Ingress.
|
||||
desc: "absent leaves the strategy unset",
|
||||
annotations: map[string]string{"ingress.kubernetes.io/service.sticky.cookie": "true"},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
cfg, err := parseServiceConfig(test.annotations)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
require.NotNil(t, cfg.Service)
|
||||
assert.Equal(t, test.expected, cfg.Service.Strategy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,6 +616,7 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In
|
||||
|
||||
if svcConfig != nil && svcConfig.Service != nil {
|
||||
svc.LoadBalancer.Sticky = svcConfig.Service.Sticky
|
||||
svc.LoadBalancer.Strategy = svcConfig.Service.Strategy
|
||||
svc.Middlewares = svcConfig.Service.Middlewares
|
||||
|
||||
if svcConfig.Service.PassHostHeader != nil {
|
||||
|
||||
Reference in New Issue
Block a user