May 29, 2018·6 min read·engineeringkubernetesconfigurationkustomize

Kustomize keeps environment differences in resource-shaped patches

Kustomize introduces a way to reuse Kubernetes manifests without parameterizing every difference. Bases and overlays preserve resource declarations while recording environment-specific changes separately.

Copying a Deployment manifest makes its first customization easy and its subsequent maintenance ambiguous. A new replica count is visible, but a later upstream change has to be reconciled with every independent copy. Google's May 29 introduction of kustomize proposes a different representation of those differences. A shared base retains complete resource declarations, and overlays describe the changes needed for particular consumers.

I like this approach because the declared Kubernetes objects remain available for inspection. The useful property of configuration is that someone can determine what a controller will receive. Reuse should help preserve that property as environments diverge. A mechanism which makes each difference explicit in the language of the resource is a useful fit for Kubernetes' API-centered design.

The announcement introduces kustomize as a SIG CLI subproject. It reads a kustomization.yaml file and the resources that file references, then writes complete resources to standard output. That separation matters operationally. Customization produces data which can be inspected and passed onward before any operation against the cluster occurs. Rendering and application are distinct steps in the workflow.

An overlay describes a change to a base

The hello-world demonstration in the May 21 v1.0.1 release shows a base with a Deployment, Service and ConfigMap. Two overlay directories customize that common set. The staging overlay changes configuration data, while the production overlay increases the replica count. The files for these variants remain small because each describes its own changes rather than repeating the complete application.

An overlay kustomization in this version can refer to its base and patch files using this structure. The relative base path connects the production directory to the shared resources without duplicating their declarations. The patch path identifies a change maintained within the overlay itself.

namePrefix: production-
commonLabels:
  variant: production
bases:
  - ../../base
patches:
  - deployment.yaml

The corresponding Deployment patch names the resource and supplies the field being changed. Here the production choice is a replica count of ten. All Deployment fields absent from this partial declaration remain the base's responsibility, so the overlay does not need to repeat the pod template merely to change capacity.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: the-deployment
spec:
  replicas: 10

These examples use the fields documented by the contemporary demonstration. The patch is intentionally incomplete. Its meaning depends on a complete Deployment in the base with the matching resource name. Reading it tells a reviewer both the target object and the location of the changed value. The number is part of a Deployment specification rather than a value whose purpose must be inferred from an unrelated parameter name.

That choice changes how configuration authors collaborate. A base author does not have to anticipate a separate named parameter for every field another environment might vary. A consumer can express a patch against the relevant resource. This avoids turning every possible deployment decision into a required entry in a shared values document. It also makes the base's resource structure part of the relationship between author and consumer.

That relationship still needs maintenance. A patch identifies an object by information supplied in the resource declaration. The demonstration explicitly requires matching names between a patch and its base resource. If a base changes the identity or shape of that resource, the overlay needs attention. Kustomize gives the difference a separate representation, but it does not remove the dependency on the base's contents.

Common transformations have a declared location

The demonstration also applies a name prefix and common labels through the kustomization file. A staging overlay can supply a prefix distinct from the production prefix. It can add common annotations at the same level. These transformations express decisions which span the resource set without requiring independent edits to each manifest.

This is useful when a variant needs an identity shared across several objects. Repeating that identity in every file makes a global change depend on finding every occurrence. A common transformation instead records that decision once in the overlay's kustomization. The resulting resources still contain their final names and labels, so the output makes the transformation's effect available for ordinary review.

Building the production variant selects its overlay directory as the input. The command reads that directory's kustomization and follows the base relationship to assemble the full resource set. An operator can capture its standard output for inspection before choosing how to submit those resources to Kubernetes.

kustomize build someapp/overlays/production

The output is a stream of complete Kubernetes resources. Its role differs from the overlay input. A patch explains what changes relative to a base, while the built result shows what is being submitted. Both are useful review artifacts because they answer different questions. A concise patch can make an intentional replica change obvious, while the output exposes other consequences of the chosen base and common transformations.

I consider that distinction important for declarative operations. A controller receives the built resource, so reviewing only the small patch is insufficient to understand the full declaration. Conversely, reviewing only a large generated result can obscure which environment-specific choice changed. Keeping the patch and the output available gives each review a concrete object to examine.

Configuration data can change resource identity

The hello-world demonstration contains a more consequential behavior than prefixes. Its ConfigMap name receives a suffix derived from the map's contents. When the staging patch changes the greeting, the resulting map name changes. Kustomize also changes references in the Deployment so that they point to the newly named ConfigMap.

This matters for the demonstration's environment-variable configuration. Its Deployment refers to the ConfigMap through configMapKeyRef. A new ConfigMap object with a new name would be ineffective if the Deployment continued to reference the previous name. The transformation has to coordinate the generated object's identity with the consumer's reference. Producing syntactically valid YAML for each object independently would not accomplish that.

The demonstration checks this coordination directly. After changing the greeting, it expects the new suffix to occur in the ConfigMap name and in two references in the Deployment. That is a concrete relationship in the generated result. A content change therefore produces a change in the pod template's configuration references, which the demonstration uses to initiate a rolling update.

This behavior also explains why the resource name in a patch differs from the final name used in the cluster. The patch selects the base object using its declared name. Prefixing and content-derived naming then contribute to the built identity. Confusing those stages would make a patch dependent on the output of a previous rendering, including a suffix which changes with content.

The example's rollback procedure reverses source configuration edits, rebuilds the resources and applies the reverted result. That procedure derives the requested configuration from the recorded inputs. It does not ask the operator to reconstruct the earlier greeting by manually editing a live ConfigMap. The relevant differences remain in files which participate in the same customization process as the forward change.

Reuse still has an upstream maintenance workflow

The v1.0.1 workflow documentation distinguishes configurations owned entirely by their user from configurations based on an external source. In the latter case, the user maintains a base repository with an upstream configuration and keeps overlays alongside it. Fetching and rebasing the base incorporates later upstream work while preserving a separate place for consumer-specific changes.

That arrangement gives common improvements a defined destination. An upstream Deployment correction belongs in the shared base, while a production replica choice belongs in the production overlay. The separation helps prevent a local operational preference from becoming an accidental permanent fork of every resource. It also gives the user a concrete way to inspect changes to the common material before rebuilding each variant.

The workflow remains ordinary file maintenance. Its benefit comes from making dependencies and differences explicit, with complete resource declarations available at the end. Updating a base can affect all of its overlays, so the source of a changed output may be a shared input even when an overlay file is unchanged. The built staging and production resources let the operator compare those consequences for the environments that consume the base.

For Kubernetes, this is a useful boundary for a customization tool. The base provides the shared resource declarations, overlays record local choices, and the build emits the declarations to be applied. The controller does not need to understand how those declarations were assembled. Its work begins with the resulting Kubernetes objects, including the names and references which the customization step has coordinated.