The core idea behind zero-downtime deployment is to have two parallel docker images running on a server with nginx port-fowarding between the two.
Here's a use-case: You've developed and tested a release candidate on local and development environments. You want to publish this release to production. You don't want to interrupt service to clients, and you want to ensure the service is healthy before you expose it.
The above code provides a wrapper to your app-- by default, this tool anticipates a triangulum-based app written in clojure-- which manages the aforementioned docker and nginx configuration/toggling.
This tool is still in progress and accepting PRs, especially in the areas of further agnostication with regards to the build tool of the wrapped app, support for SSL certification, and potential support for multi-hosting machines.
As yet, this tool best supports apps that are on production servers that do not host other apps, since part of the strategy is to restart nginx as part of deployment. We assume that restarting nginx will lead to less interruption than rebuilding an app and running its health checks.
the most important code in the above example is in the scripts/release-[blue | green].sh script and the docker-compose.yml file. It is here that the necessary adjustments to the nginx configuration are made. The rest of the code supports image generation for docker.
Slightly zoomed in, consider the following:
a) Your machine has port 8080 open and is exposed to the public over DNS.
b) Your machine has ports 8081 and 8082 open as well, but they are limited access to QA and developers.
c) You load your app into Docker so that it creates a version of your app on each of two different containers. You may even determine different starting branches for these containers.
d) Nginx forwards traffic to port 8080 to port 8081 by default. Consider this the 'green', or 'default healthy' release version of your app.
e) You develop a new release and deploy it to the image running port 8082. Port 8082 is inaccessible for a while for however long it takes your app to build, deploy, and pass health checks.
f) However, traffic to port 8080 is never interrupted because it forward to port 8081.
g) Once your new release has built and passed its health checks, you can now access it on port 8082.
h) Run the release-[blue | green].sh script to switch nginx port forwarding from 8081 to port 8082.
i) Nginx now forwards traffic to port 8080 to the new release of your app, on port 8082.
j) You may now repeat from step e, but this time building to 8081 while traffic to 8082 is uninterrupted.
The afore-linked code bears an example .env file that needs to be copied and edited:
APP_CMD: by default this starts a clojure app and should start most triangulum projects.
APP_PORT: the main port to which traffic is forwarded to 8081 and 8082; it's best to leave this to the default 8080 if able.
[BLUE | GREEN]_BRANCH: the name of the branch of your project to initiate the respective docker image. by default these are both 'main' but can be set to different branches. These values get overwritten to most recent releases anyway.
GIT_REPO: the slug from which the machine will import your project. this will get wrapped in the 'app' directory and not touched except to build onto the inactive server.
SERVER_NAME: largely a cosmetic variable, until SSL certification is finished.
SSL_CERT_FILE, SSL_KEY_FILE: these are placeholders for SSL support.