@@ -16,6 +16,7 @@ import (
1616 "path/filepath"
1717 "strconv"
1818 "strings"
19+ "time"
1920
2021 "github.com/sirupsen/logrus"
2122
@@ -38,6 +39,7 @@ type APIServer struct {
3839 Storage manager.Component
3940 EnableKonnectivity bool
4041 DisableEndpointReconciler bool
42+ StopTimeout time.Duration
4143
4244 supervisor * supervisor.Supervisor
4345 executablePath string
@@ -154,19 +156,59 @@ func (a *APIServer) Start(ctx context.Context) error {
154156 args ["endpoint-reconciler-type" ] = "none"
155157 }
156158
159+ stopTimeout := a .StopTimeout
160+
161+ // If the timeout hasn't been specified, do a
162+ // best guess based on the API server flags.
163+ if stopTimeout <= 0 {
164+ requestTimeout := 1 * time .Minute
165+ if value , ok := args ["request-timeout" ]; ok {
166+ if parsed , err := time .ParseDuration (value ); err == nil {
167+ requestTimeout = parsed
168+ }
169+ }
170+
171+ watchTerminationGrace := 0 * time .Second
172+ if value , ok := args ["shutdown-watch-termination-grace-period" ]; ok {
173+ if parsed , err := time .ParseDuration (value ); err == nil {
174+ watchTerminationGrace = parsed
175+ }
176+ }
177+
178+ stopTimeout = max (requestTimeout , watchTerminationGrace ) + (2 * time .Second )
179+
180+ // Clamp the timeout between 5 and 20 seconds. We can't wait for too long
181+ // currently because the init system will likely kill the process otherwise.
182+ stopTimeout = max (5 * time .Second , min (stopTimeout , 20 * time .Second ))
183+ }
184+
185+ // Enable the API server's watch-drain facility on shutdown, if that flag
186+ // hasn't been specified by the user. Without this flag, the API server will
187+ // almost always encounter the request timeout if anything is connected to
188+ // it via client-go watches. These have a timeout of between five and ten
189+ // minutes. Note that other types of long-running requests, such as log
190+ // streams, can still prevent a timely shutdown. However, there's not much
191+ // that can be done about them apart from setting a short request timeout.
192+ if _ , ok := args ["shutdown-watch-termination-grace-period" ]; ! ok {
193+ if gracePeriod := stopTimeout - 2 * time .Second ; gracePeriod > 0 {
194+ args ["shutdown-watch-termination-grace-period" ] = gracePeriod .String ()
195+ }
196+ }
197+
157198 var apiServerArgs []string
158199 for name , value := range args {
159200 apiServerArgs = append (apiServerArgs , fmt .Sprintf ("--%s=%s" , name , value ))
160201 }
161202 apiServerArgs = append (apiServerArgs , a .ClusterConfig .Spec .API .RawArgs ... )
162203
163204 a .supervisor = & supervisor.Supervisor {
164- Name : kubeAPIComponentName ,
165- BinPath : a .executablePath ,
166- RunDir : a .K0sVars .RunDir ,
167- DataDir : a .K0sVars .DataDir ,
168- Args : apiServerArgs ,
169- UID : a .uid ,
205+ Name : kubeAPIComponentName ,
206+ BinPath : a .executablePath ,
207+ RunDir : a .K0sVars .RunDir ,
208+ DataDir : a .K0sVars .DataDir ,
209+ Args : apiServerArgs ,
210+ UID : a .uid ,
211+ TimeoutStop : stopTimeout ,
170212 }
171213
172214 etcdArgs , err := getEtcdArgs (a .ClusterConfig .Spec .Storage , a .K0sVars )
0 commit comments