‼️ This project is no longer maintained. There are better ways to gracefully upgrade Go HTTP servers nowadays.
alternate
is a simple CLI tool for running a command with alternating parameters. Used together with a reverse proxy, it makes zero-downtime upgrade of web servers easy.
With Go installed, and GOPATH/bin
added to your PATH
:
$ go get github.com/peferron/alternate
Then verify that alternate
is installed:
$ alternate
$ alternate <command> <parameters...> <overlap>
-
command
is the command to run, with the substring%alt
acting as placeholder for the rotated parameter. -
parameters...
is a space-separated list of parameters to rotate through after receiving a USR1 signal. -
overlap
is the delay between starting the next command, and sending a TERM signal to the previous command.
To run /home/me/myserver
alternatively on ports 3000 and 3001, with 15 seconds of overlap:
$ alternate "/home/me/myserver 127.0.0.1:%alt" 3000 3001 15s
-
alternate
picks the first parameter3000
to execute the command/home/me/myserver 127.0.0.1:3000
. Thenalternate
waits for a USR1 signal. -
When a USR1 signal is received,
alternate
picks the second parameter3001
to execute the second command/home/me/myserver 127.0.0.1:3001
. Thenalternate
waits 15 seconds. -
When the 15 seconds are over, if the command from step 2 is still running,
alternate
sends a TERM signal to the command from step 1. Thenalternate
waits for a USR1 signal. -
When a USR1 signal is received,
alternate
loops back to the first parameter3000
to execute the command/home/me/myserver 127.0.0.1:3000
. Thenalternate
waits 15 seconds. -
When the 15 seconds are over, if the command from step 4 is still running,
alternate
sends a TERM signal to the command from step 3. Thenalternate
waits for another USR1 signal. -
And so on.
Steps for running an API server (serving JSON for example) with zero-downtime upgrades:
-
Install a reverse proxy, if you don't have one already. The cool thing with using a reverse proxy is that it also lets you configure static file serving, caching headers and so on very easily, without having to bake all this functionality into your API server. This setup uses nginx as reverse proxy, but Apache, HAProxy and many others would do the job as well.
-
Edit
nginx.conf
to run nginx as a reverse proxy to your API server:upstream myserver { server 127.0.0.1:3000 max_fails=1 fail_timeout=5s; server 127.0.0.1:3001 max_fails=1 fail_timeout=5s; keepalive 1; } server { listen 80; location /api { proxy_pass http://myserver; proxy_http_version 1.1; proxy_set_header Connection ""; } }
-
Make sure your API server stops gracefully. Stopping gracefully means that when your API server receives a TERM signal, it should close the listening socket, then finishing processing all active requests before exiting. Go servers can achieve this very easily using the graceful package.
-
Start your API server by running this command:
$ alternate "/home/me/myserver 127.0.0.1:%alt" 3000 3001 15s
supervisor is a great tool to automatically start this command and automatically restart it after a crash.
-
Work on your API server!
-
Overwrite
/home/me/myserver
with the latest and greatest version of your API server. -
Send a USR1 signal to
alternate
:$ pkill -USR1 -f alternate
-
Done! The old and new versions of your API server will run concurrently for 15s, then the new version will take over completely, all without a hitch. Next time you want to update to a newer version, simply repeat steps 6 and 7.