A Pod is one or more containers that share a network namespace and storage; it is the smallest thing Kubernetes schedules. Pods are mortal: they are not restarted on another node when their node dies. You almost never create bare pods. A Deployment declares how many identical pods you want and manages updates, using a ReplicaSet underneath.
Versions in use on this date. Kubernetes 1.21 (released 8 April 2021). apps/v1 is the stable Deployment API; the older extensions/v1beta1 and apps/v1beta1/2 versions are long gone.
Ownership chain
flowchart TB
D[Deployment web<br/>desired state and strategy] --> RS1[ReplicaSet web-7d9f<br/>current version]
D -.->|kept for rollback| RS0[ReplicaSet web-5b8c<br/>previous version scaled to 0]
RS1 --> P1[Pod web-7d9f-aaa]
RS1 --> P2[Pod web-7d9f-bbb]
RS1 --> P3[Pod web-7d9f-ccc]
SVC[Service web<br/>selector app=web] --> P1
SVC --> P2
SVC --> P3
HPA[HorizontalPodAutoscaler] -.->|changes replicas| D
The Service finds pods through labels, not names, so the labels in the pod template and the Service selector must match.
A realistic Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: shop
labels: { app: web }
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels: { app: web } # immutable after creation
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
template:
metadata:
labels: { app: web, version: "1.4.2" }
spec:
terminationGracePeriodSeconds: 30
containers:
- name: web
image: registry.example.com/shop/web:1.4.2 # never :latest
ports: [{ containerPort: 8080, name: http }]
env:
- name: LOG_LEVEL
value: info
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { memory: 256Mi }
readinessProbe:
httpGet: { path: /healthz, port: http }
periodSeconds: 5
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
Points worth explaining:
selectorand template labels tie the Deployment to its pods; you cannot change the selector later.maxSurge: 1, maxUnavailable: 0replaces pods one at a time without reducing capacity: a new pod must become Ready before an old one is removed.- Requests are what the scheduler reserves; limits are caps. Memory limits kill the container when exceeded (OOMKilled); CPU limits throttle.
- Readiness probe: the pod receives Service traffic only when ready (the probes post goes deeper).
- securityContext: run as non-root, no privilege escalation, read-only root filesystem. These are cheap and prevent many attacks; PodSecurityPolicy is being deprecated, so apply them in the manifest and plan for Pod Security Admission later.
Operate it
kubectl create namespace shop
kubectl apply -f deployment.yaml
kubectl -n shop get deploy,rs,pods -l app=web -o wide
kubectl -n shop rollout status deployment/web
kubectl -n shop scale deployment/web --replicas=5
# update the image (or edit the manifest and re-apply)
kubectl -n shop set image deployment/web web=registry.example.com/shop/web:1.4.3
kubectl -n shop rollout status deployment/web
kubectl -n shop rollout history deployment/web
kubectl -n shop rollout undo deployment/web # back to the previous revision
kubectl -n shop rollout restart deployment/web # fresh pods with the same spec
kubectl -n shop rollout pause deployment/web # batch several changes, then resume
Prefer committing the manifest to Git and applying it from CI over using set image by hand, so the repository stays the source of truth (kubectl diff -f previews changes).
How pods behave
- A pod gets its own IP, but the IP changes on every recreation. Use Services for stable access.
- Containers in a pod share
localhost, which is why sidecars (log shippers, proxies) work. - Termination: Kubernetes sends SIGTERM, waits
terminationGracePeriodSeconds, then SIGKILL. Handle SIGTERM and finish in-flight requests. ApreStopsleep of a few seconds avoids dropping traffic while endpoints update. - A pod that never becomes Ready blocks the rollout;
kubectl describe podshows why. restartPolicy: Always(the default for Deployments) restarts containers on the same node with exponential back-off (CrashLoopBackOff).
Troubleshooting a rollout
kubectl -n shop describe deployment web | sed -n '/Conditions/,$p'
kubectl -n shop get pods -l app=web
kubectl -n shop logs deployment/web --tail=50
kubectl -n shop get events --sort-by=.lastTimestamp | tail
Stuck rollouts usually mean a failing readiness probe, an image that cannot be pulled, or requests larger than any node can provide. Set progressDeadlineSeconds (default 600) so CI fails clearly, and use kubectl rollout undo to recover quickly.