Graceful shutdown for Nginx
This commit is contained in:
parent
d6efc293c4
commit
79a3195c2f
3 changed files with 58 additions and 2 deletions
|
@ -88,6 +88,7 @@ func newNGINXController() ingress.Controller {
|
||||||
isIPV6Enabled: isIPv6Enabled(),
|
isIPV6Enabled: isIPv6Enabled(),
|
||||||
resolver: h,
|
resolver: h,
|
||||||
ports: &config.ListenPorts{},
|
ports: &config.ListenPorts{},
|
||||||
|
stopCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
fcgiListener, err := net.Listen("unix", fastCGISocket)
|
fcgiListener, err := net.Listen("unix", fastCGISocket)
|
||||||
|
@ -161,19 +162,31 @@ type NGINXController struct {
|
||||||
|
|
||||||
isSSLPassthroughEnabled bool
|
isSSLPassthroughEnabled bool
|
||||||
|
|
||||||
|
isStopping bool
|
||||||
|
|
||||||
proxy *proxy
|
proxy *proxy
|
||||||
|
|
||||||
ports *config.ListenPorts
|
ports *config.ListenPorts
|
||||||
|
|
||||||
backendDefaults defaults.Backend
|
backendDefaults defaults.Backend
|
||||||
|
stopCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start start a new NGINX master process running in foreground.
|
// Start start a new NGINX master process running in foreground.
|
||||||
func (n *NGINXController) Start() {
|
func (n *NGINXController) Start() {
|
||||||
glog.Info("starting NGINX process...")
|
glog.Info("starting NGINX process...")
|
||||||
|
n.isStopping = false
|
||||||
|
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
cmd := exec.Command(n.binary, "-c", cfgPath)
|
cmd := exec.Command(n.binary, "-c", cfgPath)
|
||||||
|
|
||||||
|
// put nginx in another process group to prevent it
|
||||||
|
// to receive signals meant for the controller
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setpgid: true,
|
||||||
|
Pgid: 0,
|
||||||
|
}
|
||||||
|
|
||||||
n.start(cmd, done)
|
n.start(cmd, done)
|
||||||
|
|
||||||
// if the nginx master process dies the workers continue to process requests,
|
// if the nginx master process dies the workers continue to process requests,
|
||||||
|
@ -202,8 +215,43 @@ NGINX master process died (%v): %v
|
||||||
conn.Close()
|
conn.Close()
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
// start a new nginx master process
|
// restart a new nginx master process if the controller
|
||||||
n.start(cmd, done)
|
// is not being stopped
|
||||||
|
if n.isStopping {
|
||||||
|
n.stopCh <- struct{}{}
|
||||||
|
} else {
|
||||||
|
n.start(cmd, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnStop gracefully stops the NGINX master process.
|
||||||
|
func (n *NGINXController) OnStop() error {
|
||||||
|
glog.Info("stopping NGINX process...")
|
||||||
|
n.isStopping = true
|
||||||
|
|
||||||
|
o, err := exec.Command(n.binary, "-s", "quit", "-c", cfgPath).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("%v\n%v", err, string(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: If we keep the modified Controller interface
|
||||||
|
// should this be moved as a generic timeout control for the backend to stop
|
||||||
|
// on time ?
|
||||||
|
timer := time.NewTimer(60 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-n.stopCh:
|
||||||
|
return nil
|
||||||
|
case <-timer.C:
|
||||||
|
glog.Error("Timeout while waiting for nginx to gracefully shutdown")
|
||||||
|
o, err := exec.Command(n.binary, "-s", "stop", "-c", cfgPath).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%v\n%v", err, string(o))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1326,6 +1326,12 @@ func (ic GenericController) Stop() error {
|
||||||
ic.stopLock.Lock()
|
ic.stopLock.Lock()
|
||||||
defer ic.stopLock.Unlock()
|
defer ic.stopLock.Unlock()
|
||||||
|
|
||||||
|
// Shutting down the underlying backend first
|
||||||
|
err := ic.cfg.Backend.OnStop()
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected failure stopping the backend: \n%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Only try draining the workqueue if we haven't already.
|
// Only try draining the workqueue if we haven't already.
|
||||||
if !ic.syncQueue.IsShuttingDown() {
|
if !ic.syncQueue.IsShuttingDown() {
|
||||||
glog.Infof("shutting down controller queues")
|
glog.Infof("shutting down controller queues")
|
||||||
|
|
|
@ -77,6 +77,8 @@ type Controller interface {
|
||||||
// The backend returns an error if was not possible to update the configuration.
|
// The backend returns an error if was not possible to update the configuration.
|
||||||
//
|
//
|
||||||
OnUpdate(Configuration) error
|
OnUpdate(Configuration) error
|
||||||
|
// OnStop callback invoked when the controller is being stopped
|
||||||
|
OnStop() error
|
||||||
// ConfigMap content of --configmap
|
// ConfigMap content of --configmap
|
||||||
SetConfig(*api.ConfigMap)
|
SetConfig(*api.ConfigMap)
|
||||||
// SetListers allows the access of store listers present in the generic controller
|
// SetListers allows the access of store listers present in the generic controller
|
||||||
|
|
Loading…
Reference in a new issue