Skip to content
← Platform engineering

Practical lab guide

Lab: render MicroBank environment contracts

Create one reviewed base and five explicit overlays without launching four extra environments.

Documentation reviewed2026-10-01 · 5 min read · lab time varies
On this page

Understand the generator before running it

Complete the small ConfigMap render in the overlays bite first. This generator applies the same base/overlay idea to a reviewed MicroBank snapshot. It accepts exactly two Deployments and two Services, writes a base, then writes one patch per environment. It deliberately avoids inventing target infrastructure.

Three details are worth reading closely. First, the script rejects live-export fields because a desired-state file should not carry controller observations such as status. Second, includeTemplates: true puts the ownership label on generated Pods; includeSelectors: false avoids adding it to selectors. Third, the request annotation is on Deployment metadata, so changing it does not change the Pod template or itself trigger a new ReplicaSet.

The input checks catch structural mistakes, not every semantic problem. An image string can be present but unavailable. A Secret reference can be valid but unresolved. The render review is where you trace those dependencies back to the contract.

Prerequisites and boundary

Complete the platform contract. You need Python 3 and a recorded kubectl/Kustomize version. These steps write files and render manifests; they do not provision infrastructure. Only the local overlay will be connected to a running target in the next lab. Dev, QA, preprod, and production are configuration review exercises until their deployment targets, dependencies, and access controls are defined and implemented.

1. Select the latest reviewed input

Compare infra/study/gitops/base/apps.json with the original generated infra/study/k8s/apps.json and your actual operating record. Preserve deliberate hardening and image changes from the GitOps base. Check that regenerating from an older file would not erase those changes. Complete the canary recovery first if Ledger is still a Rollout.

Save the reviewed two-Deployment/two-Service snapshot to a private staging file, such as .local/platform-input.json. Do not export live objects wholesale: they can include controller-owned fields or private configuration. The new canonical source will be platform/workloads/base/apps.json; earlier snapshots become historical exercise inputs.

2. Generate a small directory structure

Create scripts/platform/ and save the following as scripts/platform/create_overlays.py. The script refuses to overwrite an existing platform workload tree and rejects unexpected object kinds/names. Review the input before running; structural checks cannot identify every embedded secret.

python
import json
import sys
from pathlib import Path

source = Path(sys.argv[1])
root = Path('platform/workloads')
if root.exists():
    raise SystemExit('Review the existing platform tree; refusing to overwrite it')
document = json.loads(source.read_text())
items = document.get('items', []) if document.get('kind') == 'List' else []
expected = {('Deployment', 'accounts'), ('Deployment', 'ledger'),
            ('Service', 'accounts'), ('Service', 'ledger')}
actual = [(item.get('kind'), item.get('metadata', {}).get('name')) for item in items]
if len(items) != 4 or set(actual) != expected:
    raise SystemExit('Expected exactly the two reviewed API Deployments and Services')
for item in items:
    if 'status' in item or item['metadata'].get('managedFields'):
        raise SystemExit('Use reviewed desired state, not a live object export')
    if item['metadata'].get('namespace') not in (None, 'microbank'):
        raise SystemExit('Unexpected source namespace')
    if item['kind'] == 'Deployment':
        pod = item['spec']['template']['spec']
        if not pod.get('containers') or any(not c.get('image') for c in pod['containers']):
            raise SystemExit('Each container needs an actual reviewed image reference')

(root / 'base').mkdir(parents=True)
(root / 'base/apps.json').write_text(json.dumps(document, indent=2) + '\n')
(root / 'base/kustomization.yaml').write_text("""apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - apps.json
labels:
  - pairs:
      platform.learnwithsk.dev/owner: microbank
    includeSelectors: false
    includeTemplates: true
""")
for environment in ('local', 'dev', 'qa', 'preprod', 'production'):
    target = root / 'overlays' / environment
    target.mkdir(parents=True)
    namespace = 'microbank' if environment == 'local' else 'microbank-' + environment
    (target / 'kustomization.yaml').write_text(f"""apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {namespace}
resources:
  - ../../base
patches:
  - path: request.yaml
    target:
      group: apps
      version: v1
      kind: Deployment
      name: ledger
""")
    (target / 'request.yaml').write_text(f"""apiVersion: apps/v1
kind: Deployment
metadata:
  name: ledger
  annotations:
    platform.learnwithsk.dev/environment: {environment}
    platform.learnwithsk.dev/request: initial
""")
print('Created reviewable overlays. No cluster operation was performed.')
bash
python3 scripts/platform/create_overlays.py .local/platform-input.json
mkdir -p .local/platform-rendered
for environment in local dev qa preprod production; do
  kubectl kustomize "platform/workloads/overlays/$environment" \
    > ".local/platform-rendered/$environment.yaml" || break
done

Inspect command results and all five output files. A shell loop exiting does not prove every render succeeded. Each output must contain the intended two Deployments and Services, correct namespace, unchanged application images, and the owner label on Pod templates. No database, Secret value, CronJob, or fault runner belongs in this base.

3. Review what is still missing

The four nonlocal namespaces are not created here. Their runtime Secret, databases, messaging, image access, storage, and identity are also not created. An environment name does not supply those dependencies. Keep these overlays render-only until their owners implement them.

The initial images come from your selected baseline. For a real release, publish the built images once, retain their registry digests and architecture, and update the target overlay to those identities. Kustomize's images transformer can set newName and digest. Use actual registry values; a local image ID is not a substitute for a registry digest. Verify the target can pull them before deploying.

4. Practise a reviewable request

Change only platform/workloads/overlays/local/request.yaml from request initial to review-001. Render again and inspect the diff. This annotation on the Deployment metadata is intentionally harmless and does not itself change the Pod template. It gives the portal capstone a small first request to automate.

For a later artifact promotion, change an image identity and retain separate build, policy, reconciliation, and transaction evidence. A harmless annotation rehearsal cannot substitute for that release test.

Acceptance and cleanup

Confirm that all five manifests render, selectors remain stable, and only the intended namespace and annotation differences appear. This exercise does not deploy resources. Commit only reviewed public configuration to your own practice branch; retain private inputs/results outside Git. The next lab connects only local to Argo CD.

Read the diff, not just the exit status

For the annotation rehearsal, expect the selected annotation to change while image references, selectors and Pod templates remain unchanged. If an image changes too, return to your selected baseline and find why before syncing anything.

The QA render should select microbank-qa, but that namespace and its dependencies are not created by this exercise. This is why “five files rendered” and “five environments deployed” are different outcomes. Carry only the reviewed local overlay into the GitOps handoff.

Sources

Kustomize transformations↗, Docker digest identity↗.

Your notes and evidence

Record observations, questions, or links to your work. Keep credentials out of your notes.

Loading saved progress…

Back up or restore this path

Progress and notes stay in this browser. A backup contains only this learning path.