Learning bite
Deployments, Services, and configuration
Separate replaceable workloads from stable access and runtime inputs.
On this page
Learn the pieces with one web page
A container can restart inside its existing Pod. If the Pod itself is deleted, a standalone Pod has no general promise of replacement. A ReplicaSet maintains a requested number of matching Pods. A Deployment manages ReplicaSets and changes their Pod templates during updates, so it is the usual starting point for a replaceable web process. Keep independent services in separate Pods; sharing a Pod means sharing placement and a network namespace.
Pod names and IPs can change after replacement. A Service gives callers a stable name and address while selecting the current backends by their labels. A ClusterIP Service is intended for cluster access. NodePort and LoadBalancer add exposure mechanisms that depend on the surrounding network; they are not needed for this local first exercise. Cluster DNS makes study-web resolve within its namespace; from another namespace use study-web.classroom or the full cluster DNS name.
Use the running kind cluster from the preceding bite. In the MicroBank checkout, save the following as .local/classroom.yaml. This separate classroom namespace contains no application data. If it already exists, inspect it and use only your own previous fixture. The Python image may need a registry download; record its resolved image ID, since the tag can change over time.
apiVersion: v1
kind: Namespace
metadata:
name: classroom
---
apiVersion: v1
kind: ConfigMap
metadata:
name: study-page
namespace: classroom
data:
index.html: "<p>Kubernetes study page</p>\n"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: study-web
namespace: classroom
spec:
replicas: 1
selector:
matchLabels:
app: study-web
template:
metadata:
labels:
app: study-web
spec:
automountServiceAccountToken: false
containers:
- name: web
image: python:3.13-slim
command: [python, -m, http.server, "8080"]
workingDir: /site
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
memory: 128Mi
volumeMounts:
- name: page
mountPath: /site
readOnly: true
volumes:
- name: page
configMap:
name: study-page
---
apiVersion: v1
kind: Service
metadata:
name: study-web
namespace: classroom
spec:
selector:
app: study-web
ports:
- port: 80
targetPort: 8080
Read the connections before applying: the Deployment selector matches its template label; the Service selects that same label. Service port 80 forwards to the process listening on 8080. Declaring a container port would not make a process listen. The ConfigMap supplies a file; the volume mounts it into /site. The next bite explains the resource settings and adds an explicit readiness probe.
kubectl --context kind-microbank-advanced apply -f .local/classroom.yaml
kubectl --context kind-microbank-advanced -n classroom rollout status deployment/study-web --timeout=180s
kubectl --context kind-microbank-advanced -n classroom get deploy,rs,pods,svc
kubectl --context kind-microbank-advanced -n classroom get pods --show-labels
kubectl --context kind-microbank-advanced -n classroom get endpointslices -l kubernetes.io/service-name=study-web
kubectl --context kind-microbank-advanced -n classroom exec deployment/study-web -- python -c 'from urllib.request import urlopen; print(urlopen("http://study-web", timeout=3).read().decode())'
Expected: one available Deployment replica, a ReplicaSet, one ready Pod, a Service, and an endpoint for that Pod. The in-cluster request should print the study-page HTML. If it fails, inspect Pod events and the endpoint before continuing. This request checks DNS and the Service path from a Pod. A host port-forward is useful too, but uses a different access path.
Now record the Pod name, delete only this fixture's Pod, and watch replacement:
kubectl --context kind-microbank-advanced -n classroom delete pod -l app=study-web
kubectl --context kind-microbank-advanced -n classroom rollout status deployment/study-web --timeout=180s
kubectl --context kind-microbank-advanced -n classroom get pods -o wide
Explain your observation: a new name is expected because the controller creates a replacement Pod, not because the deleted Pod returns. The desired Deployment replica count remained one. A single replica can be unavailable during replacement; self-repair is not uninterrupted service. Keep this fixture for the next bites.
Translate responsibilities, not file syntax
A Compose service is not automatically equivalent to one Kubernetes object. For MicroBank, describe each responsibility before choosing objects:
| Need | Starting object | What remains your responsibility |
|---|---|---|
| Run Accounts and replace failed Pods | Deployment | Correct image, configuration, and compatible schema |
| Reach Accounts without tracking Pod IPs | ClusterIP Service | Matching selector and port |
| Supply nonsecret settings | ConfigMap | Reload or restart when the app reads only at startup |
| Supply credentials | Secret | Access control, encryption configuration, and rotation |
| Retain database files | PersistentVolumeClaim | Storage availability, backup, and restore |
A Service selects Pods by labels; naming a Deployment does not connect it automatically. A healthy Pod with the wrong Service selector remains unreachable through that Service. Inside a Pod, localhost reaches the network namespace shared by that Pod’s containers; it does not reach your Mac or another Pod.
Inspect the MicroBank contract
The small fixture teaches the same relationships used by MicroBank. The following is a later inspection, once the module lab has created the microbank namespace and workloads:
kubectl --context kind-microbank-advanced -n microbank get deploy,svc,configmap
kubectl --context kind-microbank-advanced -n microbank get endpointslices \
-l kubernetes.io/service-name=accounts
kubectl --context kind-microbank-advanced -n microbank get deploy accounts \
-o jsonpath='{.spec.template.spec.containers[0].image}'
Trace the path from Service port → target port → process listener. Explain why changing a ConfigMap does not rewrite an already-started process's environment. Avoid dumping Secret YAML into your learning notes: base64 is encoding, not confidentiality.
Revision prompt
A Pod responds to its health endpoint but the Service has no usable endpoints. Inspect labels, readiness, and target ports before rebuilding the image. What evidence would distinguish each cause? The lab's health endpoint confirms a responding process; it does not prove that a deposit reached Ledger.
Revision answer: no selected addresses suggests comparing Service selectors with Pod labels; selected but unready addresses point to readiness and startup evidence. Ready endpoints with failed connections lead to targetPort, the actual listener, and network policy/routing. A wrong numeric target port can leave endpoints present while traffic fails. Do not use endpoint count alone to clear the whole path. Next, separate readiness, storage, and resource allocation in Storage, probes, and resource requests.
Sources
Deployments↗, Services↗, and ConfigMaps↗.
Your notes and evidence
Record observations, questions, or links to your work. Keep credentials out of your notes.
Back up or restore this path
Progress and notes stay in this browser. A backup contains only this learning path.