In this lesson: Write a Deployment, Service and Ingress and explain how a request reaches a pod.
Kubernetes has dozens of resource types. Four of them account for most day-to-day work.
Pod — the unit that runs
A pod is one or more containers scheduled together, sharing a network address and storage. Almost always it is one container; the exception is a helper — a log shipper, a proxy — that must live beside the main process.
You rarely write a pod directly. Pods are disposable: they are created, killed and replaced constantly, and each one gets a new address. Anything that assumes a pod is long-lived is wrong.
Deployment — how many, and of what
apiVersion: apps/v1
kind: Deployment
metadata:
name: yanjye-web
spec:
replicas: 4
selector:
matchLabels:
app: yanjye-web
template:
metadata:
labels:
app: yanjye-web
spec:
containers:
- name: app
image: ghcr.io/yanjye/app:a1b2c3d
ports:
- containerPort: 9000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 512Mi
readinessProbe:
httpGet: { path: /health, port: 9000 }
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet: { path: /health, port: 9000 }
initialDelaySeconds: 30
periodSeconds: 20
Change the image tag and apply: Kubernetes performs a rolling update, bringing up new pods and removing old ones only as the new ones become ready. Change replicas and it scales. Delete a pod and it is replaced.
The two probes do different jobs
| Probe | Question | Failure means |
|---|---|---|
| readiness | Can this pod serve traffic right now? | Removed from the load balancer. Not restarted. |
| liveness | Is this pod broken beyond recovery? | The container is killed and restarted. |
| startup | Has it finished starting yet? | Holds the other two off for slow starters. |
Service — a stable name in front of moving pods
apiVersion: v1
kind: Service
metadata:
name: yanjye-web
spec:
selector:
app: yanjye-web
ports:
- port: 80
targetPort: 9000
Pods come and go with new addresses; the Service name does not change. It resolves in cluster DNS and load balances across the pods whose labels match its selector — and only the ones passing readiness. That link between the readiness probe and the load balancer is what makes zero-downtime rollouts work.
Ingress — the way in from outside
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: yanjye
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts: [yanjye.com]
secretName: yanjye-tls
rules:
- host: yanjye.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: yanjye-web
port: { number: 80 }
One controller holds the public addresses, terminates TLS, and routes by hostname and path to the right Service. It is the reverse proxy from the last track, driven by cluster configuration instead of a hand-edited file.
The full path of a request
Browser
→ DNS: yanjye.com resolves to the ingress controller's load balancer
→ Ingress controller: terminates TLS, matches host and path
→ Service: picks a ready pod
→ Pod: your container answers
When something is broken, walk that list. kubectl get ingress, then kubectl get endpoints yanjye-web — an empty endpoints list means no pod is ready, and that is a readiness or label problem, not a networking one.
Commands you will use constantly
kubectl get pods -o wide
kubectl describe pod yanjye-web-abc123 # events at the bottom — read these first
kubectl logs -f deploy/yanjye-web
kubectl logs yanjye-web-abc123 --previous # the crashed container's output
kubectl exec -it yanjye-web-abc123 -- sh
kubectl get events --sort-by=.lastTimestamp
kubectl rollout status deploy/yanjye-web
kubectl rollout undo deploy/yanjye-web # back to the previous revision
describe before logs. If a pod never started there are no logs to read. The Events section of describe tells you it could not pull the image, could not be scheduled, or was killed for exceeding memory — the answer to most "it will not start" questions.
Try it yourself
Deploy anything to a local cluster with a Deployment and a Service. Then break it on purpose: change one letter in the Service selector and watch kubectl get endpoints go empty while everything still reports as healthy. That silent failure is the most common Kubernetes mistake there is.