Skip to content

Commit

Permalink
Graceful shutdown for Nginx
Browse files Browse the repository at this point in the history
  • Loading branch information
maxlaverse committed Aug 29, 2017
1 parent 3fd8abe commit 73d8ddd
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 14 deletions.
12 changes: 5 additions & 7 deletions controllers/nginx/pkg/cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,15 @@ import (
"time"

"github.com/golang/glog"
"k8s.io/ingress/core/pkg/ingress/controller"
)

func main() {
// start a new nginx controller
ngx := newNGINXController()
// create a custom Ingress controller using NGINX as backend
ic := controller.NewIngressController(ngx)
go handleSigterm(ic)

go handleSigterm(ngx)
// start the controller
ic.Start()
ngx.Start()
// wait
glog.Infof("shutting down Ingress controller...")
for {
Expand All @@ -42,14 +40,14 @@ func main() {
}
}

func handleSigterm(ic *controller.GenericController) {
func handleSigterm(ngx *NGINXController) {
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGTERM)
<-signalChan
glog.Infof("Received SIGTERM, shutting down")

exitCode := 0
if err := ic.Stop(); err != nil {
if err := ngx.Stop(); err != nil {
glog.Infof("Error during shutdown %v", err)
exitCode = 1
}
Expand Down
81 changes: 74 additions & 7 deletions controllers/nginx/pkg/cmd/controller/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"time"

"github.com/golang/glog"
"github.com/mitchellh/go-ps"
"github.com/spf13/pflag"

proxyproto "github.com/armon/go-proxyproto"
Expand All @@ -43,6 +44,7 @@ import (
ngx_template "k8s.io/ingress/controllers/nginx/pkg/template"
"k8s.io/ingress/controllers/nginx/pkg/version"
"k8s.io/ingress/core/pkg/ingress"
"k8s.io/ingress/core/pkg/ingress/controller"
"k8s.io/ingress/core/pkg/ingress/defaults"
"k8s.io/ingress/core/pkg/net/dns"
"k8s.io/ingress/core/pkg/net/ssl"
Expand Down Expand Up @@ -71,7 +73,7 @@ var (
// newNGINXController creates a new NGINX Ingress controller.
// If the environment variable NGINX_BINARY exists it will be used
// as source for nginx commands
func newNGINXController() ingress.Controller {
func newNGINXController() *NGINXController {
ngx := os.Getenv("NGINX_BINARY")
if ngx == "" {
ngx = binary
Expand Down Expand Up @@ -132,14 +134,13 @@ Error loading new template : %v

n.t = ngxTpl

go n.Start()

return ingress.Controller(n)
return n
}

// NGINXController ...
type NGINXController struct {
t *ngx_template.Template
controller *controller.GenericController
t *ngx_template.Template

configmap *api_v1.ConfigMap

Expand All @@ -161,6 +162,8 @@ type NGINXController struct {

isSSLPassthroughEnabled bool

isShuttingDown bool

proxy *proxy

ports *config.ListenPorts
Expand All @@ -170,10 +173,22 @@ type NGINXController struct {

// Start start a new NGINX master process running in foreground.
func (n *NGINXController) Start() {
glog.Info("starting NGINX process...")
n.isShuttingDown = false

n.controller = controller.NewIngressController(n)
go n.controller.Start()

done := make(chan error, 1)
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,
}

glog.Info("starting NGINX process...")
n.start(cmd, done)

// if the nginx master process dies the workers continue to process requests,
Expand All @@ -183,6 +198,11 @@ func (n *NGINXController) Start() {
// To avoid this issue we restart nginx in case of errors.
for {
err := <-done

if n.isShuttingDown {
break
}

if exitError, ok := err.(*exec.ExitError); ok {
waitStatus := exitError.Sys().(syscall.WaitStatus)
glog.Warningf(`
Expand All @@ -202,11 +222,34 @@ NGINX master process died (%v): %v
conn.Close()
time.Sleep(1 * time.Second)
}
// start a new nginx master process
// restart a new nginx master process if the controller
// is not being stopped
n.start(cmd, done)
}
}

// Stop gracefully stops the NGINX master process.
func (n *NGINXController) Stop() error {
n.isShuttingDown = true
n.controller.Stop()

// Send stop signal to Nginx
glog.Info("stopping NGINX process...")
cmd := exec.Command(n.binary, "-c", cfgPath, "-s", "quit")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
}

// Wait for the Nginx process disappear
waitForNginxShutdown()
glog.Info("NGINX process has stopped")

return nil
}

func (n *NGINXController) start(cmd *exec.Cmd, done chan error) {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
Expand Down Expand Up @@ -716,3 +759,27 @@ func isIPv6Enabled() bool {
cmd := exec.Command("test", "-f", "/proc/net/if_inet6")
return cmd.Run() == nil
}

// isNginxRunning returns true if a process with the name 'nginx' is found
func isNginxProcessPresent() bool {
processes, _ := ps.Processes()
for _, p := range processes {
if p.Executable() == "nginx" {
return true
}
}
return false
}

func waitForNginxShutdown() {
timer := time.NewTicker(time.Second * 1)
defer timer.Stop()
for {
select {
case <-timer.C:
if !isNginxProcessPresent() {
return
}
}
}
}

0 comments on commit 73d8ddd

Please sign in to comment.