Section 2 · Vocabulary With Proof

Every K8s object, pointed at a real one

Definitions don't stick; running examples do. Each object below is introduced with the instance living in the QuickBites cluster — real names, real output, exactly what you'd meet on the job.

🫛Pod — the unit that actually runs

A pod is one or more containers that share a network identity (one IP) and lifecycle. You almost never create pods by hand — controllers (Deployments, StatefulSets, DaemonSets) create them for you.

real pods in orders-prod
$ kubectl get pods -n orders-prod -o wide
NAME                                    READY   STATUS    RESTARTS      AGE   IP               NODE
mongodb-0                               1/1     Running   1 (6h ago)    46d   10.244.194.68    node-1
orders-backend-84d8667cdc-hwtgx    1/1     Running   1 (6h ago)    46d   10.244.194.70    node-1
orders-backend-84d8667cdc-zm2xs    1/1     Running   1 (6h ago)    46d   10.244.194.72    node-1
orders-frontend-79d7f88fcc-4ldl4   1/1     Running   7 (6h ago)    46d   10.244.194.126   node-1
orders-frontend-79d7f88fcc-9hz57   1/1     Running   7 (6h ago)    46d   10.244.194.125   node-1

Notice the two name shapes: mongodb-0 (a StatefulSet pod — stable, numbered) versus orders-backend-84d8667cdc-hwtgx (a Deployment pod — deployment-name + ReplicaSet hash + random suffix, disposable by design). The 10.244.x.x addresses are pod IPs handed out by Calico.

🎛Deployment → ReplicaSet → Pods

A Deployment says "keep N copies of this pod template running, and when the template changes, roll to the new version gradually." It does this through a middleman, the ReplicaSet:

What kubectl set image actually triggers.
Deployment orders-backend (replicas: 2) ReplicaSet · 84d8667cdc (new) image: …backend-v1.1.0 · desired 2 scaled UP during a rollout ReplicaSet · 75fc56966 (old) image: …backend-dev · desired 0 kept around → enables instant rollback pod …-hwtgx Running pod …-zm2xs Running A rollout = new ReplicaSet scales 0→2 while the old one scales 2→0, one pod at a time (maxSurge:1, maxUnavailable:0)

See it live — both apps still keep their old -dev ReplicaSets at desired 0:

kubectl
$ kubectl get rs -n orders-prod
NAME                              DESIRED   CURRENT   READY   AGE
orders-backend-75fc56966     0         0         0       56d   ← old version, parked
orders-backend-84d8667cdc    2         2         2       55d   ← live
orders-frontend-79d7f88fcc   2         2         2       55d
orders-frontend-967c94788    0         0         0       56d

🔌Service — a stable address for moving targets

Pods die and get new IPs. A Service gives a fixed virtual IP + DNS name and load-balances to whatever pods currently match its selector. This cluster shows all three types you need to know:

real services in orders-prod
$ kubectl get svc -n orders-prod
NAME                   TYPE        CLUSTER-IP      PORT(S)           SELECTOR
mongodb                ClusterIP   None            27017/TCP         app=mongodb          ← "headless" (§5)
mongodb-external       NodePort    10.106.38.9     27017:30017/TCP   app=mongodb
orders-backend    ClusterIP   10.97.145.114   8000/TCP          app=orders,tier=backend
orders-frontend   NodePort    10.108.87.195   80:30082/TCP      app=orders,tier=frontend
TypeReachable fromReal example here
ClusterIP (default)Only inside the cluster orders-backend — the frontend nginx proxies API calls to it by DNS name
NodePortAny node IP, port 30000–32767 orders-frontend on 30082, Grafana on 30300
Headless (clusterIP: None)DNS returns pod IPs directly mongodb — required by the StatefulSet, explained in Section 5
The DNS name pattern to memorise <service>.<namespace>.svc.cluster.local — the orders backend finds its database via mongodb.orders-prod.svc.cluster.local (that exact string is in the orders-config ConfigMap; go look).

🚪Ingress — hostname routing at the edge

NodePorts work but nobody wants :30082 in a URL. The Ingress object maps hostnames to Services, and the ingress-nginx controller (a pod!) does the actual routing:

kubectl
$ kubectl get ingress -A
NAMESPACE              NAME                      CLASS   HOSTS                       ADDRESS         PORTS
orders-prod       orders-ingress       nginx   orders.quickbites.io       192.0.2.11   80
riders-prod   riders-ingress   nginx   riders.quickbites.io   192.0.2.11   80

The rule inside orders-ingress reads: Host orders.quickbites.io, path / → Service orders-frontend port 80. Outside the cluster, orders.quickbites.io is just a DNS record pointing at the ingress controller's address — DNS gets the request to the right door, the Ingress rules route it inside.

⚙️ConfigMap & Secret — config out of the image

ConfigMap · non-sensitive

Plain key-value settings, injected as env vars via envFrom. The real one:

orders-config (excerpt)
MONGO_HOST: "mongodb.orders-prod.svc.cluster.local"
MONGO_PORT: "27017"
MONGO_DATABASE: "orders_db"
APP_ENV: "prod"
LOG_LEVEL: "info"
Secret · sensitive

Same idea, but base64-encoded and access-controlled. The backend pods pull DB credentials from mongodb-secret / mysql-secret like this:

deployment excerpt
env:
- name: MONGO_PASSWORD
  valueFrom:
    secretKeyRef:
      name: mongodb-secret
      key: MONGO_PASSWORD

The values themselves are in your Secret / values.yaml — never in this doc, never in git, never in chat.

base64 is NOT encryption kubectl get secret mongodb-secret -o yaml shows base64 text that anyone can decode with base64 -d. Secrets protect against casual exposure and enable RBAC — treat cluster access itself as the real security boundary.

💾PersistentVolume & PersistentVolumeClaim

Containers lose their filesystem when they restart. Databases obviously can't accept that, so Kubernetes splits storage into the PV (the actual disk — here, a directory on a specific node) and the PVC (a pod's request to use one):

kubectl
$ kubectl get pv
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM
grafana-pv      2Gi        RWO            Retain           Bound    monitoring/grafana-pvc
loki-pv         5Gi        RWO            Retain           Bound    monitoring/loki-pvc
mongodb-pv      5Gi        RWO            Retain           Bound    orders-prod/mongodb-pvc
mysql-pv        5Gi        RWO            Retain           Bound    riders-prod/mysql-pvc
prometheus-pv   5Gi        RWO            Retain           Bound    monitoring/prometheus-pvc

👥DaemonSet — one pod per node

Some things must run on every node. This cluster has two perfect examples in monitoring: node-exporter (hardware metrics) and promtail (log shipping). Count them — three of each, one per node:

kubectl
$ kubectl get pods -n monitoring -o wide | grep -E 'node-exporter|promtail'
node-exporter-m9db4   1/1   Running   192.0.2.10   control-plane
node-exporter-pn54r   1/1   Running   192.0.2.12   node-2
node-exporter-xb9bj   1/1   Running   192.0.2.11   node-1
promtail-256k9        1/1   Running   10.244.126.29   node-2
promtail-fs6qx        1/1   Running   10.244.194.71   node-1
promtail-kzkk4        1/1   Running   10.244.235.215  control-plane

🏷Labels & selectors — the glue

Nothing in Kubernetes holds a list of pods. Everything selects pods by labels. The orders frontend Service says selector: app=orders, tier=frontend — any pod wearing those two labels instantly starts receiving traffic. That is the entire mechanism behind Services, Deployments and rollouts. Try it:

kubectl
$ kubectl get pods -n orders-prod -l tier=backend
NAME                                   READY   STATUS    RESTARTS   AGE
orders-backend-84d8667cdc-hwtgx   1/1     Running   1          46d
orders-backend-84d8667cdc-zm2xs   1/1     Running   1          46d
🧠

Checkpoint — objects

1. You delete pod orders-backend-84d8667cdc-hwtgx. What happens?
Deployment pods are cattle: the ReplicaSet immediately reconciles back to 2 replicas with a fresh pod (new suffix, possibly a new node, new IP). Compare with mongodb-0, which comes back with the same name — that's Section 5.
2. The backend reaches MongoDB at mongodb.orders-prod.svc.cluster.local. Where is that hostname configured?
Config lives outside the image so the same image can run in test (docker-compose, where MONGO_HOST is a container name) and prod (K8s service DNS) without a rebuild.
3. The PVs here use Retain. You delete mongodb-pvc by accident. Is the data gone?
With Retain, the data survives at /data/… on the node. (With reclaim policy Delete — common in clouds — it would be gone.)
4. Why does this cluster run exactly 3 promtail pods?
DaemonSets scale with the cluster. Add a 4th node tomorrow and a 4th promtail (and node-exporter, and calico-node, and kube-proxy) appears automatically.
🔓 You're reading a free chapter of Kubernetes — the first two are open.
Unlock the rest of this course with a one-time payment.
Unlock this course →
🔓 See course prices