Under the spotlight

Container management platforms: road to containerZENtion

Call To Light — Jul 2019 by Alexandre Coelho

The ascension of containers and how it all began, a developer’s tale from initial hurdle to the Zen of containers.

Why are containers so cool and why is the hype so big. Is it just a hype? Is it an old pattern revisited? Or is it really revolutionizing the industry?

Let’s recap a developer’s day-to-day tasks, aside from coding.

Coding is just part of a whole lot of things developers must perform before writing a line of code.

First, developers are given the programming language, install and setup the needed tools to fulfill the art of development.

Depending on the language this might be a daunting task, you may now need to setup Frameworks or products that will also be used to create the applications, set Environment variables, deal with Operating System requirements or incompatibilities, etc. This is no easy task and sometimes it takes well over a day of setup just to get started and open the IDE to start and discover something else is missing.

And this is just the work of one of the team members, now all others will go through the same process, and all the learning from the first installation (hopefully it was documented) might not be enough, because the new developer might have a different version of an SDK that is incompatible and will take another full day to troubleshoot or a different Operating System(OS) and the first guide won’t be helpful.

Hasn’t this happened to all developers?

For this long problem of wasted time, that some might refer to as “getting acquainted with the technology”, we call Virtual Machines (VMs) to the requeue. With a VM we can setup a full development with all the dependencies and share the VM image with the team.

But now we have at least a 50-60 GB image to share with team members, to start coding immediately, assuming the computer used is powerful enough to run the VM. After all, we are adding performance penalty to the overall, and this directly impacts developers productivity.

In the meantime, let’s hope remote team members have a good internet connection speed to download the image.

Fast forward, after all is up and running, we developers still feel like problems keep coming. Now we are developing in VM that is shared with all team members, probably a windows VM and then there are the real Test, SIT, UAT, etc. environments.

And these environments are different from the VMs and host machines that developers use, typically if not running a .Net Application (not .Net core) the server will most likely be running in a Linux machine.

And this is a major problem for development, that is a misalignment on the target environment and applications goes through its lifecycle. Ideally all environment stages should be similar, but it isn’t always possible.
Oh and in the meantime let’s hope we don’t require a formatted PC where we need to it do all over again.

Some more useful tools are being used to help with this, by using Vagrant, that makes it possible to setup a VM with all the needed dependencies via a configuration file.

But in the end, the problem is the same, it is still a full VM running with a hypervisor, and the performance penalty it comes with for machine resources.

This is a long-time problem and VMs help the development environment, but in the end developers still lack the ability to replicate the environment or even better to abstract from all stages an app must go through, until it gets to the final Production Environment.
Now we can easily separate development into two main concerns, first the ability to easily setup and share a development environment, and second the ability to emulate or abstract all environment stages.

How can developers be able to abstract all the infrastructure and just focus on the App dependencies and runtime?

Let’s focus on how to abstract applications from the environment stages, and at the same time provide a common ground that might even replace the need to have VM with the development environment.

Enter Containers.

Containers, containers, abstraction, abstraction…

The hype around abstraction has currently come with the promise that apps can run inside an even more abstract environment than a Virtual Machine itself.
Containers have a much simpler concept and are much more lightweight since it does not run a full OS kernel and hypervisor, but instead run inside the host operating system, being the host a VM or a bare metal installed OS.
This image provides a simple view of the difference between a VM and a container.

But what does all this mean? In order to provide some context let’s go trough some basic concepts of how it all works.

In sum what is a container.
Control Groups (cgroups) this control machine resources(cpu, memory, etc) for a given namespace/s represent a process isolation or sandbox and UnionFS that allows different folders to act as one(this is actually how container layers are created), all wrapped by an Application(such as Docker and containerD) with a magic touch, that is all controlled via an API or CLI commands.

Although all this became mainstream in 2013, containers themselves didn’t create innovation within development teams, other pieces of the puzzle must be in place for that to happen, such as processes and organization level changes, that we will not cover in this article.

How does this help?

Developers can now use containers to package their application or even migrate existing legacy systems. These containers run all of the system libs, system tools and code in an encapsulated and isolated environment to what we now call a container, guaranteeing that the container will run the same regardless of the environment running.

This means that we no longer have version conflicts within an OS, i.e.

Let’s imagine that we have an application running with JAVA 8 and now we want to run a new app in the same Tomcat, but this app can only run with JAVA 6. This was a major concern for app updates, with containers this issue is removed from the equation.

One important note on containers - everything that is built into a container image has an important concept, the image is immutable.

This is important because any changes to configurations within a running container, will be deleted and reset to original image state, if for some reason the image is restarted.

Of course, it is possible to overcome this by using what is called a volume, or simply put a folder that acts as a volume that is persistent inside the host OS. We don’t want any Database to lose all its data, if for some reason an image is restarted or deleted.

Now that we know some basics of what a container can be used for and the important fact that an image is immutable, we have addressed the second concern of how developers can abstract environment stages and be able to package an APP in a Container that runs the same in any environment.

Take the following as an important principle, one image build that passes all tests, should be considered final and stable.

What does this mean? It means specific builds for different environments are discouraged due to configuration error during build periods.

An image Build should be prepared from the initial build to go through all different environments/stages required (dev, SIT, UAT, PROD).

Containers provide the possibility to create portable applications that eliminate dependencies between applications, OS and infrastructure.

Now fast forward to 2019, it seems to be the year of microservices, where everyone is developing applications with a Service Oriented Architecture (SOA) using microservices principles.

For this, containers are the cherry on top, as it allows developers to create and deploy containers with a single microservice in each container (Note a container is meant to run a single process and not multiple Processes or apps).

Using tools like Docker, we can easily run an application in an in-premises or cloud environment without changes to the container.

But now we have an army of containers being created, either monoliths or microservices and this creates a new challenge, how to manage all of these images and their deployment? How to know if they are running, how to scale them up or down, load balance traffic, etc.

When developers first started to leverage on containers to overcome the hurdles of environments abstraction, organizations are still not using them for production due to the early stages of the technology, but now that proof of maturity was given by a number of high influencers in the market, organizations are heavily using and deploying them into production environments.

This brings us to the last issue of this article. How to manage this army on newly built Container images.

In this article, we will highlight Kubernetes as the container manager of election, but others exist such as docker swarm and Netflix Titus.

Now what is Kubernetes and why is it useful.

Kubernetes is an application to manage and orchestrate containers (Docker, Containerd), initially developed by Google, open source and promoted by the Cloud native computing foundation (CNCF).

Kubernetes is more and more the de-facto container management platform, being available in all major Cloud providers (AWS, GCP, Azure), as well as solutions for in-premises either with Openshift or installed in bare metal.

We can look at Kubernetes as an application platform that allows businesses and developers to create solution platforms. A platform to manage and leverage other application platforms.

Kubernetes manages, scales, performs rolling updates, manages traffic within Kubernetes PODS and controls traffic coming from the outside.

Really, we can see Kubernetes as a Software Defined Application platform, similar to what Software defined networks do for networking. Where everything can be extended and controlled by an API.

This brings a lot of power to developers, making it really easy to deploy, scale up or down an application and create abstract separations between Kubernetes areas by the use of namespaces, really bringing the rise of the DevOps culture.

Before container management systems where available this was a very hard task to perform and orchestrate.

Deployment, rollbacks, service discovery, scaling, networking, security, monitoring, etc. These are some of the problems that Kurbernetes solve and allowed containers to become the ideal tool for production.

I will try to simply and quickly describe how Kubernetes work at the basic level.

Kubernetes brings the concept of a cluster or group of machines that are seen as one. One of the machines within the cluster is elected to be the Master (the one that controls all others) and the other are called minions or nodes.

When deploying an application, a configuration file is sent to the master calling the deployment API with the configuration as the input parameter, then the master is responsible for creating the PODs (most basic unit of deployment in Kubernetes, typically one pod has 1 container, but some scenarios might require it to be multi-container or more than 1 container per POD).

Network is made easier since Kubernetes has its own network management and is mostly abstracted, so no need to know about IPs, all is referenced via metadata with names and labels.

Scalability is ensured by the replication Controller that can control a minimum and maximum number of running pods at any given moment in time. This means we can have multiple pods in different or the same node running at the same time.

To be able to discover all these running pods, Kubernetes resource named Service is used to load balance traffic to all running pods and act as a service registry, abstracting any caller to the fact of where and how many instances are running in the system.

Volumes are used for those PODs that need to persist information, remember, Containers are ephemeral. So, Pods can mount volumes like filesystems for persistency.

And to group all different pods that can represent a monolith, a business area, a dev environment, or any other grouping we need to isolate, Kubernetes provide the means to define namespaces, that act as a virtual fence with all other namespaces.

But how do we develop an app that takes all of this into consideration?

There is the cloud, there is Kubernetes, that can run in premises or on the cloud, then the developer might not be able to setup everything in the local development machine.
Taking some key takeaways from projects like Vodafone Germany, let’s provide some basic rules that will make App cloud ready and independent of the platform being used.

Rule 1:
Never develop for a specific platform or technology (docker, Kubernetes, AWS) unless strictly needed (example: don’t add Kubernetes dependencies if not required). A microservice or DB does not need to know if is running inside a VM, Container, Cloud, in-premises or inside Kubernetes.

Rule 2:
All configurations that change according to the environment/stage (SIT,DEV, UAT, etc) must be externalized to properties files and Environment Variables, containing values and references to those configurations. Example are Endpoints, integration username and passwords, certificates.

Rule 3:
Always provide a default value to all needed properties, one that if not set a default one is used, typically used in the development environment, by using rule 2 this is then overridden by the correct setup in the running stage.

Example :

Springboot application.properties

hostname=${<ENV-VAR>:<defafult value>}

Rule 4:
All properties and configurations should be created via scripts and versioned using a version control system. This can be within the project or a separate project, depending on the team that is setting up the environment and deployments.

Rule 5:
Don’t reference Kubernetes resources or dependencies that are not needed, within the application, unless you are developing a Kubernetes application. Otherwise, it won’t be possible to run the app outside Kubernetes.

Rule 6:
After a stable build of an App, it should be able to be run in the local environment, containerized and run as a single docker container, or deployed inside a Kubernetes platform with no changes to the code, other than external configurations.

Meet all the other rules and you have a platform loosely coupled application capable of running in any environment you need.

In conclusion, the cloud is the now facto target for applications to be deployed. However, we developers must always keep the horizon open and reduce technical debt, by following some simple rules, a lot can be achieved with minimum change and impact to the applications.

Kubernetes and docker should be seen as detail for the developer and not a constraint.

Overall picture of what is intended with Kubernetes:

Just like Uncle Bob did the clean architecture and indirection pattern, this can be used as the same principle for our developments and environments.

The solution flow should be inwards, the inside layers should not be aware of the outside layers but instead the other way around.

Use a common development environment and target the build to run inside a container and you will reach the ZEN of development, confident that your app will run the same everywhere.