When we began Dockerizing this blog for good, I began to look for ways to automate the build and deployment process. It turned out that although Docker is an excellent container for running applications, there is no standard way to update a server. Luckily with a moderately complex shell script it is indeed doable, and can be fully automated.
Our current architecture is made of several dependent Docker images and a Linux box as a production server. Luckily we have no dynamic data, but the deploy script can easily be modified to handle those too. In that case we would use the data only container approach.
So, let’s build our deployment script!
The basic idea of the build script is simple: Build the images, upload them to the server, then restart the containers with the new versions. These are the building blocks, just need some tricks to mix in order to be able to fully automate the process.
For the sake of example, let’s say we have an apache app in the apache/ subdirectory and a monitoring app residing in monitoring/.
Let’s name our script deploy.sh and add some bootstrapping:
The last variable will be the repository name for the Docker images. It is important to add one, as we will use it for the up-to-date check later.
The first thing to do is to build the images. It has nothing interesting, just a standard Docker build:
It builds the image to the predefined repository and adds a tag for easy retrieval.
Uploading to the remote server
Fortunately Docker can save and load an image to/from the standard input stream, so it makes piping possible. It effectively makes the whole uploading a one-liner.
What we should implement is to first check whether the image was actually modified or not. As Docker images tends to weight several hundred megabytes, it can save a lot of bandwidth, especially when there are several images and only a few of them are changing. The idea is that we can list the images for a given repository, then extract the image ID, then do the same on the remote machine, and if the IDs are the same, then the images are the same.
The finished part looks like this:
Updating the containers
The last step is to restart the containers using the new images. The good thing is that we can embed remote bash commands in our deploy script and treat them just like the local ones:
The first thing is to kill the current containers if they exist:
The || true is needed, because if the container does not exists, then docker rm gives back an error. Since we just want to have the containers killed, in case they did not exist in the first place, doing nothing is perfectly fine.
The second step is to start the containers, the standard way:
The final script
For a better overview, here is the complete script:
Docker is a fascinating container technology that allows building works-everywhere apps, and it comes with good CLI support. It currently lacks the features to make deploying easy, but with some scripting magic, we can work around these issues. I hope the above script gives some insight and possibly a solution to people facing the same problem.