Principles for Well Desgined Docker Containers

NP
Nikolay PenkovJanuary 5, 2025

The core concept

Docker has revolutionized the development, deployment, and management of software. As a lightweight containerization platform, Docker allows developers to package applications and their dependencies into portable, self-sufficient blocks that run them seamlessly across various environments in a consistent and reliabile manner.

As with any other software paradigm, the design of a Docker container plays a critical role in its performance. Poorly designed containers can lead to bloated images, security vulnerabilities, and degraded application performance.

In this article we are going to review the best practices for designing a Docker container, proposed by Red Hat in their whitepaper "Principles of container-based application design".

Red Hat synthesized a set of 7 principles, which ensure the stability and correctness of your self-crafted Docker container. Those principles can be categorized in two categories - Build time and Runtime principles. We are going to discuss each of them below and see what advantages they are bringing when applied correctly.

data-center Image from Redhat whitepaper

Build time principles

Image Immutability

Applications running in containers should be immutable, meaning that after getting built no change is expected between the different environments. If done properly, the application developers can confidently state that whatever was tested in Development is identical to what is running in Production. Additionally, there is a lower chance of hidden logic, making rollbacks to previous versions less risky.

Single Concern

Each container should handle a single task only. In this way each service can be scaled in and out independently, making it seamless to the rest of the services. Also if something goes wrong within the container (e.g. the webserver crashes) this won't impact additional services (e.g. database downtime during restart).

Self-Containment

A container should be provided with all needed dependencies at build time. It should rely on the presence of a Linux kernel and have any additional libraries added when being built. At runtime the container should be provided only with configuration (e.g. ENV variables).

A common misconception here is to merge the web application with the database in a single container because it is needed to run the aplication. However, this is wrong and the self-containment principle suggests that the database container contains everything needed to run the database and the web application container everything needed to run the web application.

Runtime principles

High Observability

Containers are package in a black box manner but provide a standardized way for observability and health checks. Usually this includes a health check and readiness probe, proper event logging to the standard error (STDERR) and standard output (STDOUT) logs, and integration with metrics-gathering tools.

Process Disposability

Containerized application should be ephemeral and replaceable by another container instance at any time needed. This means that the state should be handled by external services (e.g. database or cache) so that starting or stopping the container can be done in a quick and non-intrusive manner.

Runtime Confinement

Every container should provide information about its resource requirements such as CPU, memory, networking, and disk usage so that the orchestrating platform can perform scheduling, auto-scaling, and/or capacity management correctly. A proper level of resource split granularity helps optimize resource allocation and identify potential bottlenecks before they happen.

Life-cycle Conformance

Every container should have a way to read events comming from the orchestrating platform, and conform and react to those events. This however does not mean that the application encapsulated in the container should act on all received events. It is up to you to decide which events are covered in the logic of your application and how to act upon them. For example if you want to ensure a graceful shutdown of your container and track any unfishied processing requests, you can implement a routine that is triggrered whenever the container receives a SIGTERM event. This principle allows to implement a supporting functionality around the main functionality of your application so that you ensure secure and stable executing of the corresponding processes.

Conclusion

The whitepaper "Principles of container-based application design" provides a set of principles that ensure a well designed Docker container and stability of the containerized application. However, those principles are not final but provide a way of working. While these principles offer a solid foundation, it’s important to continuously evaluate and refine your container design based on the challenges and requirements you have to address.

We use cookies

We use cookies to ensure you get the best experience on our website. For more information on how we use cookies, please see our cookie policy.