Skip to content
← Advanced DevOps

Learning bite

Deployments, Services, and configuration

Separate replaceable workloads from stable access and runtime inputs.

Documentation reviewed2026-10-01 · 5 min read
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.

yaml
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.

bash
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:

bash
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:

NeedStarting objectWhat remains your responsibility
Run Accounts and replace failed PodsDeploymentCorrect image, configuration, and compatible schema
Reach Accounts without tracking Pod IPsClusterIP ServiceMatching selector and port
Supply nonsecret settingsConfigMapReload or restart when the app reads only at startup
Supply credentialsSecretAccess control, encryption configuration, and rotation
Retain database filesPersistentVolumeClaimStorage 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:

bash
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.

Loading saved progress…

Back up or restore this path

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