mirror of
https://github.com/alibaba/higress.git
synced 2026-06-26 02:35:02 +08:00
feat(loadbalance): enhance consistent hashing with useSourceIp support (#2844)
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
155
samples/loadbalance/useSourceIp-example.yaml
Normal file
155
samples/loadbalance/useSourceIp-example.yaml
Normal 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"]
|
||||||
Reference in New Issue
Block a user