In the previous chapter, the cluster became Ready. Now let's put something inside it.
The temptation is to create a pod directly. Resist. A pod is ephemeral: if it dies, no one recreates it. The right level for an application is the Deployment, which creates a ReplicaSet, which creates the pods.
Deployment -> ReplicaSet -> Pod(s)
The path of kubectl apply
When you run:
kubectl apply -f app.yaml
This is what happens:
kubectlreads the YAML and sends it to the API server.- The API server validates and stores it in etcd.
- The controller manager sees the Deployment and creates a ReplicaSet.
- The ReplicaSet sees that pods are missing and creates Pod objects.
- The scheduler chooses a node for each pod.
- The kubelet on the node downloads the image and starts the container.
- The CNI assigns an IP to the pod.
- The kube-proxy updates the Service rules.
If something fails at any step, the state appears in kubectl get and kubectl describe. Learning to read these two commands is worth more than memorizing YAML.
A complete Deployment
Create app.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
Apply it:
kubectl apply -f app.yaml
kubectl get deploy,rs,pods -o wide
You should see 3 pods Running and Ready.
Why labels and selector?
The Deployment does not look for pods by name. It selects by label. The selector.matchLabels must match template.metadata.labels. If they don't match, the Deployment doesn't find the pods and the cluster complains. This is one of the most common YAML mistakes.
The Service
Pods are born and die with different IPs. The Service gives a name and a stable IP:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
type: ClusterIP
kubectl apply -f service.yaml
kubectl get svc web
Test from inside the cluster:
kubectl run curl --image=curlimages/curl:8.10.1 --rm -it --restart=Never -- \
curl -s http://web
ClusterIP is only accessible within the cluster. For external access, we use NodePort, LoadBalancer or Ingress — chapter 09 covers this.
To test locally without exposing anything:
kubectl port-forward svc/web 8080:80
# in another terminal
curl -I http://localhost:8080
Rollout and rollback
Update the image:
kubectl set image deployment/web web=nginx:1.28
kubectl rollout status deployment/web
kubectl rollout history deployment/web
If the new version breaks:
kubectl rollout undo deployment/web
kubectl rollout status deployment/web
Kubernetes keeps old ReplicaSets for rollback. This is the revisionHistoryLimit.
Scaling
kubectl scale deployment/web --replicas=5
kubectl get pods -l app=web
In production, what scales by itself is the HPA (Horizontal Pod Autoscaler), which we will see in season 3. Manual scaling is great for understanding; HPA is for operating.
Probes: what separates “running” from “ready”
- readinessProbe: can the pod receive traffic?
- livenessProbe: is the container alive? If it fails, the kubelet restarts it.
- startupProbe: for slow applications; holds liveness until the app starts.
A Java application with Spring Boot can take 90–120 seconds to start. If the liveness has initialDelaySeconds: 10, the kubelet kills the container before it finishes — and you enter CrashLoopBackOff with no application error. I've seen this happen in a real deployment; the log showed the app starting normally and being killed.
Rule of thumb:
- readiness: generous at first, shorter later.
- liveness: only after the app provably starts.
- startup: ideal for slow apps.
Diagnostics: the three states you will see
Pending
The pod was created, but not scheduled.
kubectl describe pod <pod> | tail -30
Causes: insufficient resources, impossible nodeSelector/affinity, taint without toleration, unbound PVC.
ImagePullBackOff / ErrImagePull
The node failed to download the image.
kubectl describe pod <pod> | grep -A5 Events
Causes: non-existent image/tag, private registry without imagePullSecret, Docker Hub rate limit, or image removed from the registry.
Real case: an image that existed only in cache on two nodes failed when the pod was rescheduled to a third. The cluster was “working” until the day the node changed. The image needs to be available in the registry (or on all nodes), not just in cache.
CrashLoopBackOff
The container starts and dies repeatedly.
kubectl logs <pod>
kubectl logs <pod> --previous
kubectl describe pod <pod> | tail -20
Causes: missing configuration, wrong environment variable, unavailable dependency, permission, OOMKilled (see Last State: OOMKilled).
Cleaning up
kubectl delete -f service.yaml
kubectl delete -f app.yaml
Checklist
- [ ] Understood Deployment → ReplicaSet → Pod.
- [ ] Know that
selectorandlabelsmust match. - [ ] Created a Deployment with 3 replicas and a Service.
- [ ] Tested with
port-forwardand/or internalcurl. - [ ] Did rollout and rollback.
- [ ] Configured readiness and liveness with realistic times.
- [ ] Know how to diagnose
Pending,ImagePullBackOffandCrashLoopBackOff.
Glossary
- ReplicaSet: ensures N pods of a version.
- Service: stable name/IP for a set of pods.
- ClusterIP: internal IP of the Service.
- NodePort: port exposed on each node.
- readinessProbe: defines whether the pod enters load balancing.
- livenessProbe: defines whether the container should be restarted.
- OOMKilled: container killed due to excess memory.
Next
Now that you know how to create and diagnose a deployment, let's use kubeadm to set up the real cluster, with separate control plane and workers.
Next: Kubernetes Course 04 — kubeadm from inside: certificates, static pods and the join
Keep reading
Read also

StorCLI - Complete Cheat Sheet
StorCLI is the official and modern tool for managing Broadcom/LSI RAID controllers. This guide covers essential commands, syntax and use cases.
12 min de leitura
TutoriaisBusiness Services in Nyverra Portal: Translating IT into Business
The business doesn't feel a server go down — it feels email stop. The Business Services (Service Mapping) module of Nyverra ITSM Portal connects services like Email, ERP or PACS to CMDB assets, revealing the real impact of each failure.
5 min de leitura
SegurançaRPO vs RTO: How to Define Before Buying Backup (and Why Most Companies Get It Wrong)
Most companies buy backup asking the wrong question: "how much does it cost?". The right question is: how much data can I lose (RPO) and how long can I be down (RTO). Discover how to define these numbers per system and avoid purchasing protection based on price, not on real need.
9 min de leitura
Comments
No comments yet
Be the first to share your thoughts on this article.

Leave a comment