The goal of this article is to introduce the jinja-init Docker image and the reasons why we created it.
Intended audience: users and administrators of container orchestration solutions, Kubernetes in particular.
Pre-requisite: be familiar with the pod and file template concepts.
By Gauvain Pocentek, Cloud Consultant @ Objectif Libre
Images before orchestration
It has become simple and quick to orchestrate containers deployment today. But the creation of easy-to-use and uniform container images is still a challenge.
New applications developed today tend to follow the 12 factors model: 12 rules to make application deployments easier and to avoid differences between environments (dev/prod). Containerizing these applications is simpler, especially with rule #3: don’t use configuration files, use environment variables instead. Container orchestrators can easily handle these variables. As an example, Kubernetes can expose data contained in ConfigMaps and Secrets as environment variables in pods.
But a vast majority of existing applications do not support configuration through environment variables. How to handle those applications?
Several solutions exist, with pros and cons:
- Inclusion of the configuration file in the image: the best way to render a supposedly portable image useless.
- Code modification: possible for internal and open source applications, but requires more work than other solutions. Including maintenance work.
- Utilization of a bootstrap script: the script reads environment variables, generates the application configuration file, then runs the application. This is a common practice but requires some customization for most images.
- Storing the configuration file as an object: this is why ConfigMaps exist in Kubernetes. This is also a great way to store sensitive information (passwords) in clear text. Don’t do it.
In the Kubernetes context, you can use 2 types of objects to store the required information: ConfigMaps, and Secrets for sensitive data. But you still have to use all this data, and from multiple sources, to build the application configuration file. InitContainers can help.
InitContainers: towards a solution
InitContainers are simply containers executed in a pod before the main application container startup. These containers are not supposed to run alongside the main container: they perform a task then exit.
Some use cases for initContainers:
- Downloading artifacts required by the application from a repository (the latest .jar file for example)
- Ensuring that the database service is up and running, or waiting for it to become available
- Generating a configuration file
Using ConfigMaps and Secrets exposed in a pod, an initContainer can generate and store the configuration file on a volume. The main container can then access the same volume to read its configuration from the file.
The most interesting advantage is that the main container only takes care of running the application, without any kind of bootstrapping action. This is especially convenient when using automatic image build tools such as source to image.
But using initContainer only moves the problem somewhere else: each application has its own specific configurations and you now have to create dedicated images and scripts for initContainers, instead of creating specific application containers.
Templates to the rescue!
Generating configuration files for an application is not a new problem, and it’s not specific to containerized environments. Configuration management tools such as Ansible or Puppet have provided solutions that work very well, using templating engines.
The process to generate configuration files for applications are basically the same for all these tools:
- The user writes a templated version of the configuration file, and defines variables for each specific environment
- The templating engine does its job using the correct variables
- The generated file is pushed to the servers
In a Kubernetes environment, the same process can be used: the initContainer becomes a simple templating engine, and a single initContainer image can now be used for all the applications. jinja-init (github repository, docker hub) has been created for this purpose. This Docker image uses the Jinja2 template engine to generate the application configuration file using ConfigMaps, Secrets and environment variables.
We, at Objectif Libre, use this image for multiple reasons:
- Our internal infrastructure is now deployed on OpenShift, and we make heavy use of source-to-image to build container images. We needed a tool to handle configuration files.
- We migrated to Kubernetes/OpenShift to standardize our apps deployments. Using jinja-init allows us standardize the way we handle configuration files.
- The app configuration remains a configuration step: no script to write, we just have to define data in ConfigMaps and Secrets.
Why Jinja2? Because we use Python and Ansible a lot, so it was the obvious choice for us. You prefer Go or Ruby? You can easily build a custom initContainer image that will render templates using the language and templating engine of your choice.
Building and using jinja-init made our configuration management a lot easier and uniform in a container orchestration environment. It would obviously be better to be able to avoid it, but we are not yet living in a world where container-ready applications are the standard.