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{
"$request_uri": ":path",
"$host": ":authority",
"$remote_addr": "x-envoy-external-address",
}
)
type consistentHashByOther struct {
header string
queryParam string
header string
queryParam string
useSourceIp bool
}
type consistentHashByCookie struct {
@@ -110,19 +110,26 @@ func (l loadBalance) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
} else if isOtherAffinity(annotations) {
if key, err := annotations.ParseStringASAP(upstreamHashBy); err == nil &&
strings.HasPrefix(key, varIndicator) {
value, exist := headersMapping[key]
if exist {
// Special case for $remote_addr: use useSourceIp instead of header mapping
if key == "$remote_addr" {
loadBalanceConfig.other = &consistentHashByOther{
header: value,
useSourceIp: true,
}
} else {
if strings.HasPrefix(key, headerIndicator) {
value, exist := headersMapping[key]
if exist {
loadBalanceConfig.other = &consistentHashByOther{
header: strings.TrimPrefix(key, headerIndicator),
header: value,
}
} else if strings.HasPrefix(key, queryParamIndicator) {
loadBalanceConfig.other = &consistentHashByOther{
queryParam: strings.TrimPrefix(key, queryParamIndicator),
} else {
if strings.HasPrefix(key, headerIndicator) {
loadBalanceConfig.other = &consistentHashByOther{
header: strings.TrimPrefix(key, headerIndicator),
}
} else if strings.HasPrefix(key, queryParamIndicator) {
loadBalanceConfig.other = &consistentHashByOther{
queryParam: strings.TrimPrefix(key, queryParamIndicator),
}
}
}
}
@@ -165,7 +172,13 @@ func (l loadBalance) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy,
}
} else if loadBalanceConfig.other != nil {
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{
HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpHeaderName{
HttpHeaderName: loadBalanceConfig.other.header,

View File

@@ -109,7 +109,7 @@ func TestLoadBalanceParse(t *testing.T) {
expect: &LoadBalanceConfig{
simple: networking.LoadBalancerSettings_ROUND_ROBIN,
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 {

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"]