Kubernetes Introduction: Pods, Deployments, and Services
- Explain Kubernetes as a declarative control loop that drives actual state toward desired state
- Distinguish the core objects: Pod, Deployment/ReplicaSet, and Service
- Read and apply a manifest with kubectl, and inspect what the cluster did
- Know why you deploy Deployments, not bare Pods, and why Services exist
The big idea: declare desired state, let the loop reconcile
Understand Kubernetes as a reconciliation loop, not a script runner.
Kubernetes (k8s) is a container orchestrator: it runs and keeps containers running across a fleet of machines (nodes). Its defining idea is declarative desired state. You don't tell it how to do things step by step; you declare what you want — 'three replicas of this image, reachable on port 80' — and the cluster's control loop continuously compares actual state to desired state and takes action to close the gap.
A node dies and takes your containers with it? The loop notices the replica count dropped and schedules replacements elsewhere. You ask for 5 replicas instead of 3? It starts 2 more. This self-healing, level-triggered reconciliation — not one-shot commands — is what makes k8s different from
docker run, and it shapes everything else in this lesson.It helps to know who runs that loop. A cluster has a control plane and a set of worker nodes. The control plane holds the API server (the single front door — everything, including
kubectl, talks to it), etcd (the consistent key-value store that records desired state), the scheduler (decides which node a new Pod lands on), and the controllers (the loops that reconcile). Each worker node runs a kubelet (starts and health-checks the containers the API server assigns it) and a container runtime. When youkubectl applya manifest, you're writing your desired state into etcd via the API server; the controllers and scheduler then make the worker nodes match it. Nothing you run imperatively 'on a node' — the whole system is mediated through that declarative API, which is what makes it auditable and self-healing.Pods: the smallest deployable unit
Understand what a Pod is and why it's rarely created directly.
The smallest thing k8s schedules is not a container but a Pod: one or more containers that share a network namespace (same IP and port space) and can share storage volumes. Most Pods hold a single application container, sometimes with a helper 'sidecar'. Pods are ephemeral and disposable — they get a cluster-internal IP, but if a Pod dies it is replaced by a new Pod with a new IP, never resurrected.
Because of that churn you almost never create bare Pods yourself. You declare a higher-level object (a Deployment) that manages Pods for you — creating, replacing, and scaling them to match desired state.
Example — not runnable on your lab box
kubectl get podsNAME READY STATUS RESTARTS AGE web-7d9f8c6b5f-2xk4p 1/1 Running 0 3m web-7d9f8c6b5f-l7m2q 1/1 Running 0 3m web-7d9f8c6b5f-qp8vn 1/1 Running 0 3m
Three Pods, each with a generated name (the Deployment's name + a ReplicaSet hash + a random suffix). READY 1/1means the single container in each Pod is up;STATUS RunningandRESTARTS 0show health. These names change whenever Pods are replaced — which is exactly why clients must not target Pod IPs directly.Deployments: declarative replicas and rollouts
Use a Deployment to keep N replicas and roll out updates safely.
A Deployment is the object you actually manage. It records your desired replica count and Pod template, and it owns a ReplicaSet that enforces 'keep exactly N Pods matching this template alive.' Change the image in the Deployment and it performs a rolling update: it brings up new Pods and retires old ones gradually, so the app stays available. If a rollout goes bad,
kubectl rollout undosteps back to the previous ReplicaSet.Scaling is just editing desired state: set replicas to 5 and the ReplicaSet creates two more Pods to match.
Example — not runnable on your lab box
kubectl scale deployment web --replicas=5 kubectl get deployment webdeployment.apps/web scaled NAME READY UP-TO-DATE AVAILABLE AGE web 5/5 5 5 6m
kubectl scaleedits the Deployment's desired replica count; the control loop reconciles by creating Pods untilREADY 5/5.UP-TO-DATEis how many Pods match the current template (relevant during a rollout) andAVAILABLEhow many are passing readiness — all 5 here.Check yourself
You need three copies of your app to stay running and to survive a node failure. Which Kubernetes object do you create — a bare Pod, or a Deployment?
Services: a stable address for moving targets
Expose ephemeral Pods behind one stable name and IP.
Pods come and go with ever-changing IPs, so you never connect to a Pod directly. A Service gives a set of Pods (selected by labels) a single stable virtual IP and DNS name, and load-balances connections across the healthy Pods behind it. Your frontend talks to
http://web(the Service); k8s routes each request to one of the currentwebPods, whatever their IPs happen to be right now.Common Service types: ClusterIP (default — reachable only inside the cluster), NodePort (opens a port on every node), and LoadBalancer (asks the cloud for an external load balancer).
Example — not runnable on your lab box
kubectl get service webNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web ClusterIP 10.96.140.22 <none> 80/TCP 6m
The Service webhas a stable ClusterIP (10.96.140.22) and DNS name (web) that never changes even as the Pods behind it are replaced.EXTERNAL-IP <none>because ClusterIP is internal-only; a LoadBalancer type would show an external address here. Behind the scenes the Service keeps an up-to-date list of healthy Pod IPs (its endpoints) as Pods come and go, and the node's networking layer rewrites traffic aimed at the ClusterIP to one of those endpoints. So the indirection you get is real load balancing with automatic membership — add replicas and they start receiving traffic with no client change; drain a Pod and it stops — all keyed off the label selector, never off any fixed address.Manifests and kubectl: declare it in YAML, apply it
Read a manifest and apply/inspect it with kubectl.
You declare objects in YAML manifests and hand them to the cluster with
kubectl apply -f.applyis declarative and idempotent — it makes the live object match the file, creating it the first time and updating it thereafter, so the manifest in git is the source of truth.kubectl getlists objects, andkubectl describe/kubectl logsare your first debugging stops.Two concepts tie the objects together. Labels are arbitrary key/value tags on objects (
app: web), and selectors match on them — that's the loose coupling by which a Deployment claims its Pods and a Service finds its backends; there are no hard-wired references, just labels that agree. Namespaces partition a cluster into virtual sub-clusters (default,kube-system, and your own likestaging/prod) so names only need to be unique within a namespace and you can scope access and quotas per team. When a Pod won't start, the workflow is almost always the same:kubectl get podsto see the STATUS (Pending,CrashLoopBackOff,ImagePullBackOffeach point at a different cause), thenkubectl describe pod <name>to read the Events at the bottom, thenkubectl logs <name>for what the container itself said. That trio resolves the large majority of 'why is my app not up' questions.Example — not runnable on your lab box
cat web-deploy.yamlapiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 3 selector: matchLabels: { app: web } template: metadata: labels: { app: web } spec: containers: - name: web image: ghcr.io/acme/web:1.4.2 ports: - containerPort: 80A Deployment manifest: kindnames the object type,spec.replicasthe desired count,selector.matchLabelswhich Pods it owns, andtemplatethe Pod blueprint (labels must match the selector, and the image is pinned to an immutable tag).kubectl apply -f web-deploy.yamlreconciles the cluster to this file.
- Kubernetes is a declarative control loop: you state desired state and it continuously reconciles actual state toward it (self-healing, rolling updates).
- A Pod is the smallest, ephemeral unit; you rarely create bare Pods — a Deployment (via a ReplicaSet) keeps N replicas alive and rolls out updates.
- A Service gives label-selected Pods one stable IP/DNS name and load-balances to them, so clients never target ever-changing Pod IPs.
- Declare objects in YAML manifests and reconcile with kubectl apply -f; inspect with kubectl get/describe/logs.
