Skip to content

Commit 9863bd1

Browse files
committed
Add ip_hash based session affinity support for NGINX OSS (#4257)
Problem: Users want to be able to specify ip_hash load balancing for upstreams Solution: Add support for session affinity using ip_hash directive in upstreams
1 parent 27486c6 commit 9863bd1

File tree

8 files changed

+164
-33
lines changed

8 files changed

+164
-33
lines changed

internal/controller/nginx/config/http/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,12 @@ const (
119119

120120
// Upstream holds all configuration for an HTTP upstream.
121121
type Upstream struct {
122-
Name string
123-
ZoneSize string // format: 512k, 1m
124-
StateFile string
125-
KeepAlive UpstreamKeepAlive
126-
Servers []UpstreamServer
122+
Name string
123+
ZoneSize string // format: 512k, 1m
124+
StateFile string
125+
LoadBalancingMethod string
126+
KeepAlive UpstreamKeepAlive
127+
Servers []UpstreamServer
127128
}
128129

129130
// UpstreamKeepAlive holds the keepalive configuration for an HTTP upstream.

internal/controller/nginx/config/policies/upstreamsettings/processor.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ type Processor struct{}
1313
type UpstreamSettings struct {
1414
// ZoneSize is the zone size setting.
1515
ZoneSize string
16+
// LoadBalancingMethod is the load balancing method setting.
17+
LoadBalancingMethod string
1618
// KeepAlive contains the keepalive settings.
1719
KeepAlive http.UpstreamKeepAlive
1820
}
@@ -61,6 +63,10 @@ func processPolicies(pols []policies.Policy) UpstreamSettings {
6163
upstreamSettings.KeepAlive.Timeout = string(*usp.Spec.KeepAlive.Timeout)
6264
}
6365
}
66+
67+
if usp.Spec.LoadBalancingMethod != nil {
68+
upstreamSettings.LoadBalancingMethod = string(*usp.Spec.LoadBalancingMethod)
69+
}
6470
}
6571

6672
return upstreamSettings

internal/controller/nginx/config/policies/upstreamsettings/processor_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestProcess(t *testing.T) {
3737
Time: helpers.GetPointer[ngfAPIv1alpha1.Duration]("5s"),
3838
Timeout: helpers.GetPointer[ngfAPIv1alpha1.Duration]("10s"),
3939
}),
40+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
4041
},
4142
},
4243
},
@@ -48,6 +49,24 @@ func TestProcess(t *testing.T) {
4849
Time: "5s",
4950
Timeout: "10s",
5051
},
52+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
53+
},
54+
},
55+
{
56+
name: "load balancing method set",
57+
policies: []policies.Policy{
58+
&ngfAPIv1alpha1.UpstreamSettingsPolicy{
59+
ObjectMeta: metav1.ObjectMeta{
60+
Name: "usp",
61+
Namespace: "test",
62+
},
63+
Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{
64+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeRandomTwoLeastConnection),
65+
},
66+
},
67+
},
68+
expUpstreamSettings: UpstreamSettings{
69+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeRandomTwoLeastConnection),
5170
},
5271
},
5372
{
@@ -220,6 +239,15 @@ func TestProcess(t *testing.T) {
220239
}),
221240
},
222241
},
242+
&ngfAPIv1alpha1.UpstreamSettingsPolicy{
243+
ObjectMeta: metav1.ObjectMeta{
244+
Name: "usp-loadBalancingMethod",
245+
Namespace: "test",
246+
},
247+
Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{
248+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
249+
},
250+
},
223251
},
224252
expUpstreamSettings: UpstreamSettings{
225253
ZoneSize: "2m",
@@ -229,6 +257,7 @@ func TestProcess(t *testing.T) {
229257
Time: "5s",
230258
Timeout: "10s",
231259
},
260+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
232261
},
233262
},
234263
{
@@ -310,6 +339,15 @@ func TestProcess(t *testing.T) {
310339
},
311340
},
312341
},
342+
&ngfAPIv1alpha1.UpstreamSettingsPolicy{
343+
ObjectMeta: metav1.ObjectMeta{
344+
Name: "usp-lb-method",
345+
Namespace: "test",
346+
},
347+
Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{
348+
LoadBalancingMethod: helpers.GetPointer(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
349+
},
350+
},
313351
},
314352
expUpstreamSettings: UpstreamSettings{
315353
ZoneSize: "2m",
@@ -319,6 +357,7 @@ func TestProcess(t *testing.T) {
319357
Time: "5s",
320358
Timeout: "10s",
321359
},
360+
LoadBalancingMethod: string(ngfAPIv1alpha1.LoadBalancingTypeIPHash),
322361
},
323362
},
324363
}

internal/controller/nginx/config/policies/upstreamsettings/validator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ func conflicts(a, b ngfAPI.UpstreamSettingsPolicySpec) bool {
8383
}
8484
}
8585

86+
if a.LoadBalancingMethod != nil && b.LoadBalancingMethod != nil {
87+
return true
88+
}
89+
8690
return false
8791
}
8892

internal/controller/nginx/config/policies/upstreamsettings/validator_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func createValidPolicy() *ngfAPI.UpstreamSettingsPolicy {
3838
Timeout: helpers.GetPointer[ngfAPI.Duration]("30s"),
3939
Connections: helpers.GetPointer[int32](100),
4040
},
41+
LoadBalancingMethod: helpers.GetPointer(ngfAPI.LoadBalancingTypeRandomTwoLeastConnection),
4142
},
4243
Status: v1.PolicyStatus{},
4344
}
@@ -176,6 +177,7 @@ func TestValidator_Conflicts(t *testing.T) {
176177
Requests: helpers.GetPointer[int32](900),
177178
Time: helpers.GetPointer[ngfAPI.Duration]("50s"),
178179
},
180+
LoadBalancingMethod: helpers.GetPointer(ngfAPI.LoadBalancingTypeRandomTwoLeastConnection),
179181
},
180182
},
181183
polB: &ngfAPI.UpstreamSettingsPolicy{
@@ -246,6 +248,16 @@ func TestValidator_Conflicts(t *testing.T) {
246248
},
247249
conflicts: true,
248250
},
251+
{
252+
name: "load balancing method conflicts",
253+
polA: createValidPolicy(),
254+
polB: &ngfAPI.UpstreamSettingsPolicy{
255+
Spec: ngfAPI.UpstreamSettingsPolicySpec{
256+
LoadBalancingMethod: helpers.GetPointer(ngfAPI.LoadBalancingTypeIPHash),
257+
},
258+
},
259+
conflicts: true,
260+
},
249261
}
250262

251263
v := upstreamsettings.NewValidator(nil)

internal/controller/nginx/config/upstreams.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const (
3232
plusZoneSizeStream = "1m"
3333
// stateDir is the directory for storing state files.
3434
stateDir = "/var/lib/nginx/state"
35+
// default load balancing method.
36+
defaultLBMethod = "random two least_conn"
3537
)
3638

3739
// keepAliveChecker takes an upstream name and returns if it has keep alive settings enabled.
@@ -185,12 +187,18 @@ func (g GeneratorImpl) createUpstream(
185187
}
186188
}
187189

190+
chosenLBMethod := defaultLBMethod
191+
if upstreamPolicySettings.LoadBalancingMethod != "" {
192+
chosenLBMethod = upstreamPolicySettings.LoadBalancingMethod
193+
}
194+
188195
return http.Upstream{
189-
Name: up.Name,
190-
ZoneSize: zoneSize,
191-
StateFile: stateFile,
192-
Servers: upstreamServers,
193-
KeepAlive: upstreamPolicySettings.KeepAlive,
196+
Name: up.Name,
197+
ZoneSize: zoneSize,
198+
StateFile: stateFile,
199+
Servers: upstreamServers,
200+
KeepAlive: upstreamPolicySettings.KeepAlive,
201+
LoadBalancingMethod: chosenLBMethod,
194202
}
195203
}
196204

internal/controller/nginx/config/upstreams_template.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ package config
1010
const upstreamsTemplateText = `
1111
{{ range $u := . }}
1212
upstream {{ $u.Name }} {
13-
random two least_conn;
13+
{{ if $u.LoadBalancingMethod -}}
14+
{{ $u.LoadBalancingMethod }};
15+
{{- end }}
1416
{{ if $u.ZoneSize -}}
1517
zone {{ $u.Name }} {{ $u.ZoneSize }};
1618
{{ end -}}

0 commit comments

Comments
 (0)