Merge pull request #51415 from jpbetz/timeout-param-minimal

Automatic merge from submit-queue (batch tested with PRs 50932, 49610, 51312, 51415, 50705)

Add --request-timeout to kube-apiserver to make global request timeout configurable

**What this PR does / why we need it**:

Make the currently hard coded 60 global request timeout in apiserver configurable via a --request-timeout command line flag.

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

https://github.com/kubernetes/kubernetes/issues/51355

**Special notes for your reviewer**:

We plan to backport this to 1.6 and 1.7. To ease the backport work, we've kept this change to a minimum. After this PR we will submit an additional PR to rename `--min-request-timeout` to something that more clearly describes it's purpose, such as `--long-running-request-timeout-min`.

**Release note**:

```release-note
Add --request-timeout to kube-apiserver to make global request timeout configurable.
```
pull/6/head
Kubernetes Submit Queue 2017-08-29 01:17:40 -07:00 committed by GitHub
commit 0b0b2a22c5
5 changed files with 29 additions and 12 deletions

View File

@ -81,6 +81,7 @@ func TestAddFlags(t *testing.T) {
"--kubelet-certificate-authority=/var/run/kubernetes/caserver.crt", "--kubelet-certificate-authority=/var/run/kubernetes/caserver.crt",
"--proxy-client-cert-file=/var/run/kubernetes/proxy.crt", "--proxy-client-cert-file=/var/run/kubernetes/proxy.crt",
"--proxy-client-key-file=/var/run/kubernetes/proxy.key", "--proxy-client-key-file=/var/run/kubernetes/proxy.key",
"--request-timeout=2m",
"--storage-backend=etcd2", "--storage-backend=etcd2",
} }
f.Parse(args) f.Parse(args)
@ -95,6 +96,7 @@ func TestAddFlags(t *testing.T) {
CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"}, CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"},
MaxRequestsInFlight: 400, MaxRequestsInFlight: 400,
MaxMutatingRequestsInFlight: 200, MaxMutatingRequestsInFlight: 200,
RequestTimeout: time.Duration(2) * time.Minute,
MinRequestTimeout: 1800, MinRequestTimeout: 1800,
}, },
Admission: &apiserveroptions.AdmissionOptions{ Admission: &apiserveroptions.AdmissionOptions{

View File

@ -46,7 +46,7 @@ func BuildInsecureHandlerChain(apiHandler http.Handler, c *server.Config) http.H
handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, insecureSuperuser{}, nil) handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, insecureSuperuser{}, nil)
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true") handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
handler = genericfilters.WithPanicRecovery(handler) handler = genericfilters.WithPanicRecovery(handler)
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc) handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc, c.RequestTimeout)
handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc) handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc)
handler = genericapifilters.WithRequestInfo(handler, server.NewRequestInfoResolver(c), c.RequestContextMapper) handler = genericapifilters.WithRequestInfo(handler, server.NewRequestInfoResolver(c), c.RequestContextMapper)
handler = apirequest.WithRequestContext(handler, c.RequestContextMapper) handler = apirequest.WithRequestContext(handler, c.RequestContextMapper)

View File

@ -148,8 +148,11 @@ type Config struct {
// RESTOptionsGetter is used to construct RESTStorage types via the generic registry. // RESTOptionsGetter is used to construct RESTStorage types via the generic registry.
RESTOptionsGetter genericregistry.RESTOptionsGetter RESTOptionsGetter genericregistry.RESTOptionsGetter
// If specified, requests will be allocated a random timeout between this value, and twice this value. // If specified, all requests except those which match the LongRunningFunc predicate will timeout
// Note that it is up to the request handlers to ignore or honor this timeout. In seconds. // after this duration.
RequestTimeout time.Duration
// If specified, long running requests such as watch will be allocated a random timeout between this value, and
// twice this value. Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
MinRequestTimeout int MinRequestTimeout int
// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further // MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
// request has to wait. Applies only to non-mutating requests. // request has to wait. Applies only to non-mutating requests.
@ -222,6 +225,7 @@ func NewConfig(codecs serializer.CodecFactory) *Config {
EnableProfiling: true, EnableProfiling: true,
MaxRequestsInFlight: 400, MaxRequestsInFlight: 400,
MaxMutatingRequestsInFlight: 200, MaxMutatingRequestsInFlight: 200,
RequestTimeout: time.Duration(60) * time.Second,
MinRequestTimeout: 1800, MinRequestTimeout: 1800,
EnableAPIResponseCompression: utilfeature.DefaultFeatureGate.Enabled(features.APIResponseCompression), EnableAPIResponseCompression: utilfeature.DefaultFeatureGate.Enabled(features.APIResponseCompression),
@ -477,7 +481,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
} }
handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.RequestContextMapper, c.Serializer, c.SupportsBasicAuth)) handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.RequestContextMapper, c.Serializer, c.SupportsBasicAuth))
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true") handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc) handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc, c.RequestTimeout)
handler = genericapifilters.WithRequestInfo(handler, NewRequestInfoResolver(c), c.RequestContextMapper) handler = genericapifilters.WithRequestInfo(handler, NewRequestInfoResolver(c), c.RequestContextMapper)
handler = apirequest.WithRequestContext(handler, c.RequestContextMapper) handler = apirequest.WithRequestContext(handler, c.RequestContextMapper)
handler = genericfilters.WithPanicRecovery(handler) handler = genericfilters.WithPanicRecovery(handler)

View File

@ -31,12 +31,10 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request" apirequest "k8s.io/apiserver/pkg/endpoints/request"
) )
const globalTimeout = time.Minute
var errConnKilled = fmt.Errorf("kill connection/stream") var errConnKilled = fmt.Errorf("kill connection/stream")
// WithTimeoutForNonLongRunningRequests times out non-long-running requests after the time given by globalTimeout. // WithTimeoutForNonLongRunningRequests times out non-long-running requests after the time given by timeout.
func WithTimeoutForNonLongRunningRequests(handler http.Handler, requestContextMapper apirequest.RequestContextMapper, longRunning apirequest.LongRunningRequestCheck) http.Handler { func WithTimeoutForNonLongRunningRequests(handler http.Handler, requestContextMapper apirequest.RequestContextMapper, longRunning apirequest.LongRunningRequestCheck, timeout time.Duration) http.Handler {
if longRunning == nil { if longRunning == nil {
return handler return handler
} }
@ -45,13 +43,13 @@ func WithTimeoutForNonLongRunningRequests(handler http.Handler, requestContextMa
ctx, ok := requestContextMapper.Get(req) ctx, ok := requestContextMapper.Get(req)
if !ok { if !ok {
// if this happens, the handler chain isn't setup correctly because there is no context mapper // if this happens, the handler chain isn't setup correctly because there is no context mapper
return time.After(globalTimeout), func() {}, apierrors.NewInternalError(fmt.Errorf("no context found for request during timeout")) return time.After(timeout), func() {}, apierrors.NewInternalError(fmt.Errorf("no context found for request during timeout"))
} }
requestInfo, ok := apirequest.RequestInfoFrom(ctx) requestInfo, ok := apirequest.RequestInfoFrom(ctx)
if !ok { if !ok {
// if this happens, the handler chain isn't setup correctly because there is no request info // if this happens, the handler chain isn't setup correctly because there is no request info
return time.After(globalTimeout), func() {}, apierrors.NewInternalError(fmt.Errorf("no request info found for request during timeout")) return time.After(timeout), func() {}, apierrors.NewInternalError(fmt.Errorf("no request info found for request during timeout"))
} }
if longRunning(req, requestInfo) { if longRunning(req, requestInfo) {
@ -69,7 +67,7 @@ func WithTimeoutForNonLongRunningRequests(handler http.Handler, requestContextMa
metrics.MonitorRequest(req, strings.ToUpper(requestInfo.Verb), "", requestInfo.Path, "", scope, http.StatusGatewayTimeout, 0, now) metrics.MonitorRequest(req, strings.ToUpper(requestInfo.Verb), "", requestInfo.Path, "", scope, http.StatusGatewayTimeout, 0, now)
} }
} }
return time.After(globalTimeout), metricFn, apierrors.NewTimeoutError(fmt.Sprintf("request did not complete within %s", globalTimeout), 0) return time.After(timeout), metricFn, apierrors.NewTimeoutError(fmt.Sprintf("request did not complete within %s", timeout), 0)
} }
return WithTimeout(handler, timeoutFunc) return WithTimeout(handler, timeoutFunc)
} }

View File

@ -19,6 +19,7 @@ package options
import ( import (
"fmt" "fmt"
"net" "net"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
@ -39,6 +40,7 @@ type ServerRunOptions struct {
ExternalHost string ExternalHost string
MaxRequestsInFlight int MaxRequestsInFlight int
MaxMutatingRequestsInFlight int MaxMutatingRequestsInFlight int
RequestTimeout time.Duration
MinRequestTimeout int MinRequestTimeout int
TargetRAMMB int TargetRAMMB int
WatchCacheSizes []string WatchCacheSizes []string
@ -49,6 +51,7 @@ func NewServerRunOptions() *ServerRunOptions {
return &ServerRunOptions{ return &ServerRunOptions{
MaxRequestsInFlight: defaults.MaxRequestsInFlight, MaxRequestsInFlight: defaults.MaxRequestsInFlight,
MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight, MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
RequestTimeout: defaults.RequestTimeout,
MinRequestTimeout: defaults.MinRequestTimeout, MinRequestTimeout: defaults.MinRequestTimeout,
} }
} }
@ -59,6 +62,7 @@ func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
c.ExternalAddress = s.ExternalHost c.ExternalAddress = s.ExternalHost
c.MaxRequestsInFlight = s.MaxRequestsInFlight c.MaxRequestsInFlight = s.MaxRequestsInFlight
c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
c.RequestTimeout = s.RequestTimeout
c.MinRequestTimeout = s.MinRequestTimeout c.MinRequestTimeout = s.MinRequestTimeout
c.PublicAddress = s.AdvertiseAddress c.PublicAddress = s.AdvertiseAddress
@ -93,7 +97,11 @@ func (s *ServerRunOptions) Validate() []error {
errors = append(errors, fmt.Errorf("--max-requests-inflight can not be negative value")) errors = append(errors, fmt.Errorf("--max-requests-inflight can not be negative value"))
} }
if s.MaxMutatingRequestsInFlight < 0 { if s.MaxMutatingRequestsInFlight < 0 {
errors = append(errors, fmt.Errorf("--min-request-timeout can not be negative value")) errors = append(errors, fmt.Errorf("--max-mutating-requests-inflight can not be negative value"))
}
if s.RequestTimeout.Nanoseconds() < 0 {
errors = append(errors, fmt.Errorf("--request-timeout can not be negative value"))
} }
return errors return errors
@ -132,6 +140,11 @@ func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
"The maximum number of mutating requests in flight at a given time. When the server exceeds this, "+ "The maximum number of mutating requests in flight at a given time. When the server exceeds this, "+
"it rejects requests. Zero for no limit.") "it rejects requests. Zero for no limit.")
fs.DurationVar(&s.RequestTimeout, "request-timeout", s.RequestTimeout, ""+
"An optional field indicating the duration a handler must keep a request open before timing "+
"it out. This is the default request timeout for requests but may be overridden by flags such as "+
"--min-request-timeout for specific types of requests.")
fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+ fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+
"An optional field indicating the minimum number of seconds a handler must keep "+ "An optional field indicating the minimum number of seconds a handler must keep "+
"a request open before timing it out. Currently only honored by the watch request "+ "a request open before timing it out. Currently only honored by the watch request "+