Skip to content

Phase 2: Pods, Deployments, Services

Time: weeks 1-2. Goal: the holy trinity. This is 70% of day-to-day k8s.

Pod

The smallest deployable unit. A Pod wraps one or more containers that share a network namespace and volumes - they see each other on localhost.

  • Why a wrapper instead of bare containers: sidecars (log shippers, proxies) need to live next to the main container.
  • You almost never create bare Pods directly. No controller watches them, so they don't survive node failures or deletion.
  • Know the lifecycle states: Pending, Running, Succeeded, Failed - and restartPolicy.

Deployment

Your docker compose service equivalent, but with superpowers.

  • Declares "N replicas of this pod template must exist". A ReplicaSet underneath enforces the count.
  • Rolling updates: change the image tag, k8s replaces pods gradually.
  • Rollbacks: kubectl rollout undo deployment/<name>.

The self-healing demo that makes k8s click:

bash
kubectl create deployment web --image=nginx --replicas=3
kubectl get pods -w
# in another terminal: kill one
kubectl delete pod <one-of-them>
# watch it resurrect

Service

Pods are mortal and their IPs change. A Service gives a stable virtual IP and DNS name in front of a moving set of pods.

  • ClusterIP (default): internal only, like compose service names.
  • NodePort: opens a port on every node. Mostly for local testing.
  • LoadBalancer: provisions a cloud load balancer. Costs money per service.

How a Service finds its pods: label selectors. The Service selects app: web, every pod with that label gets traffic. Labels are the duct tape of all of k8s - internalize this. Nothing is linked by name; everything is linked by label.

Cluster DNS

Every Service gets a DNS name: my-service.my-namespace.svc.cluster.local. Within the same namespace, apps just use http://my-service. This is why compose-style inter-service URLs often survive migration unchanged.

Namespaces

Lightweight isolation, like compose project names. Use them to separate apps or environments on one cluster.

YAML anatomy

Every manifest has the same four top-level fields:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  # the resource-specific part

Use kubectl explain deployment.spec (and deeper, e.g. deployment.spec.template.spec.containers) instead of googling field names.

Exercise

Full playbook with solutions, expected output, and a load-balancing demo: Lab 2.

Take the traefik/whoami image and, by hand, no copy-paste:

  1. Write a Deployment with 3 replicas.
  2. Write a ClusterIP Service in front of it.
  3. kubectl apply both, port-forward to the Service, verify.
  4. Do a rolling update to a new image tag, watch with kubectl rollout status.
  5. Roll it back with kubectl rollout undo.

Checkpoint

  • You can explain Pod vs ReplicaSet vs Deployment and why the layers exist.
  • You can explain how a Service knows which pods to send traffic to.
  • You wrote a working Deployment + Service from a blank file.

Next: Phase 3: Configuration and State

A VineLab lab. Released under the MIT License.