feat(loadbalance): enhance consistent hashing with useSourceIp support (#2844)

This commit is contained in:
aias00
2025-09-02 20:24:45 +08:00
committed by GitHub
parent 854ec1e289
commit f31e8b0495
3 changed files with 202 additions and 13 deletions

View File

@@ -50,13 +50,13 @@ var (
headersMapping = map[string]string{ headersMapping = map[string]string{
"$request_uri": ":path", "$request_uri": ":path",
"$host": ":authority", "$host": ":authority",
"$remote_addr": "x-envoy-external-address",
} }
) )
type consistentHashByOther struct { type consistentHashByOther struct {
header string header string
queryParam string queryParam string
useSourceIp bool
} }
type consistentHashByCookie struct { type consistentHashByCookie struct {
@@ -110,6 +110,12 @@ func (l loadBalance) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
} else if isOtherAffinity(annotations) { } else if isOtherAffinity(annotations) {
if key, err := annotations.ParseStringASAP(upstreamHashBy); err == nil && if key, err := annotations.ParseStringASAP(upstreamHashBy); err == nil &&
strings.HasPrefix(key, varIndicator) { strings.HasPrefix(key, varIndicator) {
// Special case for $remote_addr: use useSourceIp instead of header mapping
if key == "$remote_addr" {
loadBalanceConfig.other = &consistentHashByOther{
useSourceIp: true,
}
} else {
value, exist := headersMapping[key] value, exist := headersMapping[key]
if exist { if exist {
loadBalanceConfig.other = &consistentHashByOther{ loadBalanceConfig.other = &consistentHashByOther{
@@ -127,6 +133,7 @@ func (l loadBalance) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
} }
} }
} }
}
} else { } else {
if lb, err := annotations.ParseStringASAP(loadBalanceAnnotation); err == nil { if lb, err := annotations.ParseStringASAP(loadBalanceAnnotation); err == nil {
lb = strings.ToUpper(lb) lb = strings.ToUpper(lb)
@@ -165,7 +172,13 @@ func (l loadBalance) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy,
} }
} else if loadBalanceConfig.other != nil { } else if loadBalanceConfig.other != nil {
var consistentHash *networking.LoadBalancerSettings_ConsistentHashLB var consistentHash *networking.LoadBalancerSettings_ConsistentHashLB
if loadBalanceConfig.other.header != "" { if loadBalanceConfig.other.useSourceIp {
consistentHash = &networking.LoadBalancerSettings_ConsistentHashLB{
HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_UseSourceIp{
UseSourceIp: true,
},
}
} else if loadBalanceConfig.other.header != "" {
consistentHash = &networking.LoadBalancerSettings_ConsistentHashLB{ consistentHash = &networking.LoadBalancerSettings_ConsistentHashLB{
HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpHeaderName{ HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpHeaderName{
HttpHeaderName: loadBalanceConfig.other.header, HttpHeaderName: loadBalanceConfig.other.header,

View File

@@ -109,7 +109,7 @@ func TestLoadBalanceParse(t *testing.T) {
expect: &LoadBalanceConfig{ expect: &LoadBalanceConfig{
simple: networking.LoadBalancerSettings_ROUND_ROBIN, simple: networking.LoadBalancerSettings_ROUND_ROBIN,
other: &consistentHashByOther{ other: &consistentHashByOther{
header: "x-envoy-external-address", useSourceIp: true,
}, },
}, },
}, },
@@ -233,6 +233,27 @@ func TestLoadBalanceApplyTrafficPolicy(t *testing.T) {
}, },
}, },
}, },
{
config: &Ingress{
LoadBalance: &LoadBalanceConfig{
other: &consistentHashByOther{
useSourceIp: true,
},
},
},
input: &networking.TrafficPolicy_PortTrafficPolicy{},
expect: &networking.TrafficPolicy_PortTrafficPolicy{
LoadBalancer: &networking.LoadBalancerSettings{
LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{
ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{
HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_UseSourceIp{
UseSourceIp: true,
},
},
},
},
},
},
} }
for _, inputCase := range inputCases { for _, inputCase := range inputCases {

View File

@@ -0,0 +1,155 @@
# Example: Load Balancing with useSourceIp Support
# This example demonstrates the enhanced consistent hashing feature
# that uses source IP for load balancing instead of headers.
#
# Issue: https://github.com/alibaba/higress/issues/2790
# PR: https://github.com/alibaba/higress/pull/2844
#
# The key improvement is that $remote_addr now uses useSourceIp field
# instead of x-envoy-external-address header, which works better for
# private IP addresses.
---
apiVersion: v1
kind: Namespace
metadata:
name: test-remote-addr
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-server-1
namespace: test-remote-addr
spec:
replicas: 1
selector:
matchLabels:
app: echo-server-1
template:
metadata:
labels:
app: echo-server-1
spec:
containers:
- name: echo-server
image: hashicorp/http-echo:0.2.3
args:
- "-text=Server 1 - IP: $(hostname -i)"
ports:
- containerPort: 5678
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-server-2
namespace: test-remote-addr
spec:
replicas: 1
selector:
matchLabels:
app: echo-server-2
template:
metadata:
labels:
app: echo-server-2
spec:
containers:
- name: echo-server
image: hashicorp/http-echo:0.2.3
args:
- "-text=Server 2 - IP: $(hostname -i)"
ports:
- containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
name: echo-service
namespace: test-remote-addr
spec:
selector:
app: echo-server-1
ports:
- port: 80
targetPort: 5678
name: http
---
apiVersion: v1
kind: Service
metadata:
name: echo-service-2
namespace: test-remote-addr
spec:
selector:
app: echo-server-2
ports:
- port: 80
targetPort: 5678
name: http
---
# Example 1: Using $remote_addr with useSourceIp (NEW FEATURE)
# This configuration now uses source IP directly for consistent hashing
# instead of relying on x-envoy-external-address header
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-remote-addr-source-ip
namespace: test-remote-addr
annotations:
higress.io/upstream-hash-by: "$remote_addr"
spec:
ingressClassName: higress
rules:
- host: test-source-ip.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: echo-service
port:
number: 80
---
# Example 2: Using traditional header-based hashing for comparison
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-remote-addr-header
namespace: test-remote-addr
annotations:
higress.io/upstream-hash-by: "$http_x_real_ip"
spec:
ingressClassName: higress
rules:
- host: test-header.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: echo-service-2
port:
number: 80
---
# Test client for sending requests
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-client
namespace: test-remote-addr
spec:
replicas: 1
selector:
matchLabels:
app: test-client
template:
metadata:
labels:
app: test-client
spec:
containers:
- name: curl
image: busybox:1.28
command: ["sleep", "3600"]