Skip to content

Commit 4d337d4

Browse files
authored
Merge pull request #274 from Dean-Coakley/lb-method
Change the default load balancing method to least_conn
2 parents ef6d825 + 547055c commit 4d337d4

File tree

7 files changed

+176
-5
lines changed

7 files changed

+176
-5
lines changed

examples/customization/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The table below summarizes all of the options. For some of them, there are examp
3939
| N/A | `http-snippets` | Sets a custom snippet in http context. | N/A | |
4040
| `nginx.org/location-snippets` | `location-snippets` | Sets a custom snippet in location context. | N/A | |
4141
| `nginx.org/server-snippets` | `server-snippets` | Sets a custom snippet in server context. | N/A | |
42-
| `nginx.org/lb-method` | `lb-method` | Sets the [load balancing method](https://www.nginx.com/resources/admin-guide/load-balancer/#method). The default `""` specifies the round-robin method. | `""` | |
42+
| `nginx.org/lb-method` | `lb-method` | Sets the [load balancing method](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#choosing-a-load-balancing-method). To use the round-robin method, specify `"round_robin"`. | `"least_conn"` | |
4343
| `nginx.org/listen-ports` | N/A | Configures HTTP ports that NGINX will listen on. | `[80]` | |
4444
| `nginx.org/listen-ports-ssl` | N/A | Configures HTTPS ports that NGINX will listen on. | `[443]` | |
4545
| N/A | `worker-processes` | Sets the value of the [worker_processes](http://nginx.org/en/docs/ngx_core_module.html#worker_processes) directive. | `auto` | |
@@ -64,7 +64,7 @@ The table below summarizes all of the options. For some of them, there are examp
6464

6565
1. Make sure that you specify the configmaps resource to use when you start an Ingress controller.
6666
For example, `-nginx-configmaps=default/nginx-config`, where we specify
67-
the config map to use with the following format: `<namespace>/<name>`.
67+
the config map to use with the following format: `<namespace>/<name>`.
6868

6969
1. Create a configmaps file with the name *nginx-config.yaml* and set the values
7070
that make sense for your setup:

examples/customization/nginx-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ data:
4545
if ($new_uri) {
4646
rewrite ^ $new_uri permanent;
4747
}
48+
lb-method: "round_robin" # default is least_conn. Sets the load balancing method for upstreams. See https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#choosing-a-load-balancing-method
4849
location-snippets: | # No default. Pipe is used for multiple line snippets. Make sure the snippet is not a default value, in order to avoid duplication.
4950
proxy_temp_path /var/nginx/proxy_temp;
5051
charset koi8-r;

nginx-controller/controller/controller.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ import (
3434
"k8s.io/client-go/tools/cache"
3535
"k8s.io/client-go/tools/record"
3636

37+
"sort"
38+
3739
api_v1 "k8s.io/api/core/v1"
3840
extensions "k8s.io/api/extensions/v1beta1"
3941
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40-
"sort"
4142
)
4243

4344
const (
@@ -449,7 +450,19 @@ func (lbc *LoadBalancerController) syncCfgm(task Task) {
449450
}
450451

451452
if lbMethod, exists := cfgm.Data["lb-method"]; exists {
452-
cfg.LBMethod = lbMethod
453+
if lbc.nginxPlus {
454+
if parsedMethod, err := nginx.ParseLBMethodForPlus(lbMethod); err != nil {
455+
glog.Errorf("Configmap %s/%s: Invalid value for the lb-method key: got %q: %v", cfgm.GetNamespace(), cfgm.GetName(), lbMethod, err)
456+
} else {
457+
cfg.LBMethod = parsedMethod
458+
}
459+
} else {
460+
if parsedMethod, err := nginx.ParseLBMethod(lbMethod); err != nil {
461+
glog.Errorf("Configmap %s/%s: Invalid value for the lb-method key: got %q: %v", cfgm.GetNamespace(), cfgm.GetName(), lbMethod, err)
462+
} else {
463+
cfg.LBMethod = parsedMethod
464+
}
465+
}
453466
}
454467

455468
if proxyConnectTimeout, exists := cfgm.Data["proxy-connect-timeout"]; exists {

nginx-controller/nginx/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,6 @@ func NewDefaultConfig() *Config {
7373
SSLPorts: []int{443},
7474
MaxFails: 1,
7575
FailTimeout: "10s",
76+
LBMethod: "least_conn",
7677
}
7778
}

nginx-controller/nginx/configurator.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,19 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config {
280280

281281
//Override from annotation
282282
if lbMethod, exists := ingEx.Ingress.Annotations["nginx.org/lb-method"]; exists {
283-
ingCfg.LBMethod = lbMethod
283+
if cnf.isPlus() {
284+
if parsedMethod, err := ParseLBMethodForPlus(lbMethod); err != nil {
285+
glog.Errorf("Ingress %s/%s: Invalid value for the nginx.org/lb-method: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), lbMethod, err)
286+
} else {
287+
ingCfg.LBMethod = parsedMethod
288+
}
289+
} else {
290+
if parsedMethod, err := ParseLBMethod(lbMethod); err != nil {
291+
glog.Errorf("Ingress %s/%s: Invalid value for the nginx.org/lb-method: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), lbMethod, err)
292+
} else {
293+
ingCfg.LBMethod = parsedMethod
294+
}
295+
}
284296
}
285297

286298
if serverTokens, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/server-tokens", ingEx.Ingress); exists {

nginx-controller/nginx/extensions.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package nginx
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// ParseLBMethod parses method and matches it to a corresponding load balancing method in NGINX. An error is returned if method is not valid
9+
func ParseLBMethod(method string) (string, error) {
10+
method = strings.TrimSpace(method)
11+
if method == "round_robin" {
12+
return "", nil
13+
}
14+
if strings.HasPrefix(method, "hash") {
15+
method, err := validateHashLBMethod(method)
16+
return method, err
17+
}
18+
19+
if method == "least_conn" || method == "ip_hash" {
20+
return method, nil
21+
}
22+
return "", fmt.Errorf("Invalid load balancing method: %q", method)
23+
}
24+
25+
var nginxPlusLBValidInput = map[string]bool{
26+
"least_time": true,
27+
"last_byte": true,
28+
"least_conn": true,
29+
"ip_hash": true,
30+
"least_time header": true,
31+
"least_time last_byte": true,
32+
"least_time header inflight": true,
33+
"least_time last_byte inflight": true,
34+
}
35+
36+
// ParseLBMethodForPlus parses method and matches it to a corresponding load balancing method in NGINX Plus. An error is returned if method is not valid
37+
func ParseLBMethodForPlus(method string) (string, error) {
38+
method = strings.TrimSpace(method)
39+
if method == "round_robin" {
40+
return "", nil
41+
}
42+
if strings.HasPrefix(method, "hash") {
43+
method, err := validateHashLBMethod(method)
44+
return method, err
45+
}
46+
47+
if _, exists := nginxPlusLBValidInput[method]; exists {
48+
return method, nil
49+
}
50+
return "", fmt.Errorf("Invalid load balancing method: %q", method)
51+
}
52+
53+
func validateHashLBMethod(method string) (string, error) {
54+
keyWords := strings.Split(method, " ")
55+
if keyWords[0] == "hash" {
56+
if len(keyWords) == 2 || len(keyWords) == 3 && keyWords[2] == "consistent" {
57+
return method, nil
58+
}
59+
}
60+
return "", fmt.Errorf("Invalid load balancing method: %q", method)
61+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package nginx
2+
3+
import "testing"
4+
5+
func TestParseLBMethod(t *testing.T) {
6+
var testsWithValidInput = []struct {
7+
input string
8+
expected string
9+
}{
10+
{"least_conn", "least_conn"},
11+
{"round_robin", ""},
12+
{"ip_hash", "ip_hash"},
13+
{"hash $request_id", "hash $request_id"},
14+
{"hash $request_id consistent", "hash $request_id consistent"},
15+
}
16+
17+
var invalidInput = []string{
18+
"",
19+
"blabla",
20+
"least_time header",
21+
"hash123",
22+
"hash $request_id conwrongspelling",
23+
}
24+
25+
for _, test := range testsWithValidInput {
26+
result, err := ParseLBMethod(test.input)
27+
if err != nil {
28+
t.Errorf("TestParseLBMethod(%q) returned an error for valid input", test.input)
29+
}
30+
31+
if result != test.expected {
32+
t.Errorf("TestParseLBMethod(%q) returned %q expected %q", test.input, result, test.expected)
33+
}
34+
}
35+
36+
for _, input := range invalidInput {
37+
_, err := ParseLBMethod(input)
38+
if err == nil {
39+
t.Errorf("TestParseLBMethod(%q) does not return an error for invalid input", input)
40+
}
41+
}
42+
}
43+
44+
func TestParseLBMethodForPlus(t *testing.T) {
45+
var testsWithValidInput = []struct {
46+
input string
47+
expected string
48+
}{
49+
{"least_conn", "least_conn"},
50+
{"round_robin", ""},
51+
{"ip_hash", "ip_hash"},
52+
{"hash $request_id", "hash $request_id"},
53+
{"least_time header", "least_time header"},
54+
{"least_time last_byte", "least_time last_byte"},
55+
{"least_time header inflight", "least_time header inflight"},
56+
{"least_time last_byte inflight", "least_time last_byte inflight"},
57+
}
58+
59+
var invalidInput = []string{
60+
"",
61+
"blabla",
62+
"hash123",
63+
"least_time inflight header",
64+
}
65+
66+
for _, test := range testsWithValidInput {
67+
result, err := ParseLBMethodForPlus(test.input)
68+
if err != nil {
69+
t.Errorf("TestParseLBMethod(%q) returned an error for valid input", test.input)
70+
}
71+
72+
if result != test.expected {
73+
t.Errorf("TestParseLBMethod(%q) returned %q expected %q", test.input, result, test.expected)
74+
}
75+
}
76+
77+
for _, input := range invalidInput {
78+
_, err := ParseLBMethodForPlus(input)
79+
if err == nil {
80+
t.Errorf("TestParseLBMethod(%q) does not return an error for invalid input", input)
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)