Skip to content

Support slow-start for Plus #287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/customization/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The table below summarizes all of the options. For some of them, there are examp
| `nginx.com/health-checks` | N/A | Enables active health checks. | `False` | [Support for Active Health Checks](../health-checks). |
| `nginx.com/health-checks-mandatory` | N/A | Configures active health checks as mandatory. | `False` | [Support for Active Health Checks](../health-checks). |
| `nginx.com/health-checks-mandatory-queue` | N/A | When active health checks are mandatory, configures a queue for temporary storing incoming requests during the time when NGINX Plus is checking the health of the endpoints after a configuration reload. | `0` | [Support for Active Health Checks](../health-checks). |
| `nginx.com/slow-start` | N/A | Sets the upstream server [slow-start period](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#server-slow-start). By default, slow-start is activated after a server becomes [available](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#passive-health-checks) or [healthy](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#active-health-checks). To enable slow-start for newly added servers, configure [mandatory active health checks](../health-checks). | `"0s"` | |

## Using ConfigMaps

Expand Down
1 change: 1 addition & 0 deletions nginx-controller/nginx/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Config struct {
HealthCheckEnabled bool
HealthCheckMandatory bool
HealthCheckMandatoryQueue int64
SlowStart string

// http://nginx.org/en/docs/http/ngx_http_realip_module.html
RealIPHeader string
Expand Down
42 changes: 30 additions & 12 deletions nginx-controller/nginx/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,22 +349,38 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config {
}
if cnf.isPlus() {
ingCfg.HealthCheckEnabled = healthCheckEnabled
if healthCheckMandatory, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory", ingEx.Ingress); exists {
if err != nil {
glog.Error(err)
}
ingCfg.HealthCheckMandatory = healthCheckMandatory
if healthCheckQueue, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory-queue", ingEx.Ingress); exists {
if err != nil {
glog.Error(err)
}
ingCfg.HealthCheckMandatoryQueue = healthCheckQueue
}
}
} else {
glog.Warning("Annotation 'nginx.com/health-checks' requires NGINX Plus")
}
}
if ingCfg.HealthCheckEnabled {
if healthCheckMandatory, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory", ingEx.Ingress); exists {
if err != nil {
glog.Error(err)
}
ingCfg.HealthCheckMandatory = healthCheckMandatory
}
}
if ingCfg.HealthCheckMandatory {
if healthCheckQueue, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory-queue", ingEx.Ingress); exists {
if err != nil {
glog.Error(err)
}
ingCfg.HealthCheckMandatoryQueue = healthCheckQueue
}
}

if slowStart, exists := ingEx.Ingress.Annotations["nginx.com/slow-start"]; exists {
if parsedSlowStart, err := ParseSlowStart(slowStart); err != nil {
glog.Errorf("Ingress %s/%s: Invalid value nginx.org/slow-start: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), slowStart, err)
} else {
if cnf.isPlus() {
ingCfg.SlowStart = parsedSlowStart
} else {
glog.Warning("Annotation 'nginx.com/slow-start' requires NGINX Plus")
}
}
}

if serverTokens, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/server-tokens", ingEx.Ingress); exists {
if err != nil {
Expand Down Expand Up @@ -732,6 +748,7 @@ func (cnf *Configurator) createUpstream(ingEx *IngressEx, name string, backend *
Port: addressport[1],
MaxFails: cfg.MaxFails,
FailTimeout: cfg.FailTimeout,
SlowStart: cfg.SlowStart,
})
}
if len(upsServers) > 0 {
Expand Down Expand Up @@ -905,6 +922,7 @@ func (cnf *Configurator) updatePlusEndpoints(ingEx *IngressEx) error {
cfg := plus.ServerConfig{
MaxFails: ingCfg.MaxFails,
FailTimeout: ingCfg.FailTimeout,
SlowStart: ingCfg.SlowStart,
}

if ingEx.Ingress.Spec.Backend != nil {
Expand Down
27 changes: 27 additions & 0 deletions nginx-controller/nginx/extensions.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package nginx

import (
"errors"
"fmt"
"regexp"
"strings"
)

Expand Down Expand Up @@ -59,3 +61,28 @@ func validateHashLBMethod(method string) (string, error) {
}
return "", fmt.Errorf("Invalid load balancing method: %q", method)
}

// http://nginx.org/en/docs/syntax.html
var validTimeSuffixes = []string{
"ms",
"s",
"m",
"h",
"d",
"w",
"M",
"y",
}

var durationEscaped = strings.Join(validTimeSuffixes, "|")
var validNginxTime = regexp.MustCompile(`^([0-9]+([` + durationEscaped + `]?){0,1} *)+$`)

// ParseSlowStart ensures that the slow_start value in the annotation is valid.
func ParseSlowStart(s string) (string, error) {
s = strings.TrimSpace(s)

if validNginxTime.MatchString(s) {
return s, nil
}
return "", errors.New("Invalid time string")
}
21 changes: 21 additions & 0 deletions nginx-controller/nginx/extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,24 @@ func TestParseLBMethodForPlus(t *testing.T) {
}
}
}


func TestParseSlowStart(t *testing.T) {
var testsWithValidInput = []string{"1", "1m10s", "11 11", "5m 30s", "1s", "100m", "5w", "15m", "11M", "3h", "100y", "600"}
var invalidInput = []string{"ss", "rM", "m0m", "s1s", "-5s", "", "1L"}
for _, test := range testsWithValidInput {
result, err := ParseSlowStart(test)
if err != nil {
t.Errorf("TestParseSlowStart(%q) returned an error for valid input", test)
}
if test != result {
t.Errorf("TestParseSlowStart(%q) returned %q expected %q", test, result, test)
}
}
for _, test := range invalidInput {
result, err := ParseSlowStart(test)
if err == nil {
t.Errorf("TestParseSlowStart(%q) didn't return error. Returned: %q", test, result)
}
}
}
1 change: 1 addition & 0 deletions nginx-controller/nginx/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type UpstreamServer struct {
Port string
MaxFails int64
FailTimeout string
SlowStart string
}

// HealthCheck describes an active HTTP health check
Expand Down
2 changes: 2 additions & 0 deletions nginx-controller/nginx/plus/nginx_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type NginxAPIController struct {
type ServerConfig struct {
MaxFails int64
FailTimeout string
SlowStart string
}

func NewNginxAPIController(httpClient *http.Client, endpoint string, local bool) (*NginxAPIController, error) {
Expand All @@ -37,6 +38,7 @@ func (nginx *NginxAPIController) UpdateServers(upstream string, servers []string
Server: s,
MaxFails: config.MaxFails,
FailTimeout: config.FailTimeout,
SlowStart: config.SlowStart,
})
}

Expand Down
1 change: 1 addition & 0 deletions nginx-controller/nginx/plus/nginx_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type UpstreamServer struct {
Server string `json:"server"`
MaxFails int64 `json:"max_fails"`
FailTimeout string `json:"fail_timeout,omitempty"`
SlowStart string `json:"slow_start,omitempty"`
}

type apiErrorResponse struct {
Expand Down
3 changes: 2 additions & 1 deletion nginx-controller/nginx/templates/nginx-plus.ingress.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ upstream {{$upstream.Name}} {
zone {{$upstream.Name}} 256k;
{{if $upstream.LBMethod }}{{$upstream.LBMethod}};{{end}}
{{range $server := $upstream.UpstreamServers}}
server {{$server.Address}}:{{$server.Port}} max_fails={{$server.MaxFails}} fail_timeout={{$server.FailTimeout}};{{end}}
server {{$server.Address}}:{{$server.Port}} max_fails={{$server.MaxFails}} fail_timeout={{$server.FailTimeout}}
{{- if $server.SlowStart}} slow_start={{$server.SlowStart}}{{end}};{{end}}
{{if $upstream.StickyCookie}}
sticky cookie {{$upstream.StickyCookie}};
{{end}}
Expand Down
1 change: 1 addition & 0 deletions nginx-controller/nginx/templates/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var testUps = nginx.Upstream{
Port: "8181",
MaxFails: 0,
FailTimeout: "1s",
SlowStart: "5s",
},
},
}
Expand Down