TAGS: |

I Can Hardly Contain(erize) Myself!

John Capobianco

Happy New Year! Last year I wrote a series of blogs under the “Infrastructure as Software” banner exploring how to build a Django three-tiered application from pyATS that parsed network state data. Now that I’ve built a working Django application locally the challenge is to make it available to others.

README

After I had built Merlin Unchained, I wanted to make it available for others to build and run on their own, and as an open-source project. This proved much more difficult than writing an in-depth Installation Guide in the project’s README file. I know because my README was written for Windows WSL2 Ubuntu. This included how to set up WSL2, install Ubuntu, clone the repository, the Django installation instructions, and then a long list of packages the user needed. Oh, and the order of operations on how to build and migrate data models to PostgreSQL. Also, how to install and set up PostgreSQL. Do you get the idea here? There was a lot of friction involved for even the most seasoned Ubuntu user.

And what happened? My very first user opened an issue that one of their steps had failed.

But It Works On My Machine

Famous last words. But this was true in my case. I was confident my installation guide would work, so I asked the user what version of Ubuntu they were running. They were trying to use this code on CentOS! Doh! Time to add CentOS installation steps in the instructions, right? Wrong. After this first mishap I decided to containerize my solution with Docker.

A Docker Primer

Docker solves the problem described above and allows solution developers to package up their application into containers. What is a container? Containers are made up of images. Now this is important because I will use the terminology for my analogy and metaphor. Do you remember the CD-ROM? Or have you ever had to mount an ISO image? I like to compare a Docker container image to a CD-ROM-like construct. It is immutable like an .iso or CD-ROM and can be interactive with its own shell. Unlike a virtual machine that requires a hypervisor layer, containers can run on any Docker environment – on a Windows or Linux machine or even in the cloud. Our revised README is simply the Docker command to run the container. Windows also has the Docker Desktop, so users at home can run containers.

How To Build Your Container

Docker containers can be built from what’s known as a dockerfile. This dockerfile has its own Docker syntax and is used to specify things like the base operating system of the container, the packages, applications, and utilities installed in the container, and any files or folders to be moved into the container. Yes, you read that correctly: we actually specify the OS of the Docker container environment.

Here is the breakdown of Merlin Unchained’s dockerfile.

The first line we specify FROM and the Operating System and the version, in this case the latest version, the container image will use. You can only have one FROM line and this must be the first line of the dockerfile.

Arguments can be passed, such as this argument that sets the front end to be non-interactive. Environment variables can be set, for example our Django environment variables such as the username and email.

Docker containers have a very strong concept of caching layers inside an image. Keep this in mind to help the performance of builds.  Subsequent builds will use the cache if the image layer has not changed since the previous build.

Using this layering technique, the RUN command is added and compounded to perform our installations. As a general rule, it is a good idea to update the operating system and install any required low-level utilities like sudo.

Install Python3, pip for Python3, and the OpenSSH client (for pyATS SSH connectivity).

Install pyATS[full] and fix it.

Now we install Django and all of the related Django components.

The COPY command can be used to move local folders and files into the container.

Finally, we can use the CMD (or ENTRYPOINT, which is a little different) to run a command as the final step of the container. I have chosen to launch a bash script.

This bash script handles the Python3 commands, manage.py, to initialize, setup, and start the Django server.

We’re Just Getting Started!

As incredible as building our own stand-alone container is, and it really is as easy as the steps above, we can orchestrate our container with other containers! Using a docker-compose file our standalone application becomes one of many microservices all interacting with each other using exposed network ports and mounted volumes.

Here is the portion of the docker-compose for Merlin Unchained that includes the image path and the build context (where to find the dockerfile for this image).

Ports are exposed (8000 and 8001) and mapped from the container ports to the Docker host ports. This image depends_on many other container images (which we will specify their specifications later in this compose file). Finally, we setup some environment variables.

Let’s look at the PostgreSQL image which I’ve labeled db. We do not need to specific a dockerfile because we are using a public image (postgres) and because we want persistent storage of the data inside the database we setup a volume to mount the data on the Docker host. We expose and map the default PostgreSQL port of 5432.

And since we are using PostgreSQL why not include the PostgreSQL Admin container to manage it?

It is a Django application so let’s include the necessary components for scheduling tasks; Celery Beats, Redis, and Flower.

Prometheus can be included for performance metrics of the entire Django application.

Elastic Stack and the various utilities like ElasticVue, DejaVu, and Kibana can extend our solution even further.

Finally, Heimdall can be included to provide a single pane of glass to manage and access all of these amazing services.

The Revised README

After converting to a Docker container my new README is a single line command:

docker-compose up -d merlin_unchained

Which builds the Merlin Unchained container, all ancillary containers, and starts the Django applications. All users need to do then is launch their browser and start using the application.

I hope this demystifies Docker, containers, and images and demonstrates how local solutions can easily be packaged as full-blown portable applications.

About John Capobianco: My name is John Capobianco, a 20-year IT professional who has fallen in love with automation, including network automation. I like to help other IT professionals learn how to modernize their approach to problem-solving with an automated DevOps infrastructure as code approach.