k8s.io/apiserver@v0.31.1/pkg/server/lifecycle_signals.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package server 18 19 import ( 20 "sync" 21 ) 22 23 /* 24 We make an attempt here to identify the events that take place during 25 lifecycle of the apiserver. 26 27 We also identify each event with a name so we can refer to it. 28 29 Events: 30 - ShutdownInitiated: KILL signal received 31 - AfterShutdownDelayDuration: shutdown delay duration has passed 32 - InFlightRequestsDrained: all in flight request(s) have been drained 33 - HasBeenReady is signaled when the readyz endpoint succeeds for the first time 34 35 The following is a sequence of shutdown events that we expect to see with 36 'ShutdownSendRetryAfter' = false: 37 38 T0: ShutdownInitiated: KILL signal received 39 - /readyz starts returning red 40 - run pre shutdown hooks 41 42 T0+70s: AfterShutdownDelayDuration: shutdown delay duration has passed 43 - the default value of 'ShutdownDelayDuration' is '70s' 44 - it's time to initiate shutdown of the HTTP Server, server.Shutdown is invoked 45 - as a consequene, the Close function has is called for all listeners 46 - the HTTP Server stops listening immediately 47 - any new request arriving on a new TCP socket is denied with 48 a network error similar to 'connection refused' 49 - the HTTP Server waits gracefully for existing requests to complete 50 up to '60s' (dictated by ShutdownTimeout) 51 - active long running requests will receive a GOAWAY. 52 53 T0+70s: HTTPServerStoppedListening: 54 - this event is signaled when the HTTP Server has stopped listening 55 which is immediately after server.Shutdown has been invoked 56 57 T0 + 70s + up-to 60s: InFlightRequestsDrained: existing in flight requests have been drained 58 - long running requests are outside of this scope 59 - up-to 60s: the default value of 'ShutdownTimeout' is 60s, this means that 60 any request in flight has a hard timeout of 60s. 61 - it's time to call 'Shutdown' on the audit events since all 62 in flight request(s) have drained. 63 64 65 The following is a sequence of shutdown events that we expect to see with 66 'ShutdownSendRetryAfter' = true: 67 68 T0: ShutdownInitiated: KILL signal received 69 - /readyz starts returning red 70 - run pre shutdown hooks 71 72 T0+70s: AfterShutdownDelayDuration: shutdown delay duration has passed 73 - the default value of 'ShutdownDelayDuration' is '70s' 74 - the HTTP Server will continue to listen 75 - the apiserver is not accepting new request(s) 76 - it includes new request(s) on a new or an existing TCP connection 77 - new request(s) arriving after this point are replied with a 429 78 and the response headers: 'Retry-After: 1` and 'Connection: close' 79 - note: these new request(s) will not show up in audit logs 80 81 T0 + 70s + up to 60s: InFlightRequestsDrained: existing in flight requests have been drained 82 - long running requests are outside of this scope 83 - up to 60s: the default value of 'ShutdownTimeout' is 60s, this means that 84 any request in flight has a hard timeout of 60s. 85 - server.Shutdown is called, the HTTP Server stops listening immediately 86 - the HTTP Server waits gracefully for existing requests to complete 87 up to '2s' (it's hard coded right now) 88 */ 89 90 // lifecycleSignal encapsulates a named apiserver event 91 type lifecycleSignal interface { 92 // Signal signals the event, indicating that the event has occurred. 93 // Signal is idempotent, once signaled the event stays signaled and 94 // it immediately unblocks any goroutine waiting for this event. 95 Signal() 96 97 // Signaled returns a channel that is closed when the underlying event 98 // has been signaled. Successive calls to Signaled return the same value. 99 Signaled() <-chan struct{} 100 101 // Name returns the name of the signal, useful for logging. 102 Name() string 103 } 104 105 // lifecycleSignals provides an abstraction of the events that 106 // transpire during the lifecycle of the apiserver. This abstraction makes it easy 107 // for us to write unit tests that can verify expected graceful termination behavior. 108 // 109 // GenericAPIServer can use these to either: 110 // - signal that a particular termination event has transpired 111 // - wait for a designated termination event to transpire and do some action. 112 type lifecycleSignals struct { 113 // ShutdownInitiated event is signaled when an apiserver shutdown has been initiated. 114 // It is signaled when the `stopCh` provided by the main goroutine 115 // receives a KILL signal and is closed as a consequence. 116 ShutdownInitiated lifecycleSignal 117 118 // AfterShutdownDelayDuration event is signaled as soon as ShutdownDelayDuration 119 // has elapsed since the ShutdownInitiated event. 120 // ShutdownDelayDuration allows the apiserver to delay shutdown for some time. 121 AfterShutdownDelayDuration lifecycleSignal 122 123 // PreShutdownHooksStopped event is signaled when all registered 124 // preshutdown hook(s) have finished running. 125 PreShutdownHooksStopped lifecycleSignal 126 127 // NotAcceptingNewRequest event is signaled when the server is no 128 // longer accepting any new request, from this point on any new 129 // request will receive an error. 130 NotAcceptingNewRequest lifecycleSignal 131 132 // InFlightRequestsDrained event is signaled when the existing requests 133 // in flight have completed. This is used as signal to shut down the audit backends 134 InFlightRequestsDrained lifecycleSignal 135 136 // HTTPServerStoppedListening termination event is signaled when the 137 // HTTP Server has stopped listening to the underlying socket. 138 HTTPServerStoppedListening lifecycleSignal 139 140 // HasBeenReady is signaled when the readyz endpoint succeeds for the first time. 141 HasBeenReady lifecycleSignal 142 143 // MuxAndDiscoveryComplete is signaled when all known HTTP paths have been installed. 144 // It exists primarily to avoid returning a 404 response when a resource actually exists but we haven't installed the path to a handler. 145 // The actual logic is implemented by an APIServer using the generic server library. 146 MuxAndDiscoveryComplete lifecycleSignal 147 } 148 149 // ShuttingDown returns the lifecycle signal that is signaled when 150 // the server is not accepting any new requests. 151 // this is the lifecycle event that is exported to the request handler 152 // logic to indicate that the server is shutting down. 153 func (s lifecycleSignals) ShuttingDown() <-chan struct{} { 154 return s.NotAcceptingNewRequest.Signaled() 155 } 156 157 // newLifecycleSignals returns an instance of lifecycleSignals interface to be used 158 // to coordinate lifecycle of the apiserver 159 func newLifecycleSignals() lifecycleSignals { 160 return lifecycleSignals{ 161 ShutdownInitiated: newNamedChannelWrapper("ShutdownInitiated"), 162 AfterShutdownDelayDuration: newNamedChannelWrapper("AfterShutdownDelayDuration"), 163 PreShutdownHooksStopped: newNamedChannelWrapper("PreShutdownHooksStopped"), 164 NotAcceptingNewRequest: newNamedChannelWrapper("NotAcceptingNewRequest"), 165 InFlightRequestsDrained: newNamedChannelWrapper("InFlightRequestsDrained"), 166 HTTPServerStoppedListening: newNamedChannelWrapper("HTTPServerStoppedListening"), 167 HasBeenReady: newNamedChannelWrapper("HasBeenReady"), 168 MuxAndDiscoveryComplete: newNamedChannelWrapper("MuxAndDiscoveryComplete"), 169 } 170 } 171 172 func newNamedChannelWrapper(name string) lifecycleSignal { 173 return &namedChannelWrapper{ 174 name: name, 175 once: sync.Once{}, 176 ch: make(chan struct{}), 177 } 178 } 179 180 type namedChannelWrapper struct { 181 name string 182 once sync.Once 183 ch chan struct{} 184 } 185 186 func (e *namedChannelWrapper) Signal() { 187 e.once.Do(func() { 188 close(e.ch) 189 }) 190 } 191 192 func (e *namedChannelWrapper) Signaled() <-chan struct{} { 193 return e.ch 194 } 195 196 func (e *namedChannelWrapper) Name() string { 197 return e.name 198 }