5.1 Pod Anatomy: Spec, Status, and Metadata
Alright, let’s get our hands dirty. If a Pod is the atom of your Kubernetes universe, then we need to pull out our metaphorical electron microscope and look at its constituent parts. Don’t worry, it’s less “quantum physics” and more “well-labeled lunchbox.”
Every Pod you define is essentially a request slip you hand to the Kubernetes API server. This request is embodied in a YAML or JSON manifest. And that manifest is built on three fundamental pillars: the metadata, the spec, and the status. You control the first two. Kubernetes controls the last one, and it will very loudly tell you when you’ve screwed up the first two.
The metadata Section: Who Are You?
This is the Pod’s ID card. It’s not about what the Pod does, but who it is and where it belongs. The two most important fields here are name and namespace.
name: This one’s obvious. It’s a unique (within a namespace) identifier for your Pod. Keep it descriptive.nginxis okay for a test;user-profile-service-bff-v2is better for reality. Pro tip: You can’t use capitals. Kubernetes is famously case-sensitive and lowercase-snobby.namespace: This is the Pod’s neighborhood. It’s a way to partition your cluster. If you don’t specify one, it plops itself into thedefaultnamespace, which is like storing your fine china in the garage. Create namespaces for logical groupings of your apps (production,staging,team-a).
You’ll also see labels and annotations here. They are crucial. Labels are for Kubernetes itself to identify groups of Pods (e.g., “all Pods with the label app=user-api”). Annotations are for you to attach arbitrary metadata that Kubernetes ignores, like a build timestamp, a git commit SHA, or the name of the dev who last touched it (for blame purposes, obviously).
apiVersion: v1
kind: Pod
metadata:
name: my-awesome-app-pod
namespace: production
labels:
app: my-awesome-app
tier: backend
annotations:
commitSha: a1b2c3d4e5f6
deployedBy: "alice"
# ... rest of the Pod definition comes next
The spec Section: What’s For Lunch?
This is the meat of the Pod. The spec (short for specification) is your desired state. You’re telling Kubernetes exactly what you want this Pod to be. This is where you define the containers, volumes, networking, and all the fun stuff.
The most important field here is containers[], a list of container definitions. Yes, list. A Pod can run more than one container, and they share the same network namespace and can communicate over localhost. This is primarily used for tightly-coupled “helper” containers, like a web server paired with a log-shipping sidecar. For 90% of your workloads, you’ll have one container per Pod. The designers made a questionable choice here by over-promoting this multi-container pattern; it often leads to people smashing things together that don’t belong together, creating a distributed monolith in a box. Use it sparingly.
Within a container spec, you’ll define the image, ports, and resources.
spec:
containers:
- name: web-server # Name *inside* the Pod
image: nginx:1.25-alpine # Always tag your images. `:latest` is a trap.
ports:
- containerPort: 80 # This is mostly informational. It doesn't "open" a port.
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
# You could define another container here for a sidecar, etc.
- name: log-shipper
image: fluentd:latest # See? I used `latest`. Do as I say, not as I do.
The resources block is not optional. It is your contract with the cluster scheduler. requests is what the scheduler uses to find a node with enough space for your Pod. limits is the hard ceiling. If your container exceeds its memory limit, Kubernetes murders it with an OOMKilled error. If it exceeds CPU, it just gets throttled. Not setting these is the number one cause of “mysterious” node failures and evictions. Don’t be that person.
The status Section: What Actually Happened?
You don’t write this. Kubernetes does. The status is the actual state of the Pod. It’s the universe’s report card on your spec. This is where you go to figure out why your brilliant spec is currently sitting in a broken heap.
It contains goldmines of information:
phase: A high-level summary: Pending, Running, Succeeded, Failed, or Unknown. It’s useful for a quick glance but too vague for real diagnosis.conditions: This is where the real truth lies. An array of detailed conditions with timestamps. Look forContainersReadyandPodReadyto beTrue.containerStatuses: The nitty-gritty for each container. Itsstate(waiting,running,terminated) and, most importantly, itslastState. If your container keeps crashing and restarting, thelastState.terminated.exitCodeandreason(oftenErrororOOMKilled) here will tell you why.
To see the gory details, use kubectl describe pod <pod-name>. This command mashes together your spec and the generated status into the most useful troubleshooting report you’ll get. If your Pod is stuck in Pending, the Events section at the bottom of describe will tell you if it’s because you asked for 64Gi of memory on a node with only 16Gi. It’s Kubernetes’ way of saying, “Nice try, buddy.”
The status is your constant feedback loop. You declare your desired reality in spec, and Kubernetes reports back on the messy, complicated actuality in status. Learning to read this is what separates you from someone who just copies YAML from the internet.