From abb1ab5465c0e8c85eeb451035912262d1e80168 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Tue, 4 Oct 2022 19:58:14 +0300 Subject: [PATCH 1/6] Added support for Envoy slow start mode. Signed-off-by: Tero Saarni --- apis/projectcontour/v1/httpproxy.go | 34 ++++++++ .../v1/zz_generated.deepcopy.go | 20 +++++ changelogs/unreleased/4772-tsaarni-major.md | 5 ++ examples/contour/01-crds.yaml | 72 +++++++++++++++++ examples/render/contour-deployment.yaml | 72 +++++++++++++++++ .../render/contour-gateway-provisioner.yaml | 72 +++++++++++++++++ examples/render/contour-gateway.yaml | 72 +++++++++++++++++ examples/render/contour.yaml | 72 +++++++++++++++++ internal/dag/dag.go | 13 +++ internal/dag/httpproxy_processor.go | 34 ++++++++ internal/dag/httpproxy_processor_test.go | 54 +++++++++++++ internal/envoy/cluster.go | 3 + internal/envoy/v3/cluster.go | 33 ++++++++ internal/envoy/v3/cluster_test.go | 33 ++++++++ .../docs/main/config/api-reference.html | 80 +++++++++++++++++++ site/content/docs/main/config/slow-start.md | 38 +++++++++ site/data/docs/main-toc.yml | 2 + 17 files changed, 709 insertions(+) create mode 100644 changelogs/unreleased/4772-tsaarni-major.md create mode 100644 site/content/docs/main/config/slow-start.md diff --git a/apis/projectcontour/v1/httpproxy.go b/apis/projectcontour/v1/httpproxy.go index 5b2f8a82be4..a29b4d5c83e 100644 --- a/apis/projectcontour/v1/httpproxy.go +++ b/apis/projectcontour/v1/httpproxy.go @@ -811,6 +811,9 @@ type Service struct { // The policies for rewriting Set-Cookie header attributes. // +optional CookieRewritePolicies []CookieRewritePolicy `json:"cookieRewritePolicies,omitempty"` + // Slow start will gradually increase amount of traffic to a newly added endpoint. + // +optional + SlowStart *SlowStart `json:"slowStart,omitempty"` } // HTTPHealthCheckPolicy defines health checks on the upstream service. @@ -1168,3 +1171,34 @@ type HTTPProxyList struct { metav1.ListMeta `json:"metadata"` Items []HTTPProxy `json:"items"` } + +// SlowStart will gradually increase amount of traffic to a newly added endpoint. +// It works only with RoundRobin and WeightedLeastRequest load balancing strategies. +type SlowStart struct { + // The duration of slow start window. + // Duration is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + // +required + // +kubebuilder:validation:Pattern=`^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$` + Window string `json:"window"` + + // The speed of traffic increase over the slow start window. + // Defaults to 1.0, so that endpoint would get linearly increasing amount of traffic. + // When increasing the value for this parameter, the speed of traffic ramp-up increases non-linearly. + // The value of aggression parameter should be greater than 0.0. + // + // More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start + // + // +optional + // +kubebuilder:default=`1.0` + // +kubebuilder:validation:Pattern=`^([0-9]+([.][0-9]+)?|[.][0-9]+)$` + Aggression string `json:"aggression"` + + // The minimum percentage of origin weight that avoids too small new weight, which may cause endpoints in slow start mode receive no traffic in slow start window. + // If not specified, the default is 10%. + // +optional + // +kubebuilder:default=10 + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=100 + MinimumWeightPercent uint32 `json:"minWeightPercent"` +} diff --git a/apis/projectcontour/v1/zz_generated.deepcopy.go b/apis/projectcontour/v1/zz_generated.deepcopy.go index 501bb2d33bb..fd7723df5a1 100644 --- a/apis/projectcontour/v1/zz_generated.deepcopy.go +++ b/apis/projectcontour/v1/zz_generated.deepcopy.go @@ -1065,6 +1065,11 @@ func (in *Service) DeepCopyInto(out *Service) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.SlowStart != nil { + in, out := &in.SlowStart, &out.SlowStart + *out = new(SlowStart) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Service. @@ -1077,6 +1082,21 @@ func (in *Service) DeepCopy() *Service { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlowStart) DeepCopyInto(out *SlowStart) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowStart. +func (in *SlowStart) DeepCopy() *SlowStart { + if in == nil { + return nil + } + out := new(SlowStart) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubCondition) DeepCopyInto(out *SubCondition) { *out = *in diff --git a/changelogs/unreleased/4772-tsaarni-major.md b/changelogs/unreleased/4772-tsaarni-major.md new file mode 100644 index 00000000000..0fc0f9347ac --- /dev/null +++ b/changelogs/unreleased/4772-tsaarni-major.md @@ -0,0 +1,5 @@ +## Slow start mode + +Slow start mode is a configuration setting that is used to gradually increase the amount of traffic targeted to a newly added upstream endpoint. +This can be useful for example with JVM based applications, that might otherwise get overwhelmed during JIT warm-up period. +For more information [see here](https://projectcontour.io/docs/main/config/slow-start/). diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index afc774ec31c..b9b578717b6 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -3153,6 +3153,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount + of traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. + When increasing the value for this parameter, the + speed of traffic ramp-up increases non-linearly. + The value of aggression parameter should be greater + than 0.0. \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic + in slow start window. If not specified, the default + is 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", + "s", "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate @@ -3495,6 +3531,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount of + traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. When + increasing the value for this parameter, the speed + of traffic ramp-up increases non-linearly. The value + of aggression parameter should be greater than 0.0. + \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic in + slow start window. If not specified, the default is + 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", "s", + "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index 87a22b93246..a2a72a18986 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -3362,6 +3362,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount + of traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. + When increasing the value for this parameter, the + speed of traffic ramp-up increases non-linearly. + The value of aggression parameter should be greater + than 0.0. \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic + in slow start window. If not specified, the default + is 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", + "s", "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate @@ -3704,6 +3740,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount of + traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. When + increasing the value for this parameter, the speed + of traffic ramp-up increases non-linearly. The value + of aggression parameter should be greater than 0.0. + \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic in + slow start window. If not specified, the default is + 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", "s", + "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 0bb2bc0ec95..3a752acd8eb 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -3167,6 +3167,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount + of traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. + When increasing the value for this parameter, the + speed of traffic ramp-up increases non-linearly. + The value of aggression parameter should be greater + than 0.0. \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic + in slow start window. If not specified, the default + is 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", + "s", "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate @@ -3509,6 +3545,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount of + traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. When + increasing the value for this parameter, the speed + of traffic ramp-up increases non-linearly. The value + of aggression parameter should be greater than 0.0. + \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic in + slow start window. If not specified, the default is + 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", "s", + "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index cba5495c52e..7cf09a3fd3f 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -3368,6 +3368,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount + of traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. + When increasing the value for this parameter, the + speed of traffic ramp-up increases non-linearly. + The value of aggression parameter should be greater + than 0.0. \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic + in slow start window. If not specified, the default + is 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", + "s", "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate @@ -3710,6 +3746,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount of + traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. When + increasing the value for this parameter, the speed + of traffic ramp-up increases non-linearly. The value + of aggression parameter should be greater than 0.0. + \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic in + slow start window. If not specified, the default is + 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", "s", + "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index e2cbfe65e77..412b7b7b505 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -3362,6 +3362,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount + of traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. + When increasing the value for this parameter, the + speed of traffic ramp-up increases non-linearly. + The value of aggression parameter should be greater + than 0.0. \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic + in slow start window. If not specified, the default + is 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", + "s", "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate @@ -3704,6 +3740,42 @@ spec: type: object type: array type: object + slowStart: + description: Slow start will gradually increase amount of + traffic to a newly added endpoint. + properties: + aggression: + default: "1.0" + description: "The speed of traffic increase over the + slow start window. Defaults to 1.0, so that endpoint + would get linearly increasing amount of traffic. When + increasing the value for this parameter, the speed + of traffic ramp-up increases non-linearly. The value + of aggression parameter should be greater than 0.0. + \n More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start" + pattern: ^([0-9]+([.][0-9]+)?|[.][0-9]+)$ + type: string + minWeightPercent: + default: 10 + description: The minimum percentage of origin weight + that avoids too small new weight, which may cause + endpoints in slow start mode receive no traffic in + slow start window. If not specified, the default is + 10%. + format: int32 + maximum: 100 + minimum: 0 + type: integer + window: + description: The duration of slow start window. Duration + is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + Valid time units are "ns", "us" (or "µs"), "ms", "s", + "m", "h". + pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+)$ + type: string + required: + - window + type: object validation: description: UpstreamValidation defines how to verify the backend service's certificate diff --git a/internal/dag/dag.go b/internal/dag/dag.go index 72e2e19793e..2bab2b55428 100644 --- a/internal/dag/dag.go +++ b/internal/dag/dag.go @@ -821,6 +821,8 @@ type Cluster struct { // TimeoutPolicy specifies how to handle timeouts for this cluster. TimeoutPolicy ClusterTimeoutPolicy + + SlowStartConfig *SlowStartConfig } // WeightedService represents the load balancing weight of a @@ -1007,3 +1009,14 @@ func wildcardDomainHeaderMatch(fqdn string) HeaderMatchCondition { Value: singleDNSLabelWildcardRegex + regexp.QuoteMeta(fqdn[1:]), } } + +// SlowStartConfig holds configuration for gradually increasing amount of traffic to a newly added endpoint. +type SlowStartConfig struct { + Window time.Duration + Aggression float64 + MinWeightPercent uint32 +} + +func (s *SlowStartConfig) String() string { + return fmt.Sprintf("%s%f%d", s.Window.String(), s.Aggression, s.MinWeightPercent) +} diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index e2b49d9c262..61b4ed2b5f9 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -870,6 +870,13 @@ func (p *HTTPProxyProcessor) computeRoutes( } } + slowStartConfig, err := slowStartConfig(service.SlowStart) + if err != nil { + validCond.AddErrorf(contour_api_v1.ConditionTypeServiceError, "SlowStartInvalid", + "%s on slow start", err) + return nil + } + c := &Cluster{ Upstream: s, LoadBalancerPolicy: lbPolicy, @@ -884,6 +891,7 @@ func (p *HTTPProxyProcessor) computeRoutes( DNSLookupFamily: string(p.DNSLookupFamily), ClientCertificate: clientCertSecret, TimeoutPolicy: ctp, + SlowStartConfig: slowStartConfig, } if service.Mirror && r.MirrorPolicy != nil { validCond.AddError(contour_api_v1.ConditionTypeServiceError, "OnlyOneMirror", @@ -1431,3 +1439,29 @@ func directResponsePolicy(direct *contour_api_v1.HTTPDirectResponsePolicy) *Dire return directResponse(uint32(direct.StatusCode), direct.Body) } + +func slowStartConfig(slowStart *contour_api_v1.SlowStart) (*SlowStartConfig, error) { + // If slow start is not configured, return nil. + if slowStart == nil { + return nil, nil + } + + window, err := time.ParseDuration(slowStart.Window) + if err != nil { + return nil, fmt.Errorf("error parsing window: %s", err) + } + + aggression := float64(1.0) + if slowStart.Aggression != "" { + aggression, err = strconv.ParseFloat(slowStart.Aggression, 64) + if err != nil { + return nil, fmt.Errorf("error parsing aggression: %s", err) + } + } + + return &SlowStartConfig{ + Window: window, + Aggression: aggression, + MinWeightPercent: slowStart.MinimumWeightPercent, + }, nil +} diff --git a/internal/dag/httpproxy_processor_test.go b/internal/dag/httpproxy_processor_test.go index 6ebac067358..a33f6aec88d 100644 --- a/internal/dag/httpproxy_processor_test.go +++ b/internal/dag/httpproxy_processor_test.go @@ -292,3 +292,57 @@ func TestToCORSPolicy(t *testing.T) { } } + +func TestSlowStart(t *testing.T) { + tests := map[string]struct { + input *contour_api_v1.SlowStart + want *SlowStartConfig + wantErr bool + }{ + "window only": { + input: &contour_api_v1.SlowStart{ + Window: "10s", + }, + want: &SlowStartConfig{ + Window: 10 * time.Second, + Aggression: 1.0, + MinWeightPercent: 0, // Default value 10% is set only via CRD defaulting, so we get 0 here. + }, + }, + "with all fields": { + input: &contour_api_v1.SlowStart{ + Window: "10s", + Aggression: "1.1", + MinimumWeightPercent: 5, + }, + want: &SlowStartConfig{ + Window: 10 * time.Second, + Aggression: 1.1, + MinWeightPercent: 5, + }, + }, + "invalid window, missing unit": { + input: &contour_api_v1.SlowStart{ + Window: "10", + }, + wantErr: true, + }, + "invalid aggression, not float": { + input: &contour_api_v1.SlowStart{ + Window: "10s", + Aggression: "not-a-float", + }, + wantErr: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got, gotErr := slowStartConfig(tc.input) + if tc.wantErr { + require.Error(t, gotErr) + } + require.Equal(t, tc.want, got) + }) + } +} diff --git a/internal/envoy/cluster.go b/internal/envoy/cluster.go index 41ffc2c3575..e6d4bf94fbe 100644 --- a/internal/envoy/cluster.go +++ b/internal/envoy/cluster.go @@ -54,6 +54,9 @@ func Clustername(cluster *dag.Cluster) string { if !cluster.TimeoutPolicy.IdleConnectionTimeout.UseDefault() { buf += cluster.TimeoutPolicy.IdleConnectionTimeout.Duration().String() } + if cluster.SlowStartConfig != nil { + buf += cluster.SlowStartConfig.String() + } // This isn't a crypto hash, we just want a unique name. hash := sha1.Sum([]byte(buf)) // nolint:gosec diff --git a/internal/envoy/v3/cluster.go b/internal/envoy/v3/cluster.go index ada3e041ed9..8d01e760b75 100644 --- a/internal/envoy/v3/cluster.go +++ b/internal/envoy/v3/cluster.go @@ -107,6 +107,25 @@ func Cluster(c *dag.Cluster) *envoy_cluster_v3.Cluster { cluster.TypedExtensionProtocolOptions = protocolOptions(httpVersion, c.TimeoutPolicy.IdleConnectionTimeout) + if c.SlowStartConfig != nil { + switch cluster.LbPolicy { + case envoy_cluster_v3.Cluster_LEAST_REQUEST: + cluster.LbConfig = &envoy_cluster_v3.Cluster_LeastRequestLbConfig_{ + LeastRequestLbConfig: &envoy_cluster_v3.Cluster_LeastRequestLbConfig{ + SlowStartConfig: slowStartConfig(c.SlowStartConfig), + }, + } + case envoy_cluster_v3.Cluster_ROUND_ROBIN: + cluster.LbConfig = &envoy_cluster_v3.Cluster_RoundRobinLbConfig_{ + RoundRobinLbConfig: &envoy_cluster_v3.Cluster_RoundRobinLbConfig{ + SlowStartConfig: slowStartConfig(c.SlowStartConfig), + }, + } + default: + // Slow start is only supported for round robin and weighted least request. + } + } + return cluster } @@ -312,3 +331,17 @@ func protocolOptions(explicitHTTPVersion HTTPVersionType, idleConnectionTimeout "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": protobuf.MustMarshalAny(&options), } } + +// slowStartConfig returns the slow start configuration. +func slowStartConfig(slowStartConfig *dag.SlowStartConfig) *envoy_cluster_v3.Cluster_SlowStartConfig { + return &envoy_cluster_v3.Cluster_SlowStartConfig{ + SlowStartWindow: protobuf.Duration(slowStartConfig.Window), + Aggression: &envoy_core_v3.RuntimeDouble{ + DefaultValue: slowStartConfig.Aggression, + RuntimeKey: "contour.slowstart.aggression", + }, + MinWeightPercent: &envoy_type.Percent{ + Value: float64(slowStartConfig.MinWeightPercent), + }, + } +} diff --git a/internal/envoy/v3/cluster_test.go b/internal/envoy/v3/cluster_test.go index a85c5f50b1a..986946a3407 100644 --- a/internal/envoy/v3/cluster_test.go +++ b/internal/envoy/v3/cluster_test.go @@ -555,6 +555,39 @@ func TestCluster(t *testing.T) { }, }, }, + "slow start mode": { + cluster: &dag.Cluster{ + Upstream: service(s1), + SlowStartConfig: &dag.SlowStartConfig{ + Window: 10 * time.Second, + Aggression: 1.0, + MinWeightPercent: 10, + }, + }, + want: &envoy_cluster_v3.Cluster{ + Name: "default/kuard/443/2c8f64025b", + AltStatName: "default_kuard_443", + ClusterDiscoveryType: ClusterDiscoveryType(envoy_cluster_v3.Cluster_EDS), + EdsClusterConfig: &envoy_cluster_v3.Cluster_EdsClusterConfig{ + EdsConfig: ConfigSource("contour"), + ServiceName: "default/kuard/http", + }, + LbConfig: &envoy_cluster_v3.Cluster_RoundRobinLbConfig_{ + RoundRobinLbConfig: &envoy_cluster_v3.Cluster_RoundRobinLbConfig{ + SlowStartConfig: &envoy_cluster_v3.Cluster_SlowStartConfig{ + SlowStartWindow: protobuf.Duration(10 * time.Second), + Aggression: &envoy_core_v3.RuntimeDouble{ + DefaultValue: 1.0, + RuntimeKey: "contour.slowstart.aggression", + }, + MinWeightPercent: &envoy_type.Percent{ + Value: 10.0, + }, + }, + }, + }, + }, + }, } for name, tc := range tests { diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index c862023d3df..7b2ac4ee4fe 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -3311,6 +3311,86 @@

Service

The policies for rewriting Set-Cookie header attributes.

+ + +slowStart +
+ + +SlowStart + + + + +(Optional) +

Slow start will gradually increase amount of traffic to a newly added endpoint.

+ + + + +

SlowStart +

+

+(Appears on: +Service) +

+

+

SlowStart will gradually increase amount of traffic to a newly added endpoint. +It works only with RoundRobin and WeightedLeastRequest load balancing strategies.

+

+ + + + + + + + + + + + + + + + + + + +
FieldDescription
+window +
+ +string + +
+

The duration of slow start window. +Duration is expressed in the Go Duration format. +Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”.

+
+aggression +
+ +string + +
+(Optional) +

The speed of traffic increase over the slow start window. +Defaults to 1.0, so that endpoint would get linearly increasing amount of traffic. +When increasing the value for this parameter, the speed of traffic ramp-up increases non-linearly. +The value of aggression parameter should be greater than 0.0.

+

More info: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start

+
+minWeightPercent +
+ +uint32 + +
+(Optional) +

The minimum percentage of origin weight that avoids too small new weight, which may cause endpoints in slow start mode receive no traffic in slow start window. +If not specified, the default is 10%.

+

SubCondition diff --git a/site/content/docs/main/config/slow-start.md b/site/content/docs/main/config/slow-start.md new file mode 100644 index 00000000000..8fc68368298 --- /dev/null +++ b/site/content/docs/main/config/slow-start.md @@ -0,0 +1,38 @@ +# Slow Start Mode + +Slow start mode is a configuration setting that is used to gradually increase the amount of traffic targeted to a newly added upstream endpoint. +By default, the amount of traffic will increase linearly for the duration of time window set by `window` field, starting from 10% of the target load balancing weight and increasing to 100% gradually. +The easing function for the traffic increase can be adjusted by setting optional field `aggression`. +A value above 1.0 results in a more aggressive increase initially, slowing down when nearing the end of the time window. +Value below 1.0 results in slow initial increase, picking up speed when nearing the end of the time window. +Optional field `minWeightPercent` can be set to change the minimum percent of target weight. +It is used to avoid too small new weight, which may cause endpoint to receive no traffic in beginning of the slow start window. + +Slow start mode can be useful for example with JVM based applications, that might otherwise get overwhelmed during JIT warm-up period. +Application might perform worse immediately after pod start or after container restarts. + +The following example configures slow start mode for a service: + +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: slow-start +spec: + virtualhost: + fqdn: www.example.com + routes: + - services: + - name: java-app + port: 80 + slowStart: + window: 3s + aggression: 1.0 + minWeightPercent: 10 +``` + +Slow start mode works only with `RoundRobin` and `WeightedLeastRequest` [load balancing strategies][2]. +For more details see [Envoy documentation][1]. + +[1]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/slow_start +[2]: api/#projectcontour.io/v1.LoadBalancerPolicy diff --git a/site/data/docs/main-toc.yml b/site/data/docs/main-toc.yml index 642f685a963..604480b842b 100644 --- a/site/data/docs/main-toc.yml +++ b/site/data/docs/main-toc.yml @@ -47,6 +47,8 @@ toc: url: /config/jwt-verification - page: Annotations Reference url: /config/annotations + - page: Slow Start Mode + url: /config/slow-start - page: API Reference url: /config/api - title: Deployment From 5c6d4c6541723cea0091f28c84f29048fcddf18e Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Wed, 5 Oct 2022 17:59:25 +0300 Subject: [PATCH 2/6] updates according to review Signed-off-by: Tero Saarni --- apis/projectcontour/v1/httpproxy.go | 11 +-- .../v1/zz_generated.deepcopy.go | 14 ++-- examples/contour/01-crds.yaml | 26 ++++--- examples/render/contour-deployment.yaml | 26 ++++--- .../render/contour-gateway-provisioner.yaml | 26 ++++--- examples/render/contour-gateway.yaml | 26 ++++--- examples/render/contour.yaml | 26 ++++--- internal/dag/httpproxy_processor.go | 6 +- internal/dag/httpproxy_processor_test.go | 10 +-- internal/dag/status_test.go | 74 +++++++++++++++++++ .../docs/main/config/api-reference.html | 15 ++-- 11 files changed, 173 insertions(+), 87 deletions(-) diff --git a/apis/projectcontour/v1/httpproxy.go b/apis/projectcontour/v1/httpproxy.go index a29b4d5c83e..d0db035204a 100644 --- a/apis/projectcontour/v1/httpproxy.go +++ b/apis/projectcontour/v1/httpproxy.go @@ -813,7 +813,7 @@ type Service struct { CookieRewritePolicies []CookieRewritePolicy `json:"cookieRewritePolicies,omitempty"` // Slow start will gradually increase amount of traffic to a newly added endpoint. // +optional - SlowStart *SlowStart `json:"slowStart,omitempty"` + SlowStartPolicy *SlowStartPolicy `json:"slowStartPolicy,omitempty"` } // HTTPHealthCheckPolicy defines health checks on the upstream service. @@ -1172,9 +1172,9 @@ type HTTPProxyList struct { Items []HTTPProxy `json:"items"` } -// SlowStart will gradually increase amount of traffic to a newly added endpoint. -// It works only with RoundRobin and WeightedLeastRequest load balancing strategies. -type SlowStart struct { +// SlowStartPolicy will gradually increase amount of traffic to a newly added endpoint. +// It can be used only with RoundRobin and WeightedLeastRequest load balancing strategies, otherwise the configuration is ignored. +type SlowStartPolicy struct { // The duration of slow start window. // Duration is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". @@ -1194,7 +1194,8 @@ type SlowStart struct { // +kubebuilder:validation:Pattern=`^([0-9]+([.][0-9]+)?|[.][0-9]+)$` Aggression string `json:"aggression"` - // The minimum percentage of origin weight that avoids too small new weight, which may cause endpoints in slow start mode receive no traffic in slow start window. + // The minimum or starting percentage of traffic to send to new endpoints. + // A non-zero value helps avoid a too small initial weight, which may cause endpoints in slow start mode to receive no traffic in the beginning of the slow start window. // If not specified, the default is 10%. // +optional // +kubebuilder:default=10 diff --git a/apis/projectcontour/v1/zz_generated.deepcopy.go b/apis/projectcontour/v1/zz_generated.deepcopy.go index fd7723df5a1..7de103248b1 100644 --- a/apis/projectcontour/v1/zz_generated.deepcopy.go +++ b/apis/projectcontour/v1/zz_generated.deepcopy.go @@ -1065,9 +1065,9 @@ func (in *Service) DeepCopyInto(out *Service) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.SlowStart != nil { - in, out := &in.SlowStart, &out.SlowStart - *out = new(SlowStart) + if in.SlowStartPolicy != nil { + in, out := &in.SlowStartPolicy, &out.SlowStartPolicy + *out = new(SlowStartPolicy) **out = **in } } @@ -1083,16 +1083,16 @@ func (in *Service) DeepCopy() *Service { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SlowStart) DeepCopyInto(out *SlowStart) { +func (in *SlowStartPolicy) DeepCopyInto(out *SlowStartPolicy) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowStart. -func (in *SlowStart) DeepCopy() *SlowStart { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowStartPolicy. +func (in *SlowStartPolicy) DeepCopy() *SlowStartPolicy { if in == nil { return nil } - out := new(SlowStart) + out := new(SlowStartPolicy) in.DeepCopyInto(out) return out } diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index b9b578717b6..c715c2d1c30 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -3153,7 +3153,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3170,11 +3170,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic - in slow start window. If not specified, the default - is 10%. + description: The minimum or starting percentage of + traffic to send to new endpoints. A non-zero value + helps avoid a too small initial weight, which may + cause endpoints in slow start mode to receive no + traffic in the beginning of the slow start window. + If not specified, the default is 10%. format: int32 maximum: 100 minimum: 0 @@ -3531,7 +3532,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3548,11 +3549,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic in - slow start window. If not specified, the default is - 10%. + description: The minimum or starting percentage of traffic + to send to new endpoints. A non-zero value helps avoid + a too small initial weight, which may cause endpoints + in slow start mode to receive no traffic in the beginning + of the slow start window. If not specified, the default + is 10%. format: int32 maximum: 100 minimum: 0 diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index a2a72a18986..b299c228f1f 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -3362,7 +3362,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3379,11 +3379,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic - in slow start window. If not specified, the default - is 10%. + description: The minimum or starting percentage of + traffic to send to new endpoints. A non-zero value + helps avoid a too small initial weight, which may + cause endpoints in slow start mode to receive no + traffic in the beginning of the slow start window. + If not specified, the default is 10%. format: int32 maximum: 100 minimum: 0 @@ -3740,7 +3741,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3757,11 +3758,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic in - slow start window. If not specified, the default is - 10%. + description: The minimum or starting percentage of traffic + to send to new endpoints. A non-zero value helps avoid + a too small initial weight, which may cause endpoints + in slow start mode to receive no traffic in the beginning + of the slow start window. If not specified, the default + is 10%. format: int32 maximum: 100 minimum: 0 diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 3a752acd8eb..526cb3de3ef 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -3167,7 +3167,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3184,11 +3184,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic - in slow start window. If not specified, the default - is 10%. + description: The minimum or starting percentage of + traffic to send to new endpoints. A non-zero value + helps avoid a too small initial weight, which may + cause endpoints in slow start mode to receive no + traffic in the beginning of the slow start window. + If not specified, the default is 10%. format: int32 maximum: 100 minimum: 0 @@ -3545,7 +3546,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3562,11 +3563,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic in - slow start window. If not specified, the default is - 10%. + description: The minimum or starting percentage of traffic + to send to new endpoints. A non-zero value helps avoid + a too small initial weight, which may cause endpoints + in slow start mode to receive no traffic in the beginning + of the slow start window. If not specified, the default + is 10%. format: int32 maximum: 100 minimum: 0 diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index 7cf09a3fd3f..f512e8eb838 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -3368,7 +3368,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3385,11 +3385,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic - in slow start window. If not specified, the default - is 10%. + description: The minimum or starting percentage of + traffic to send to new endpoints. A non-zero value + helps avoid a too small initial weight, which may + cause endpoints in slow start mode to receive no + traffic in the beginning of the slow start window. + If not specified, the default is 10%. format: int32 maximum: 100 minimum: 0 @@ -3746,7 +3747,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3763,11 +3764,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic in - slow start window. If not specified, the default is - 10%. + description: The minimum or starting percentage of traffic + to send to new endpoints. A non-zero value helps avoid + a too small initial weight, which may cause endpoints + in slow start mode to receive no traffic in the beginning + of the slow start window. If not specified, the default + is 10%. format: int32 maximum: 100 minimum: 0 diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index 412b7b7b505..fc06302e83b 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -3362,7 +3362,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3379,11 +3379,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic - in slow start window. If not specified, the default - is 10%. + description: The minimum or starting percentage of + traffic to send to new endpoints. A non-zero value + helps avoid a too small initial weight, which may + cause endpoints in slow start mode to receive no + traffic in the beginning of the slow start window. + If not specified, the default is 10%. format: int32 maximum: 100 minimum: 0 @@ -3740,7 +3741,7 @@ spec: type: object type: array type: object - slowStart: + slowStartPolicy: description: Slow start will gradually increase amount of traffic to a newly added endpoint. properties: @@ -3757,11 +3758,12 @@ spec: type: string minWeightPercent: default: 10 - description: The minimum percentage of origin weight - that avoids too small new weight, which may cause - endpoints in slow start mode receive no traffic in - slow start window. If not specified, the default is - 10%. + description: The minimum or starting percentage of traffic + to send to new endpoints. A non-zero value helps avoid + a too small initial weight, which may cause endpoints + in slow start mode to receive no traffic in the beginning + of the slow start window. If not specified, the default + is 10%. format: int32 maximum: 100 minimum: 0 diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 61b4ed2b5f9..699d1a37eeb 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -870,7 +870,7 @@ func (p *HTTPProxyProcessor) computeRoutes( } } - slowStartConfig, err := slowStartConfig(service.SlowStart) + slowStartConfig, err := slowStartConfig(service.SlowStartPolicy) if err != nil { validCond.AddErrorf(contour_api_v1.ConditionTypeServiceError, "SlowStartInvalid", "%s on slow start", err) @@ -1440,7 +1440,7 @@ func directResponsePolicy(direct *contour_api_v1.HTTPDirectResponsePolicy) *Dire return directResponse(uint32(direct.StatusCode), direct.Body) } -func slowStartConfig(slowStart *contour_api_v1.SlowStart) (*SlowStartConfig, error) { +func slowStartConfig(slowStart *contour_api_v1.SlowStartPolicy) (*SlowStartConfig, error) { // If slow start is not configured, return nil. if slowStart == nil { return nil, nil @@ -1455,7 +1455,7 @@ func slowStartConfig(slowStart *contour_api_v1.SlowStart) (*SlowStartConfig, err if slowStart.Aggression != "" { aggression, err = strconv.ParseFloat(slowStart.Aggression, 64) if err != nil { - return nil, fmt.Errorf("error parsing aggression: %s", err) + return nil, fmt.Errorf("error parsing aggression: \"%s\" is not a decimal number", slowStart.Aggression) } } diff --git a/internal/dag/httpproxy_processor_test.go b/internal/dag/httpproxy_processor_test.go index a33f6aec88d..ef305f4027a 100644 --- a/internal/dag/httpproxy_processor_test.go +++ b/internal/dag/httpproxy_processor_test.go @@ -295,12 +295,12 @@ func TestToCORSPolicy(t *testing.T) { func TestSlowStart(t *testing.T) { tests := map[string]struct { - input *contour_api_v1.SlowStart + input *contour_api_v1.SlowStartPolicy want *SlowStartConfig wantErr bool }{ "window only": { - input: &contour_api_v1.SlowStart{ + input: &contour_api_v1.SlowStartPolicy{ Window: "10s", }, want: &SlowStartConfig{ @@ -310,7 +310,7 @@ func TestSlowStart(t *testing.T) { }, }, "with all fields": { - input: &contour_api_v1.SlowStart{ + input: &contour_api_v1.SlowStartPolicy{ Window: "10s", Aggression: "1.1", MinimumWeightPercent: 5, @@ -322,13 +322,13 @@ func TestSlowStart(t *testing.T) { }, }, "invalid window, missing unit": { - input: &contour_api_v1.SlowStart{ + input: &contour_api_v1.SlowStartPolicy{ Window: "10", }, wantErr: true, }, "invalid aggression, not float": { - input: &contour_api_v1.SlowStart{ + input: &contour_api_v1.SlowStartPolicy{ Window: "10s", Aggression: "not-a-float", }, diff --git a/internal/dag/status_test.go b/internal/dag/status_test.go index ed30dae48b3..f8e5c7eb284 100644 --- a/internal/dag/status_test.go +++ b/internal/dag/status_test.go @@ -3601,6 +3601,80 @@ func TestDAGStatus(t *testing.T) { }, }) + // proxyWithInvalidSlowStart is invalid because it has invalid window size syntax + proxyWithInvalidSlowStartWindow := &contour_api_v1.HTTPProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "roots", + Name: "slow-start-invalid-window", + }, + Spec: contour_api_v1.HTTPProxySpec{ + VirtualHost: &contour_api_v1.VirtualHost{ + Fqdn: "www.example.com", + }, + Routes: []contour_api_v1.Route{{ + Services: []contour_api_v1.Service{{ + Name: "home", + Port: 8080, + SlowStartPolicy: &contour_api_v1.SlowStartPolicy{ + Window: "invalid", + }, + }}, + }}, + }, + } + + // proxyWithInvalidSlowStart is invalid because it has invalid aggression syntax + proxyWithInvalidSlowStartAggression := &contour_api_v1.HTTPProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "roots", + Name: "slow-start-invalid-window", + }, + Spec: contour_api_v1.HTTPProxySpec{ + VirtualHost: &contour_api_v1.VirtualHost{ + Fqdn: "www.example.com", + }, + Routes: []contour_api_v1.Route{{ + Services: []contour_api_v1.Service{{ + Name: "home", + Port: 8080, + SlowStartPolicy: &contour_api_v1.SlowStartPolicy{ + Window: "5s", + Aggression: "invalid", + }, + }}, + }}, + }, + } + + run(t, "Slow start with invalid window syntax", testcase{ + objs: []interface{}{ + proxyWithInvalidSlowStartWindow, + fixture.ServiceRootsHome, + }, + want: map[types.NamespacedName]contour_api_v1.DetailedCondition{ + k8s.NamespacedNameOf(proxyWithInvalidSlowStartWindow): fixture.NewValidCondition(). + WithError( + contour_api_v1.ConditionTypeServiceError, + "SlowStartInvalid", + "error parsing window: time: invalid duration \"invalid\" on slow start", + ), + }, + }) + + run(t, "Slow start with invalid aggression syntax", testcase{ + objs: []interface{}{ + proxyWithInvalidSlowStartAggression, + fixture.ServiceRootsHome, + }, + want: map[types.NamespacedName]contour_api_v1.DetailedCondition{ + k8s.NamespacedNameOf(proxyWithInvalidSlowStartWindow): fixture.NewValidCondition(). + WithError( + contour_api_v1.ConditionTypeServiceError, + "SlowStartInvalid", + "error parsing aggression: \"invalid\" is not a decimal number on slow start", + ), + }, + }) } func validGatewayStatusUpdate(listenerName string, kind gatewayapi_v1beta1.Kind, attachedRoutes int) []*status.GatewayStatusUpdate { diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index 7b2ac4ee4fe..115f4f4caf2 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -3313,11 +3313,11 @@

Service -slowStart +slowStartPolicy
- -SlowStart + +SlowStartPolicy @@ -3328,15 +3328,15 @@

Service -

SlowStart +

SlowStartPolicy

(Appears on: Service)

-

SlowStart will gradually increase amount of traffic to a newly added endpoint. -It works only with RoundRobin and WeightedLeastRequest load balancing strategies.

+

SlowStartPolicy will gradually increase amount of traffic to a newly added endpoint. +It can be used only with RoundRobin and WeightedLeastRequest load balancing strategies, otherwise the configuration is ignored.

@@ -3387,7 +3387,8 @@

SlowStart

From d1ad5621a0efe24966325ed86873f8bd9d6f7469 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Wed, 5 Oct 2022 19:50:05 +0300 Subject: [PATCH 3/6] fail if slow start is defined with lb strategy which does not support it Signed-off-by: Tero Saarni --- apis/projectcontour/v1/httpproxy.go | 2 +- internal/dag/httpproxy_processor.go | 22 ++++++--- internal/dag/status_test.go | 48 +++++++++++++++++-- .../docs/main/config/api-reference.html | 2 +- 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/apis/projectcontour/v1/httpproxy.go b/apis/projectcontour/v1/httpproxy.go index d0db035204a..ab63a0a2c22 100644 --- a/apis/projectcontour/v1/httpproxy.go +++ b/apis/projectcontour/v1/httpproxy.go @@ -1173,7 +1173,7 @@ type HTTPProxyList struct { } // SlowStartPolicy will gradually increase amount of traffic to a newly added endpoint. -// It can be used only with RoundRobin and WeightedLeastRequest load balancing strategies, otherwise the configuration is ignored. +// It can be used only with RoundRobin and WeightedLeastRequest load balancing strategies. type SlowStartPolicy struct { // The duration of slow start window. // Duration is expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 699d1a37eeb..238eca22a7c 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -870,11 +870,21 @@ func (p *HTTPProxyProcessor) computeRoutes( } } - slowStartConfig, err := slowStartConfig(service.SlowStartPolicy) - if err != nil { - validCond.AddErrorf(contour_api_v1.ConditionTypeServiceError, "SlowStartInvalid", - "%s on slow start", err) - return nil + var slowStart *SlowStartConfig + if service.SlowStartPolicy != nil { + // Currently Envoy implements slow start only for RoundRobin and WeightedLeastRequest LB strategies. + if lbPolicy != "" && lbPolicy != LoadBalancerPolicyRoundRobin && lbPolicy != LoadBalancerPolicyWeightedLeastRequest { + validCond.AddErrorf(contour_api_v1.ConditionTypeServiceError, "SlowStartInvalid", + "slow start is only supported with RoundRobin or WeightedLeastRequest load balancer strategy") + return nil + } + + slowStart, err = slowStartConfig(service.SlowStartPolicy) + if err != nil { + validCond.AddErrorf(contour_api_v1.ConditionTypeServiceError, "SlowStartInvalid", + "%s on slow start", err) + return nil + } } c := &Cluster{ @@ -891,7 +901,7 @@ func (p *HTTPProxyProcessor) computeRoutes( DNSLookupFamily: string(p.DNSLookupFamily), ClientCertificate: clientCertSecret, TimeoutPolicy: ctp, - SlowStartConfig: slowStartConfig, + SlowStartConfig: slowStart, } if service.Mirror && r.MirrorPolicy != nil { validCond.AddError(contour_api_v1.ConditionTypeServiceError, "OnlyOneMirror", diff --git a/internal/dag/status_test.go b/internal/dag/status_test.go index f8e5c7eb284..684d0d1de4c 100644 --- a/internal/dag/status_test.go +++ b/internal/dag/status_test.go @@ -3601,7 +3601,7 @@ func TestDAGStatus(t *testing.T) { }, }) - // proxyWithInvalidSlowStart is invalid because it has invalid window size syntax + // proxyWithInvalidSlowStartWindow is invalid because it has invalid window size syntax. proxyWithInvalidSlowStartWindow := &contour_api_v1.HTTPProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: "roots", @@ -3623,11 +3623,11 @@ func TestDAGStatus(t *testing.T) { }, } - // proxyWithInvalidSlowStart is invalid because it has invalid aggression syntax + // proxyWithInvalidSlowStartAggression is invalid because it has invalid aggression syntax. proxyWithInvalidSlowStartAggression := &contour_api_v1.HTTPProxy{ ObjectMeta: metav1.ObjectMeta{ Namespace: "roots", - Name: "slow-start-invalid-window", + Name: "slow-start-invalid-aggression", }, Spec: contour_api_v1.HTTPProxySpec{ VirtualHost: &contour_api_v1.VirtualHost{ @@ -3646,6 +3646,31 @@ func TestDAGStatus(t *testing.T) { }, } + // proxyWithInvalidSlowStartLBStrategy is invalid because route has LB strategy that does not support slow start. + proxyWithInvalidSlowStartLBStrategy := &contour_api_v1.HTTPProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "roots", + Name: "slow-start-invalid-lb-strategy", + }, + Spec: contour_api_v1.HTTPProxySpec{ + VirtualHost: &contour_api_v1.VirtualHost{ + Fqdn: "www.example.com", + }, + Routes: []contour_api_v1.Route{{ + LoadBalancerPolicy: &contour_api_v1.LoadBalancerPolicy{ + Strategy: LoadBalancerPolicyCookie, + }, + Services: []contour_api_v1.Service{{ + Name: "home", + Port: 8080, + SlowStartPolicy: &contour_api_v1.SlowStartPolicy{ + Window: "5s", + }, + }}, + }}, + }, + } + run(t, "Slow start with invalid window syntax", testcase{ objs: []interface{}{ proxyWithInvalidSlowStartWindow, @@ -3667,7 +3692,7 @@ func TestDAGStatus(t *testing.T) { fixture.ServiceRootsHome, }, want: map[types.NamespacedName]contour_api_v1.DetailedCondition{ - k8s.NamespacedNameOf(proxyWithInvalidSlowStartWindow): fixture.NewValidCondition(). + k8s.NamespacedNameOf(proxyWithInvalidSlowStartAggression): fixture.NewValidCondition(). WithError( contour_api_v1.ConditionTypeServiceError, "SlowStartInvalid", @@ -3675,6 +3700,21 @@ func TestDAGStatus(t *testing.T) { ), }, }) + + run(t, "Slow start with load balancer strategy that does not support slow start", testcase{ + objs: []interface{}{ + proxyWithInvalidSlowStartLBStrategy, + fixture.ServiceRootsHome, + }, + want: map[types.NamespacedName]contour_api_v1.DetailedCondition{ + k8s.NamespacedNameOf(proxyWithInvalidSlowStartLBStrategy): fixture.NewValidCondition(). + WithError( + contour_api_v1.ConditionTypeServiceError, + "SlowStartInvalid", + "slow start is only supported with RoundRobin or WeightedLeastRequest load balancer strategy", + ), + }, + }) } func validGatewayStatusUpdate(listenerName string, kind gatewayapi_v1beta1.Kind, attachedRoutes int) []*status.GatewayStatusUpdate { diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index 115f4f4caf2..bb9077bdd8f 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -3336,7 +3336,7 @@

SlowStartPolicy

SlowStartPolicy will gradually increase amount of traffic to a newly added endpoint. -It can be used only with RoundRobin and WeightedLeastRequest load balancing strategies, otherwise the configuration is ignored.

+It can be used only with RoundRobin and WeightedLeastRequest load balancing strategies.

(Optional) -

The minimum percentage of origin weight that avoids too small new weight, which may cause endpoints in slow start mode receive no traffic in slow start window. +

The minimum or starting percentage of traffic to send to new endpoints. +A non-zero value helps avoid a too small initial weight, which may cause endpoints in slow start mode to receive no traffic in the beginning of the slow start window. If not specified, the default is 10%.

From 5d1106121387600259d596bf510f0f9dfd729dd9 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Wed, 5 Oct 2022 22:34:22 +0300 Subject: [PATCH 4/6] fixed slowstart doc after slowStartPolicy rename Signed-off-by: Tero Saarni --- site/content/docs/main/config/slow-start.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/config/slow-start.md b/site/content/docs/main/config/slow-start.md index 8fc68368298..eaf6d22a7a2 100644 --- a/site/content/docs/main/config/slow-start.md +++ b/site/content/docs/main/config/slow-start.md @@ -25,9 +25,9 @@ spec: - services: - name: java-app port: 80 - slowStart: + slowStartPolicy: window: 3s - aggression: 1.0 + aggression: "1.0" minWeightPercent: 10 ``` From 1f03f71d08e5abcc5245584b988fcbd98cc0f022 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Wed, 5 Oct 2022 22:38:36 +0300 Subject: [PATCH 5/6] removed condition that is not needed anymore Signed-off-by: Tero Saarni --- internal/dag/httpproxy_processor.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 238eca22a7c..06a1c9612bd 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -1451,11 +1451,6 @@ func directResponsePolicy(direct *contour_api_v1.HTTPDirectResponsePolicy) *Dire } func slowStartConfig(slowStart *contour_api_v1.SlowStartPolicy) (*SlowStartConfig, error) { - // If slow start is not configured, return nil. - if slowStart == nil { - return nil, nil - } - window, err := time.ParseDuration(slowStart.Window) if err != nil { return nil, fmt.Errorf("error parsing window: %s", err) From 24ae5e26283bc01600e4142e065e43d96cd4077a Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Thu, 6 Oct 2022 09:37:24 +0300 Subject: [PATCH 6/6] doc clarification according to review comment Signed-off-by: Tero Saarni --- site/content/docs/main/config/slow-start.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/content/docs/main/config/slow-start.md b/site/content/docs/main/config/slow-start.md index eaf6d22a7a2..b44cc18fdc3 100644 --- a/site/content/docs/main/config/slow-start.md +++ b/site/content/docs/main/config/slow-start.md @@ -9,7 +9,8 @@ Optional field `minWeightPercent` can be set to change the minimum percent of ta It is used to avoid too small new weight, which may cause endpoint to receive no traffic in beginning of the slow start window. Slow start mode can be useful for example with JVM based applications, that might otherwise get overwhelmed during JIT warm-up period. -Application might perform worse immediately after pod start or after container restarts. +Such applications may respond to requests slowly or return errors immediately after pod start or after container restarts. +User impact of this behavior can be mitigated by using slow start configuration to gradually increase traffic to recently started service endpoints. The following example configures slow start mode for a service: