Avoiding the Container Anti-Pattern: Idiomatic Containers
[from Surfing the Cloud: Idiomatic Containers]
Using a container properly can be the difference between your success or failure. Knowing how to do this lies in following a few simple idiomatic patterns.
You may have heard that the ideal pattern for containers is one process per container. This is a simplified statement explaining one of the idiomatic patterns. Idiomatic patterns are not hard rules, they are simply the more natural or appropriate way to use containers, and staying closer to these patterns will help you get the most of out of your own containers.
I have created this list from my own observations as well as other sources, most notably is the Twelve-Factor App established by a team from Heroku. These Idiomatic Container patterns are specifically oriented to the operational behavior of building and running container systems with full pipelines, where the Twelve-Factor App is oriented towards a hands-free service such as Heroku, and focuses more on the application and the developer impact. Both of these provide value, and it is suggested as an exercise for the reader to study the Twelve-Factor App.
Elements of a Service
Before discussing Idiomatic patterns, let’s first discuss what elements are typically part of a running service:
- Stack — The ecosystem which runs your application (operating system plus any language frameworks, such as Node, Python, Ruby, Java, etc).
- Code — the code that defines the service, in addition to all required dependent libraries and add-on modules (in a node world you would include npm install in this element, where node itself is considered part of the Stack).
- Config — the environment specific configurations and secrets that describe how the application runs within a single environment or for a single customer.
- Data — all of the persistent data used by the application, regardless of the storage format.
Idiomatic Container Patterns
This list is a Work-In-Progress and is part of an ongoing series on Docker, started with The Docker Mind Shift.
- One Function per Container — Each running container should only focus on one function. This keeps the way you build it simple and makes managing it easier.
- Loosely Coupled Backing Services — use external services for Data elements that must persist, and couple to them loosely.
- Ephemeral — your container should be disposable, requiring no persistent state between restarts, and no updates to state during the course of its lifecycle. This is targeted to the Config and Data elements.
- Logs as Streams — Send your logs to stdout and stderr, do not log anything to syslog or local disk. The container service should take care of these streams for you.
- Configs at Runtime — Introduce your Config elements at runtime. Do not put these into the image.
- Immutable Releases — When you make a software release, build an image that is immutable, and reuse that image between lanes in your pipeline. Do not rebuild the image to address Config element changes for each lane.
- Real Service Monitoring — Define a method to test the actually running service through its external Port Binding. Do not rely on the mere status of an available port to decide if it is healthy or not (the latter is what Docker does by default).
- CI Data Migrations — Data itself should be treated in the same manner as code, with a pipeline and quality assurance testing and tests around them. Nobody should be manually adjusting data or data schemas on live systems.
Further details at Surfing the Cloud: Idiomatic Containers.