- July 24th, 2017: be sure to check the new
cookie-selector
branch, featuring an improved solution
I wanted to demo a simple way to enable deploying different versions
of the same app, on the same host/cluster, via docker-compose
.
This is pretty useful when you need to "test in production", or even better, when you want to run both canary and stable channels.
This repo contains a pretty basic Express app (with just one method), running behind an Nging reverse proxy, described in a docker-compose.yml
file.
By running some scripts, you can run two versions side by side, and make changes to them independently.
Being a simple demo, you will actually need to hit two different HTTP ports in order to reach either of two versions. In more advanced scenarios, like canary and stable channels, will need to make this "selection" transparent for the end user.
One easy way to achieve that is to put an additional reverse proxy in front of both versions, and let it re-route users based on some custom logic. For instance, you might assign canary users an additional cookie during the login phase and then they will hit the canary version of the app when Nginx filters and proxies their requests.
Additionaly, this a stateless app, meaning it doesn't manage or access any data, like databases. If you need to account for those as well (like always in the real world), there are some challenges you need to be aware of: first, you surely can add more images and containers for them, but those will not include all the data the app will likely need to run. Sharing the same data is NOT an option, so you will probably need a way to set it up, like performing a backup and restore script (if are leveraging MongoDB or SQL Server, it is pretty straightforward).
Finally, once again, this is a basic, simple demo, so it has a lot of room for improvement by design. You are looking for a much more complete, and complex, example app, you should watch this repo https://github.com/dotnet-architecture/eShopOnContainers.
For you machine:
- Docker (tested with v17.06)
I build this demo on Windows 10 v1703, but it should on Linux and Mac as well - just
notice the two scripts might have to be edited/renamed to work in bash
/sh
.
From a "developer" perspective, you should have a fair understading of:
- Containers
- Basic Docker usage (ex.
dockerfile
) - NodeJS and Express (just for the app)
- What a reverse proxy is
- nginx knowledge isn't really relevant, on the other hand
-
Open a shell prompt (on Windows, either
cmd.exe
orpowershell
), so you do not miss any output in case of issues. -
Start
RunStable.cmd
-
Open your browser and navigate to http://localhost:9001. If everything's OK, you will get a
200 OK
response with some JSON. -
Now start
RunCanary.cmd
, and hit http://localhost:9002. The two responses will match, since the app is exactly the same at this point. -
Let's now make a change to the app and release it to the test channel. Open the
app/index.js
file and change theVERSION
constant to"1.0.1"
(or whatever you prefer). -
Now start
RunCanary.cmd
and hit again http://localhost:9002 -
New version should be reported in the output.
-
Finally, hit again http://localhost:9001 and see that response is actually the original one.
- Run
docker-compose -p myapp down
anddocker-compose -p myapp_test down
First of all, we have our regular docker-compose.yml
file. However, if you read it carefully, you'll notice an important section is missing: nginx
service has is ports
section missing.
That is actually found in the other two files, docker-compose.stable.yml
and docker-compose.canary.yml
, that override the default configuration by setting each a different mapping.
However, this is not enough to achieve our goal, since Docker has still no way to distinguish two versions of the same images.
Fortunately, there's another option you can supply to docker-compose
, the --project-name
(short -p
) argument. If you take a look at both scripts, you'll notice that we're using two different values, myapp
in production, and myapp_canary
otherwise.
Indeed, this makes Docker deploy two different apps in practice. So, the two commands are simply:
docker-compose -p myapp -f docker-compose.yml -f docker-compose.stable.yml up -d --build
docker-compose -p myapp_test -f docker-compose.yml -f docker-compose.canary.yml up -d --build
-
Docker Docs - Share Compose configurations between files and projects
-
Docker Docs - Using Compose in production