Skip to content

Commit 79a3195

Browse files
committed
Graceful shutdown for Nginx
1 parent d6efc29 commit 79a3195

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

controllers/nginx/pkg/cmd/controller/nginx.go

+50-2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func newNGINXController() ingress.Controller {
8888
isIPV6Enabled: isIPv6Enabled(),
8989
resolver: h,
9090
ports: &config.ListenPorts{},
91+
stopCh: make(chan struct{}),
9192
}
9293

9394
fcgiListener, err := net.Listen("unix", fastCGISocket)
@@ -161,19 +162,31 @@ type NGINXController struct {
161162

162163
isSSLPassthroughEnabled bool
163164

165+
isStopping bool
166+
164167
proxy *proxy
165168

166169
ports *config.ListenPorts
167170

168171
backendDefaults defaults.Backend
172+
stopCh chan struct{}
169173
}
170174

171175
// Start start a new NGINX master process running in foreground.
172176
func (n *NGINXController) Start() {
173177
glog.Info("starting NGINX process...")
178+
n.isStopping = false
174179

175180
done := make(chan error, 1)
176181
cmd := exec.Command(n.binary, "-c", cfgPath)
182+
183+
// put nginx in another process group to prevent it
184+
// to receive signals meant for the controller
185+
cmd.SysProcAttr = &syscall.SysProcAttr{
186+
Setpgid: true,
187+
Pgid: 0,
188+
}
189+
177190
n.start(cmd, done)
178191

179192
// if the nginx master process dies the workers continue to process requests,
@@ -202,8 +215,43 @@ NGINX master process died (%v): %v
202215
conn.Close()
203216
time.Sleep(1 * time.Second)
204217
}
205-
// start a new nginx master process
206-
n.start(cmd, done)
218+
// restart a new nginx master process if the controller
219+
// is not being stopped
220+
if n.isStopping {
221+
n.stopCh <- struct{}{}
222+
} else {
223+
n.start(cmd, done)
224+
}
225+
}
226+
}
227+
228+
// OnStop gracefully stops the NGINX master process.
229+
func (n *NGINXController) OnStop() error {
230+
glog.Info("stopping NGINX process...")
231+
n.isStopping = true
232+
233+
o, err := exec.Command(n.binary, "-s", "quit", "-c", cfgPath).CombinedOutput()
234+
if err != nil {
235+
glog.Errorf("%v\n%v", err, string(o))
236+
}
237+
238+
// TODO: If we keep the modified Controller interface
239+
// should this be moved as a generic timeout control for the backend to stop
240+
// on time ?
241+
timer := time.NewTimer(60 * time.Second)
242+
defer timer.Stop()
243+
244+
for {
245+
select {
246+
case <-n.stopCh:
247+
return nil
248+
case <-timer.C:
249+
glog.Error("Timeout while waiting for nginx to gracefully shutdown")
250+
o, err := exec.Command(n.binary, "-s", "stop", "-c", cfgPath).CombinedOutput()
251+
if err != nil {
252+
return fmt.Errorf("%v\n%v", err, string(o))
253+
}
254+
}
207255
}
208256
}
209257

core/pkg/ingress/controller/controller.go

+6
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,12 @@ func (ic GenericController) Stop() error {
13261326
ic.stopLock.Lock()
13271327
defer ic.stopLock.Unlock()
13281328

1329+
// Shutting down the underlying backend first
1330+
err := ic.cfg.Backend.OnStop()
1331+
if err != nil {
1332+
glog.Errorf("unexpected failure stopping the backend: \n%v", err)
1333+
}
1334+
13291335
// Only try draining the workqueue if we haven't already.
13301336
if !ic.syncQueue.IsShuttingDown() {
13311337
glog.Infof("shutting down controller queues")

core/pkg/ingress/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ type Controller interface {
7777
// The backend returns an error if was not possible to update the configuration.
7878
//
7979
OnUpdate(Configuration) error
80+
// OnStop callback invoked when the controller is being stopped
81+
OnStop() error
8082
// ConfigMap content of --configmap
8183
SetConfig(*api.ConfigMap)
8284
// SetListers allows the access of store listers present in the generic controller

0 commit comments

Comments
 (0)