Skip to content
back to the episode
Episode summary2023Fellow

Kubernetes Patterns — Structural & Configuration Patterns

The club carries on with the book on Kubernetes patterns: the third episode covers parts three and four — structural and configuration patterns. The host is joined by Alexander, Vova Chistyakov and two guests from the platform side, Misha and Maxim, to work through init containers, sidecars, adapter and ambassador, and then configuration: environment variables, ConfigMaps, secrets and templates.

Code of Architecture · Book Club9 min read

This summary is shorter than the full episode; examples and assessments reflect the participants’ experience and views.

The main thread of the material
01

Init containers and pod resources

The episode opens with introductions: Maxim is in the club for the first time and explains that his team automates Kubernetes delivery — large shared clusters spread across availability zones, plus integration with infrastructure; Misha runs the language platform, the libraries and frameworks every service is built on. By tradition the new guest is asked who the book is for: Maxim calls it an introduction for developers who do not see the constraints the execution platform imposes, but advises filtering it through the realities of a particular installation. Someone suggests it should have been called Kubernetes Patterns and Antipatterns — the participants again agree that part of what is described is better left undone. The first structural pattern, the init container, is introduced through the analogy of a class constructor: a separate manifest section guarantees preparation finishes before the application starts. One of the hosts adds his own case — an init container that collects variables from Vault and hands them to the application.

The database migration example draws objections: an init container runs on every pod restart, while a migration is a one-off that takes unpredictable time, so, as discussed in earlier episodes, it is moved into a Job. What remains is warm-up, database readiness checks, and utilities nobody wants inside the application image. Init containers run sequentially — one of the guests has seen pods with four of them, with no direct dependencies but a required deterministic order; application containers, by contrast, start in parallel, probes are defined only for those, and an init container merely has to exit successfully. The non-obvious consequence is resources: the effective pod request is the maximum of the sum over application containers and the maximum over init containers, so a heavy init reserves capacity that then sits idle in production. The chapter closes with a sidebar on admission controllers: a mutating webhook adds the init container to incoming workloads itself, and at Misha's place the init container calls an internal service for zone metadata, because the webhook runs before the scheduler.

02

Sidecar versus library

In the book a sidecar is a way to extend an application without rewriting it; the naive example there is an HTTP server with static files and a neighbouring container running git pull on an interval. Alexander lays out the old dilemma: a shared service is flexible but adds load and a point of failure, while a library cannot be forced on everyone and drags backward compatibility along; a sidecar gives the benefits of both, because the container on the side is the platform's responsibility. Misha pushes back: sidecar images have to be updated exactly like libraries, only across hundreds of services at once, and densely packed nodes strain from the sheer number of containers — which is why his team deliberately minimises sidecars. The book's reading, where any non-primary container is called a sidecar, strikes him as far-fetched. Maxim is closer to Misha: the overhead of a bed of sidecars exceeds handling the same thing inside the application, and he allows such layers for dumb clients and vendor software you cannot get inside.

A question from the comments asks whether init container resources can fool the scheduler into a less loaded node: the guests answer that their platform validates workloads and will not let anyone request more in init than in the main container. The participants then split sidecars in two — transparent ones, as in a service mesh, where traffic is intercepted and retries, rate limits and tracing are configured past the application, and explicit ones the application integrates with through a shared disk or the network. One of the hosts explains adapter and ambassador from a picture in a 2016 white paper on containerised workloads: an adapter makes containers uniform for the harsh outside world, like a plug adapter — the classic case is emitting metrics in the required format on behalf of a legacy system — while an ambassador does the reverse, shaping that world into something convenient for the container. Misha has exactly one sidecar left: a proxy to PostgreSQL for third-party software that cannot do service discovery; everyone else is forbidden to use it, and stale libraries are caught by fitness functions from the club's previous book: a service with an outdated version will not deploy.

03

Config: env vars, ConfigMaps, templates

The configuration part starts with where to keep config: keeping it in code means editing, rebuilding and rolling out again for every change, so the authors' first option is environment variables, the baseline scenario of the twelve factors. Their immutability is both a plus and a minus: a parameter cannot be changed on the fly, but you know exactly which values it runs with and need no logic for changes. The downsides are that nested structures map badly onto variables, and the layers of overriding — a default in code, ENV in the Dockerfile, a value in the manifest — cannot be untangled without conventions. One of the hosts admits he reads such books partly to cross things out: the advice to put the log level into a variable annoys him, because you cannot change it without a restart, and the restart takes the faulty state with it. He widens the discussion to feature flags: in a company with a stream of experiments, a perpendicular way of changing application state means an experiment's result guarantees nothing.

Maxim weighs a switch inside the application, which drifts from the deployed config, against Kubernetes structures with an update mechanism; Misha keeps configs in etcd with subscriptions to changes, puts only four or five never-changing internal addresses into variables, and raises log verbosity for a single request with a special header that cannot be sent from outside. ConfigMaps and secrets change on the fly but are limited in size because of etcd, and a secret is base64, which unwraps easily; the guests barely use Kubernetes secrets. The authors unexpectedly take immutable configuration into a separate data container built from scratch plus an init container copying the model into a shared volume: a heavy solution, with delivery through a registry and a separate image per environment. Config templating one of the hosts calls a pattern you should not use: the mistake shows up only when the pod starts, and debugging it needs access to the node. The next episode, the last one on this book, covers security, controllers and operators.

Takeaways

What to take away

  1. 01The effective pod request is the maximum of the sum over application containers and the maximum over init containers: a heavy init reserves capacity that later sits idle.
  2. 02An init container runs on every pod restart, so a one-off database migration is moved into a Job, while warm-up, checks and assembling config from Vault stay in init.
  3. 03A sidecar does not remove the update problem: images must be rolled out across hundreds of services, and dense nodes suffer from the container count — on a homogeneous stack a library is probably simpler, with trade-offs either way.
  4. 04Templating config in an init container can only be tested when the pod starts: substitution easily yields an empty string instead of a value, and debugging needs access to a production node.

Sources