Kubernetes II — ConfigMaps, Secrets, HPA, Network Policies
Connect configuration, secret rotation, autoscaling and network isolation to a complete reference workload, then reason through controller and recovery failures.
Original publication date unrecordedUpdated
Lesson preparation & details
Level: intermediate
By the end, you should be able to
- Trace environment and mounted-file configuration through a complete Deployment
- Calculate CPU and custom-metric scaling recommendations and diagnose missing metrics
- Predict directional NetworkPolicy decisions and design allow/deny controls
- Explain restart-safe reconciliation, admission, placement and stateful recovery
Bring with you
Sources checked: . A source check does not change the publication or update date.
Editorial review: · What review means
In this article · 28 sections
Review and execution boundary
This is session 67's reference implementation and worked review, not an executed cluster lab. The manifest API baseline is Kubernetes 1.25 or later: apps/v1, autoscaling/v2, networking.k8s.io/v1, and core v1. Individual clusters, metrics adapters, CNI enforcement, admission policy and image availability still need compatibility checks. The full body was re-reviewed on 10 October 2026, reusing verified 7–8 October primary snapshots and adding release-1.34 documentation pinned at commit 47132eb5367cfdcba0a3b33a0b3ffc81185bd7e8. The optional ValidatingAdmissionPolicy discussion requires Kubernetes 1.30+, not the 1.25 baseline. These are reference/API contracts, not a recommendation to deploy an old unsupported release. Publication metadata is unchanged.
The example image python:3.12.7-alpine is a historical, explicitly versioned teaching input, not a current security recommendation or immutable digest. Before an authorized deployment, select a supported patched image, inspect it, pin its digest and retest. No cluster, image pull, metrics-server installation, cloud resource, real Secret or IAM change was performed. Offline code/manifest checks do not establish service availability, CNI enforcement or certificate rotation.
Why this session exists
A Deployment and Service establish replicas and a stable destination. They do not decide how settings arrive, how credentials rotate, whether more replicas help, or which callers should connect. This lesson joins those mechanisms without treating any one of them as a security guarantee.
By the end, explain both environment and file injection; store and rotate secrets without confusing encoding with encryption; read CPU and custom-metric HPA specifications; and reason about default-deny plus explicit allowed paths. The complete reference below supplies the Namespace, configuration, synthetic Secret, Deployment, Service, HPA and policies that earlier fragments left implicit. Its lifecycle includes startup, observation, change, failure and cleanup.
Prerequisites
Review Kubernetes basics if selectors or readiness are unfamiliar. Understand requests versus limits and an HTTP request's source, destination and port. Caching affects load and therefore scaling signals, but is not required to parse YAML. Security and observability are continuing topics, not inaccurately numbered later-session prerequisites.
The primary ConfigMap, Secret, HPA and NetworkPolicy references are listed at the end. Caching is an optional connection for interpreting load, not an HPA prerequisite.
Pre-read and pacing
Use the timed headings as planning cues, not a completion guarantee. The original 90-minute session includes discussion and recall; optional preparation and cluster work can take longer. Start with configuration delivery, then autoscaling, then directional policy; return to controller recovery after the reference manifest.
These original video titles and channel names are historical finding labels, not newly verified attribution, content, availability or runtime. Choose relevant segments; no playback or speed recommendation is required:
- ConfigMaps and Secrets Explained — originally labelled TechWorld with Nana; optional orientation to the two delivery objects, not authority for confidentiality guarantees.
- Scaling Explained Through Kubernetes HPA, VPA, KEDA & Cluster Autoscaler — originally labelled DevOps & AI Toolkit; optional comparison of replica scaling, resource sizing, event-driven integration and node capacity.
- Securing Cluster Networking with Network Policies - Ahmet Balkan, Google — originally labelled CNCF; optional policy-pattern walkthrough, not evidence this reference was executed.
- Secrets · Kubernetes concepts — official confidentiality and access-control reference; read the risks rather than trusting base64.
- Horizontal Pod Autoscaler · Kubernetes tasks — official algorithm and metric-contract reading; distinguish proportional recommendation from the complete control loop.
- Network Policies · Kubernetes concepts — official selector/direction examples and implementation limits. Mutable pages may describe newer features than the reference baseline.
(a) Intuition · 5 min
Think of a hotel: configuration sets room temperature; credentials grant access to a protected service; capacity rules open more rooms; network rules constrain routes. The analogy stops at trust boundaries. A Kubernetes Secret is not an opaque safe against an attacker in its consuming process. A sidecar does not automatically receive another container's mount or environment, but shared volumes, process-namespace settings, privileges and API permissions can expose credentials. Treat containers in a Pod as a closely coupled trust unit, not mutually hostile tenants.
What each object actually is
- ConfigMap: nonconfidential strings or binary data, commonly consumed as environment variables or files. Do not use it for credentials.
- Secret: a typed API object for confidential values.
datauses base64;stringDataaccepts strings. Base64 is reversible encoding. Access control, encryption at rest, transport protection and workload isolation remain separate controls. - HorizontalPodAutoscaler: a controller that changes a target's desired replica count through its scale subresource using observed metrics. It does not directly spawn nodes or grant CPU capacity.
- NetworkPolicy: namespaced, additive L3/L4 connection allowances implemented by a supporting networking plugin. It is not HTTP authorization, encryption or a universal zero-trust system.
The official Secrets documentation says Secrets are stored unencrypted by default in the underlying datastore. Inspect the actual cluster's encryption configuration rather than assuming a managed provider's name establishes it. Encrypting etcd protects one storage boundary; RBAC protects API access; a compromised consumer can still use the value. A principal allowed to create Pods that consume a Secret can often obtain its contents even without a direct Secret read grant.
Sealed Secrets encrypts a Git representation; its controller can create an ordinary Secret that still needs these controls. External secret synchronization can likewise materialize a Kubernetes Secret. Neither tool makes accidental exposure impossible. Key backup, rotation, controller access and revocation are operational work, not a one-time checkbox.
A quick history so the ecosystem makes sense
The Kubernetes project's April 2016 configuration-management article introduced ConfigMap in the 1.2 release; it did not establish that Secrets first appeared in that release. NetworkPolicy became stable in 1.7 (2017), not an unenforced invention until 2019. The autoscaling/v2 API became stable in 1.23; earlier v2beta* APIs must not be confused with that graduation. Pod Security Admission became stable in 1.25 (2022). These milestones explain why the reference uses stable APIs.
HPA v1 focuses on CPU utilization. HPA v2 supports multiple resource/custom/external metrics. A metrics adapter exposes the chosen metric API; KEDA is an event-driven integration option, not proof that every team uses it or that installing it solves capacity planning. Pod Security Admission's privileged/baseline/restricted standards concern Pod security settings, separately from NetworkPolicy traffic rules.
How config + secrets flow into a pod
Figure text equivalent — labels and relationships
- Node “ConfigMap” (square).
- Node “Application Pod” (square).
- Node “Secret” (square).
- Node “resource / custom metrics API” (square).
- Node “HPA” (square).
- Node “Deployment desired replicas” (square).
- Node “NetworkPolicy + enforcing plugin” (square).
- “ConfigMap” leads to “Application Pod”: environment at startup / projected files. Line: normal; ends: arrow point.
- “Secret” leads to “Application Pod”: explicit key / read-only volume. Line: normal; ends: arrow point.
- “Application Pod” leads to “resource / custom metrics API”: observed usage. Line: normal; ends: arrow point.
- “resource / custom metrics API” leads to “HPA”: sampled metrics. Line: normal; ends: arrow point.
- “HPA” leads to “Deployment desired replicas”: scale subresource. Line: normal; ends: arrow point.
- “Deployment desired replicas” leads to “Application Pod”: ReplicaSet reconciliation. Line: normal; ends: arrow point.
- “NetworkPolicy + enforcing plugin” leads to “Application Pod”: connection allowances. Line: dotted; ends: arrow point.
The arrows distinguish data delivery from control. HPA adjusts the Deployment; controllers reconcile replicas; the scheduler then finds capacity. A desired count is not a count of Ready endpoints.
Two ways to inject config, side-by-side
Environment: envFrom imports all eligible keys; env with valueFrom imports a particular key and can rename it. The container receives a startup snapshot. Updating the object does not update that running process's environment. Replacing/restarting the workload is the normal delivery path for new values. Explicit keys reduce unintended exposure compared with importing every credential.
Files: each selected ConfigMap/Secret key becomes a file in a mounted volume. Ordinary projected volume updates are eventual: kubelet sync and its configured cache/watch propagation contribute to delay. There is no universal 60-second guarantee. A subPath mount does not receive automatic updates. Avoid mounting over a directory containing required image files.
File replacement does not imply application reload. Reopen paths rather than retaining an old file descriptor; account for symlink/atomic projection changes when watching. Validate new content before adopting it. A JSON syntax failure should retain the last-known-good configuration and expose a nonsecret diagnostic, rather than partially apply settings. For a 4 KB JSON configuration, a ConfigMap key config.json mounted at /etc/config/config.json is appropriate; do not put a password in that JSON merely because it travels as a file.
How HPA thinks — the algorithm in one sentence
The core proportional recommendation is:
desiredReplicas = ceil(currentReplicas × currentMetricValue / desiredMetricValue).
Four Pods averaging 90% against a 60% utilization target suggest six replicas. A container requesting 200m CPU and using 100m is at 50% of its request, not 50% of the node or its limit. That distinction changes the denominator dramatically.
Tolerance, missing samples, readiness, min/max bounds, stabilization and rate policies modify the actual recommendation. Multiple metrics produce separate recommendations; the controller normally chooses the largest. If a metric cannot be evaluated and the available metrics recommend downscaling, the error can prevent downscaling; it does not necessarily prevent an otherwise justified scale-up.
By default scale-down stabilization is 300 seconds and scale-up stabilization is zero, subject to cluster/controller configuration. Stabilization considers recent recommendations; it is not an unconditional five-minute sleep after every change. Our example explicitly uses a 15-second scale-up window and at most 100% growth per 30 seconds; scale-down uses 300 seconds and at most 50% reduction per 60 seconds. Actual observation includes controller sampling and startup delay.
The four things HPA needs to work
- A functioning metric API. CPU/memory resource metrics usually come from metrics-server; custom/external metrics require an adapter. The Kubernetes API object alone installs neither. Do not fix TLS trust by copying an insecure metrics-server flag into production.
- A meaningful denominator. CPU utilization needs CPU requests on relevant containers; a request-less sidecar can make that Pod's utilization undefined. Absolute
AverageValueand custom/external metrics have different requirements.kubectl topshowing a number is not sufficient proof of HPA eligibility. - A scalable target and valid policy. Verify target name, namespace, scale support, min/max and metric units. Avoid a second controller or repeated declarative apply fighting HPA over
spec.replicas. - Capacity and representative load. A load test must be bounded and authorized, and must actually consume the measured resource. A static page request loop need not produce 2 → 4 → 8 scaling. Inspect Pending Pods, quotas, requests and latency rather than declaring success from desired replicas alone.
A custom-metric alternative replaces the metrics list with this autoscaling/v2 fragment. It assumes an installed adapter exposes per-Pod http_requests_per_second for the selected Pods; this name is a contract with the adapter, not a built-in metric.
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"Worked answer: four Pods averaging 150 requests/s against 100 suggest six replicas before policy adjustments. If CPU simultaneously suggests eight, eight wins. If the adapter reports a cumulative request counter rather than a rate, this is the wrong signal: establish a rate window and units upstream. If backlog is an external metric, select External and the appropriate total/per-replica target instead; do not relabel a queue-wide count as a per-Pod sample. HPA creates more Pods, whereas a node autoscaler changes node capacity under its own constraints. VPA changes resource sizing; using it to change the same CPU-request denominator while CPU HPA acts can create coupled feedback and needs deliberate configuration.
NetworkPolicy — the mental model
podSelector: {} selects all Pods in the policy's namespace. An empty selector does not select none. No selecting policy for a direction means that direction is non-isolated by NetworkPolicy; other controls may still block traffic. Once selected for Ingress or Egress, allowed connections are the union of rules from all selecting policies for that direction. An empty rule list contributes no allow; it is not a priority deny that overrides another policy.
For two isolated endpoints, source egress and destination ingress must allow the connection. Reply traffic for an allowed connection is implicitly allowed. A peer podSelector alone refers to the policy's namespace. Put a namespace selector and Pod selector in one peer for AND; separate peers mean OR. Ports constrain the permitted destination port, not the Service's name. Node-originating traffic, host networking, NAT and plugin-specific behavior have documented qualifications; NetworkPolicy is not a universal packet firewall.
A deny test alone is weak: DNS failure, no Ready endpoints and a wrong port all look like denial. Pair an allowed control with a denied control, verify the selector and inspect CNI enforcement. A missing ingress allow does not automatically block DNS egress. If egress is isolated, DNS may require both UDP and TCP 53 to the actual resolver; node-local DNS may require a different design from a kube-system Pod selector.
Restart-safe controllers and operators
An operator combines Kubernetes reconciliation with application-specific operational knowledge: membership changes, safe upgrades, backup and restore. A StatefulSet is not that knowledge. Watches are notifications, not a durable business-event journal; clients can reconnect/relist after expired resource versions. Reconcile from current desired and observed state, and make external actions repeat-safe.
Suppose creating backup backup/cluster-uid/generation-7 succeeds, but the operator crashes before status is written. On restart, look up that stable operation identity and observe the existing result rather than creating a second backup with a random name. Status may record progress; external systems and persisted objects may also hold recovery information. The earlier claim that operators must store nothing was too strong. Idempotence means retries converge safely, not that every invocation must return identical output while reality changes.
A controller can arrange requeues, watches and resync/relist behavior; not every correct controller requires the same periodic timer. A finalizer keeps a deleting object observable with deletionTimestamp until its controller completes cleanup and removes its key. It can get stuck when a dependent service is unavailable. Repair the controller/permission and verify cleanup before considering manual finalizer removal; removing it may orphan real resources.
Here is a complete pure decision model, not a Kubernetes operator or backup implementation. Persistence, API conflicts, credentials, quorum and retry backoff are intentionally outside it.
def next_backup_action(desired_generation, observed):
if desired_generation < 1:
raise ValueError("generation must be positive")
operation = f"backup/generation-{desired_generation}"
if observed.get("deleting", False):
if observed.get("external_exists", False):
return ("delete_external", operation)
return ("remove_finalizer", operation)
if not observed.get("external_exists", False):
return ("create_if_absent", operation)
if observed.get("completed_generation") != desired_generation:
return ("observe_completion", operation)
return ("ready", operation)
assert next_backup_action(7, {}) == ("create_if_absent", "backup/generation-7")
assert next_backup_action(7, {"external_exists": True})[0] == "observe_completion"
assert next_backup_action(7, {"external_exists": True, "completed_generation": 7})[0] == "ready"
assert next_backup_action(7, {"deleting": True, "external_exists": True})[0] == "delete_external"
assert next_backup_action(7, {"deleting": True})[0] == "remove_finalizer"The crash test is a table of observations, not a command to delete a real operator Pod. Repeat the create decision against an idempotent external operation; simulate the action succeeding without a status write; then verify that re-observation advances to completion instead of duplication. The model's fixed generation key is only safe within its assumed single-resource scope; a real implementation must include the resource UID and handle concurrent generations.
A CRD is a schema and API, not an operator
A namespaced BackupRequest can express intent while its controller owns observations. In apiextensions.k8s.io/v1, a structural OpenAPI schema validates the custom resource; unknown fields are normally pruned unless preservation is deliberately specified. A typo silently pruned under a permissive client path is not a backup request that your controller actually saw. Enabling the /status subresource separates ordinary writes from status writes; grant the controller only the verbs and subresources it needs. The API gives discovery and authorization machinery, not an automatically safe RBAC policy, business validation, events or backup implementation.
This inert CRD fragment documents the intended version contract, not an installable complete CRD or a backup controller:
# Fragment inside spec.versions of an apiextensions.k8s.io/v1 CRD
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [destination]
properties:
destination:
type: string
minLength: 1
status:
type: object
properties:
observedGeneration:
type: integer
phase:
type: string
subresources:
status: {}The enclosing CRD would still need its group, names and scope. served exposes a version and storage selects its persisted representation; changing a version label is not a data migration. Design conversion and compatibility before changing schema. observedGeneration is a controller-maintained acknowledgement of the intent it processed, not a field Kubernetes fills in for this custom type. A Ready condition for generation 6 does not prove generation 7's backup completed. Record the external operation's durable identity, status and recoverable error without credentials, and reconcile conflicts by re-reading rather than overwriting another writer.
Admission, placement and stateful ownership
spec expresses desired state and status observations, but controllers can legitimately write spec: HPA changes a workload's desired replicas. Define field ownership so the user, GitOps reconciler and autoscaler do not fight. CRDs provide typed API integration; useful RBAC, validation, status, events and safe operational behavior still need implementation.
Mutating admission runs before validating admission. A failing webhook with failurePolicy: Fail rejects matching requests, not necessarily every cluster write. Scope rules, namespace/object selectors, timeouts and availability matter. For a broken Pod webhook, diagnose which requests match, restore its endpoint/certificates, and use an independently authorized recovery path if policy prevents the repair itself. Fail-open trades availability against enforcement and is not a universal fix. Ignore applies to call failures, not a webhook that successfully returns an explicit rejection. A namespace/object selector narrows matching; allowing untrusted users to alter bypass labels can undermine enforcement. Keep the recovery path independent of the failing webhook where feasible and authorized.
On Kubernetes 1.30+, ValidatingAdmissionPolicy can evaluate CEL in the API server without an external webhook endpoint. A policy needs a binding to take effect. A binding can Deny, Warn or Audit; an observed warning is not enforcement. CEL evaluation errors and false validation results have distinct failure/action semantics. This reduces one network dependency but does not implement arbitrary external checks or eliminate admission-policy mistakes. Pod Security Admission separately applies privileged/baseline/restricted Pod standards; it does not replace custom-resource validation.
Placement combines requests, node affinity, Pod affinity/anti-affinity, topology spread and taints/tolerations. A toleration permits scheduling onto a tainted node; it does not attract the Pod or reserve that node against all others. Hard constraints can leave Pods Pending. Defaults may spread Pods but do not prove the application's required failure-domain separation. For the reference Deployment, this optional Pod-spec fragment makes hostname spread explicit, provided there are enough eligible, labelled nodes:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: webWith three eligible nodes and three replicas, a healthy placement can be 1/1/1. This fragment does not require three live domains: if only one eligible hostname domain remains, its global minimum changes and the constraint alone may permit all replicas there. Inspect eligibility and capacity rather than assuming either permanent separation or inevitable Pending Pods. Multi-zone requirements need zone topology, not just hostname separation; minimum-domain features have their own version gates.
A StatefulSet supplies stable Pod identity, persistent-volume association and configurable ordered lifecycle. It does not by itself implement database replication, quorum-safe membership, shard rebalancing or tested backups. The default ordered policy is not the only Pod-management mode. Compare:
- Managed service: transfers some operations to a provider under a particular service contract. Your team still owns data modeling, client behavior, access, recovery objectives and restore testing. Cost and reliability are service-specific; this lesson authorizes no purchase.
- Established operator: useful if its exact version supports your database version and upgrade/backup paths, and the team can recover when the operator fails. Neither universal automated recovery nor full configuration freedom is guaranteed.
- Plain StatefulSet plus runbooks: reasonable when the system's operations and staffing are understood. Human or separate automation owns backups, restore, membership and upgrades; there is no universal “only two clusters” threshold.
Worked choice: for a three-member quorum datastore, require two voting members during a one-at-a-time upgrade, a verified backup and a restore path. A StatefulSet rollout being Ready is insufficient evidence that replicas contain the right data. Select the service/operator/runbook option that demonstrates those contracts within the team's requirements; do not write a new operator merely to remove a manual command.
Disruption, storage and identity are separate recovery contracts
A policy/v1 PodDisruptionBudget (PDB) constrains supported Eviction API requests such as a node drain. It does not stop a node crash, direct Pod deletion, or a Deployment/StatefulSet controller's rolling update. Involuntary unavailability counts against the budget even though the budget cannot prevent it. Configure the workload's own rollout settings separately.
For a three-replica quorum service, this is an illustrative PDB, not an extra object to apply to the two-replica web reference:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: datastore-quorum
namespace: lesson
spec:
minAvailable: 2
selector:
matchLabels:
app: datastoreWith three healthy selected Pods, one voluntary eviction can be admitted; with only two healthy, another would violate the minimum. Readiness must reflect actual serving health, and this number alone does not prove two members hold a database quorum. A missing replacement node, hard placement constraint or unavailable volume can leave drain blocked. Do not force deletion just to make maintenance look finished. Coordinate HPA minimums and maintenance capacity: a minimum of two replicas with minAvailable: 2 leaves no healthy-eviction headroom at that floor.
A PVC requests storage; a PV represents the provided storage; a StorageClass/provisioner determines provisioning and other storage behavior. Check capacity, topology, attachment and access mode before interpreting a Pending Pod as a CPU shortage. ReadWriteOnce means read-write mounting by one node, not necessarily one Pod. A StatefulSet's stable claim association is not replication or a snapshot. For dynamically provisioned storage, a Delete reclaim policy can delete the backing asset after claim release; Retain leaves manual recovery/cleanup work. Inspect both StatefulSet claim-retention settings and PV reclaim policy before scale-down or deletion. A retained disk is not a tested, application-consistent backup. Restore into an isolated authorized target and verify data invariants in a future integration exercise; this review does not provision a volume.
ServiceAccount identity, RBAC, Pod security and traffic policy also differ. A ServiceAccount token authenticates to its intended audience; RBAC decides allowed API operations. In modern Kubernetes, projected tokens can be short-lived and rotate; a consumer must reload them appropriately. The reference sets automountServiceAccountToken: false because its HTTP handler does not need Kubernetes API access. This suppresses automatic token mounting, not every possible credential or API path. Cloud workload-identity federation additionally needs issuer/subject/audience trust and cloud-side authorization; a Kubernetes service account alone is no Azure role grant.
The reference's non-root UID, dropped capabilities, seccomp, no privilege escalation and read-only root filesystem reduce runtime privilege. They do not prove image safety, encrypt traffic, or authorize HTTP callers. NetworkPolicy labels are selection inputs, not cryptographic caller identities: restrict who may create or relabel trusted client Pods. Application authentication and TLS remain separate. Image signature verification (for example a reviewed cosign/admission integration) is an optional supply-chain topic, not a feature this manifest silently installs.
(c) Hands-on · 25 min
Save the following as reference.yaml only if you are preparing the optional exercise. Reading it has no side effects. Every object uses namespace lesson; the Secret is a visibly fake fixture, not a credential. The CPU HPA is complete but requires a functioning resource metrics API. No RBAC, CNI, metrics-server or paid service is installed by this reference.
apiVersion: v1
kind: Namespace
metadata:
name: lesson
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: lesson
data:
APP_MODE: "teaching"
greeting.txt: "Hello from a mounted file"
app.py: |
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
def status(mode, greeting, token):
return {"mode": mode, "greeting": greeting.strip(), "fixture_present": bool(token)}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path not in ("/", "/healthz"):
self.send_error(404)
return
try:
result = status(os.environ["APP_MODE"],
Path("/etc/config/greeting.txt").read_text(),
Path("/etc/fixture/DEMO_TOKEN").read_text())
payload = json.dumps(result).encode()
except (OSError, KeyError):
self.send_error(503, "configuration unavailable")
return
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
if __name__ == "__main__":
HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
---
apiVersion: v1
kind: Secret
metadata:
name: app-fixture
namespace: lesson
type: Opaque
stringData:
DEMO_TOKEN: "NOT-A-CREDENTIAL-version-1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: lesson
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: web
image: python:3.12.7-alpine
command: ["python", "-B", "/etc/config/app.py"]
ports:
- name: http
containerPort: 8080
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: app-config
key: APP_MODE
- name: DEMO_TOKEN_AT_START
valueFrom:
secretKeyRef:
name: app-fixture
key: DEMO_TOKEN
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
- name: fixture
mountPath: /etc/fixture
readOnly: true
resources:
requests:
cpu: "200m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "128Mi"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
readinessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 5
volumes:
- name: config
configMap:
name: app-config
- name: fixture
secret:
secretName: app-fixture
---
apiVersion: v1
kind: Service
metadata:
name: web
namespace: lesson
spec:
selector:
app: web
ports:
- port: 8080
targetPort: http
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
namespace: lesson
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
behavior:
scaleUp:
stabilizationWindowSeconds: 15
policies:
- type: Percent
value: 100
periodSeconds: 30
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-web-ingress
namespace: lesson
spec:
podSelector:
matchLabels:
app: web
policyTypes: [Ingress]
ingress: []
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-clients
namespace: lesson
spec:
podSelector:
matchLabels:
app: web
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
role: client
ports:
- protocol: TCP
port: 8080Binding 0.0.0.0 is intentional inside this reference container so a Service can reach it; it is not permission to start a public host preview. The server is deliberately small and single-threaded, not a production web server or a CPU benchmark. It reopens the greeting and synthetic token on each request but returns only token presence, never token bytes. The environment token is included only to contrast startup snapshots with mounted updates; the handler does not return it.
What each block does
Read the references in dependency order: Namespace → ConfigMap/Secret → Deployment → Ready Pods → Service. app=web agrees across Deployment selector, Pod label, Service and both policies. The Service targets the named container port http, which is 8080. The same-namespace role=client allowance is additive to the empty ingress policy. There is no egress isolation here and no external LoadBalancer or Ingress.
The application imports APP_MODE once from configMapKeyRef; the Secret key is explicitly imported once into DEMO_TOKEN_AT_START. Mounted files are reread. envFrom would import all keys, including ones the application never intended to expose; explicit references make the contract inspectable. Missing required keys/objects can prevent container startup; missing/unreadable mounted configuration later makes readiness fail. The HPA targets this Deployment, not an unspecified pre-existing workload.
Optional lifecycle, not executed: first verify the exact context, namespace ownership, API/CNI/metrics versions and image digest. Review a server-side dry-run only with permission—it contacts the API and admission webhooks. An authorized operator may then apply the manifest, inspect Deployment rollout, EndpointSlices and HPA conditions, and run the bounded controls below. Do not reinstall the cluster, fetch a floating latest manifest or disable TLS validation to force a pass.
For an approved future cluster test, instantiate two otherwise-identical temporary client Pods using the reviewed Python image: one labelled role=client, the other role=outsider. Each should attempt one urllib.request.urlopen("http://web.lesson.svc.cluster.local:8080/", timeout=3) and exit. Record namespace, labels, DNS result, connection result and HTTP status. Client should get 200; outsider should fail to connect under an enforcing CNI, absent another additive allowance. A 404 is not network denial. If both fail, fix Service/readiness/DNS first; if both succeed, inspect other policies, labels and plugin enforcement. Delete the temporary clients afterwards. This test specification avoids an unbounded load loop or interpreting every nonzero exit as successful enforcement.
For CPU scaling, first collect baseline usage and HPA conditions; predict the recommendation; then use an explicitly bounded, approved workload generator with a stop condition and CPU/latency limits. The sample server may not create enough load. Stop load, inspect recent recommendations and scale-down behavior, and record desired versus Ready replicas. Cleanup requires confirming namespace ownership: deleting lesson would delete all its contents, not just these examples. Prefer deleting the reviewed inventory; remove HPA before intentionally returning replica control to a fixed value. Do not erase a shared namespace.
Rotate a Secret and observe delivery
In a disposable authorized exercise, change only the synthetic fixture from version 1 to NOT-A-CREDENTIAL-version-2 using a reviewed manifest update. Predict before observing: the mounted file eventually changes, the running process's DEMO_TOKEN_AT_START remains version 1, and a replacement container reads version 2 in both locations. For real credentials use the protected provisioning flow, never a literal in shell history or logs.
Compare the mounted and startup fixture inside the container and report only equality and an application generation number; do not print either value. An equality change from true to false demonstrates different delivery times, not that a downstream database accepted the new credential. Test new credentials at the real consumer under authorization, keep an overlap window if supported, then revoke the old value only after adoption. Rollback may be impossible after revocation; reissue a valid credential rather than restoring an invalid string.
For TLS hot rotation, the certificate producer updates the Secret, kubelet projects it, and the TLS server must adopt a matching validated certificate/key pair. Monitor the projected directory/symlink generation, reopen both files from one generation, validate the pair and chain, then swap the active context without disrupting existing sessions. If validation fails, retain last-known-good material and alert. NGINX configuration/certificate reload requires a supported reload mechanism; cert-manager writing a Secret does not itself make every process reload or restart. Establish overlap, expiry alerts and a test that new TLS handshakes present the new certificate. The deadline must exceed propagation, reload and clock-skew margins; seven days is a policy example, not a universal rule. This design is not an implemented or executed TLS server.
(d) Production reality · 15 min
Exposed Secret, hypothetical: a base64 Secret is committed. Contain access, rotate/revoke the credential, audit use and restrict/remove exposed copies under the incident process. History cleanup alone does not invalidate a credential. Encryption tooling reduces some exposure paths but cannot prevent someone committing plaintext elsewhere or leaking from a consuming process.
Scaling oscillation, hypothetical: a custom short downscale window repeatedly sheds capacity before the next burst. Inspect configured behavior and recommendation history; do not blame an alleged default one-minute downscale that contradicts the documented default. Increase a justified minimum, tune stabilization/rate limits and account for startup and downstream saturation. Extra replicas cost money and can overload databases; “false-positive scale-up is always cheap” is false.
Policy lockout, hypothetical: a namespace-wide ingress-and-egress default-deny blocks the application's required paths. Map the actual call graph; restore narrowly scoped allows for clients, dependencies and resolver traffic, including TCP DNS where used. An ingress-only policy would not explain blocked outbound DNS. Use paired controls before and after the change, not an assumed visualization-tool guarantee.
Common footguns
- Changing a ConfigMap does not restart a Deployment automatically. A change to a Pod-template checksum annotation can trigger rollout; changing only Deployment metadata does not.
- A file mount does not prevent credential reads by its consumer or a privileged attacker. Do not log environments or credential contents.
- HPA success conditions, metric freshness, resource requests and scheduler capacity are separate. A Pod count alone is not SLO evidence.
- A CNI may accept NetworkPolicy objects without enforcing them. Verify its installed mode and version with an allowed and denied control.
- A finalizer is a safety gate, not litter. Removing it without understanding cleanup can orphan data or paid resources.
Where this shows up in the rest of the plan
Continue to Azure identity, storage and networking for the host-cloud boundary and Terraform for resource/state lifecycle. Observability connects metric choice to latency and error budgets; security connects admission, credentials and packet paths; system design connects Pod scaling to downstream bottlenecks. These are subject transitions, not a claim that every cluster enables Azure Policy or that obsolete session numbers identify the right chapter. Helm is an optional manifest-packaging path; Prometheus adapters and KEDA are optional metric integrations; VPA concerns resource sizing; Sealed Secrets, External Secrets/Vault and cosign address different credential/supply-chain boundaries. Hubble and policy-analysis tools can aid inspection, but no visualization tool is assumed to predict all enforcement behavior. None is installed or tested here.
(e) Recall + stretch · 10 min
Try each question before opening its answer.
1. ConfigMap versus Secret—and why is base64 not encryption?
Use a ConfigMap for nonsecret flags/JSON and a Secret for confidential inputs. Base64 is reversible without a key. Protect API reads, etcd/backups, workloads and transport separately; an external store does not undo exposure at the final consumer. A Secret is not automatically encrypted or least-privileged.
2. Why is a rotated Secret still old in the environment?
The process received a startup snapshot. Ordinary mounted files update eventually, not on a fixed 60-second deadline, and subPath mounts do not automatically update. Reopen/reload files or replace the container for new environment values. Prove adoption without logging the value.
3. What HPA prerequisites are commonly missed?
A healthy metric API/adapter and a meaningful target metric are required. CPU utilization additionally requires relevant CPU requests. Check namespace/target, conditions, readiness, bounds and schedulable capacity. Absolute/custom metrics do not all require the same CPU denominator.
4. What does a selected policy with no rules do?
It isolates the selected direction and contributes no allows. Empty podSelector selects all Pods in that namespace. Other selecting policies can still permit traffic; node/plugin qualifications remain. An Ingress-only policy leaves egress unchanged.
5. Why asymmetric HPA windows, and how many replicas for 4 × 90 / 60?
The raw recommendation is six. Fast scale-up can reduce overload and slower scale-down can avoid churn, but both cost and correctness depend on the workload. Defaults are zero/300-second stabilization for up/down, with controller and policy qualifications. Missing metrics, readiness, tolerance and rate policies can change the action.
6. How do you deliver a 4 KB JSON file?
Use a ConfigMap data.config.json key and an ordinary read-only volume. Validate JSON and atomically adopt it in the application; a projected-file change alone is not reload proof. Keep confidential fields out of the ConfigMap.
7. HPA versus node autoscaling?
HPA adjusts workload replicas. A node autoscaler provisions/removes node capacity based on its own scheduling and consolidation rules. HPA may produce Pending Pods when nodes are full; node scaling can be blocked by quota or constraints. Neither guarantees low latency or lower cost.
8. Policy applied and the app is unreachable: first three checks?
First verify endpoints/readiness, port and DNS with an allowed control. Second resolve selectors, namespaces and both source-egress/destination-ingress permissions, including resolver access only if needed. Third verify actual networking-plugin enforcement and other additive policies. Do not add namespace-wide allows as a reflex.
9. Design TLS rotation without restarting Pods.
Use the producer → Secret → projected generation → validated certificate/key → server reload sequence above. Preserve last-known-good context on invalid input; verify new handshakes and expiry alarms, account for projection delay, then retire old material. A Secret update alone does not prove an active TLS context changed.
Explain-out-loud test
Explain configuration versus confidentiality, the HPA metric/denominator/control/capacity chain, and directional additive policy decisions. Then explain how an interrupted operator discovers that its external action already succeeded. If your explanation needs an unobserved in-memory flag or a blanket “deny wins” rule, revisit the corresponding section.
Corrected contracts and failure analysis
The critical distinction is desired, delivered, observed, adopted. An HPA desire is not a Ready Pod; an accepted NetworkPolicy is not enforcement; an updated Secret file is not an adopted credential; a StatefulSet is not a database recovery algorithm. Check each transition independently rather than using one green command as evidence for all four.
Boundary exercise with solution
Four Pods average 90% CPU against 60%, and an Ingress-only policy selects every Pod in lesson with no rules. Predict the results. Answer: raw HPA recommendation six before adjustments; all selected Pods become ingress-isolated except additive allows and documented qualifications. Egress is unchanged. Explain why an allowed client plus an unlabelled client distinguishes enforcement from a broken Service.
What comes next
Next: S068 · Azure Cloud — Identity, Storage, Networking, App Service. Use the 6-month learning plan to choose another route. This is session 67 of the original 130-session sequence: intuition → visual → reference practice → failure analysis → recall. The 90-minute plan is an estimate, not an evergreen correctness or completion promise. Optional exercises require a separate authorized environment; completing the reasoning is not a claim that cloud resources were deployed.
Source-backed review notes
Re-reviewed 10 October 2026; verified inherited passages and new raw/canonical snapshots are bound in the batch-38 evidence packet. Historical batch-4 review remains separately attributable. API versions above are the reference contract, while current documentation may describe newer optional features not used here.
-
ConfigMaps and Secrets: startup environments, eventual projection, subPath and confidentiality boundaries.
-
HPA algorithm and v2 walkthrough: metrics, multiple recommendations and stabilization.
-
NetworkPolicy: directional isolation, additive peers and implementation qualifications.
-
Operator pattern, API watches and finalizers: observation and cleanup contracts.
-
Admission webhooks, scheduling, topology spread and StatefulSets: match scope, placement and lifecycle limits.
-
cert-manager certificate lifecycle and Sealed Secrets v0.27.1: producer/reloader and encrypted-representation distinctions.
-
ConfigMap 1.2 history, NetworkPolicy 1.7, HPA v2 in 1.23, Pod Security Admission: dated API milestones, not adoption percentages.
-
Custom-resource schemas and subresources, Validating Admission Policy, disruptions, persistent volumes and ServiceAccounts: release-1.34 source snapshots govern the added conceptual treatment; CEL policy has the separate 1.30+ gate.
Pause / Recall / Apply
Can you explain it without the page?
Close the example. Reconstruct the core idea, then change one assumption. Mark complete when you’re ready; you can always undo it.
Stored in this browser only. No account, no sync. Clearing browser data removes your record.