Writing your first Pod
Time to write YAML. One small file, every line explained, then the five kubectl commands you will use on Pods every day.
The Pod file, line by line
Create pod.yaml with this content. Click any highlighted part to see what it means:
apiVersion: v1
kind: Pod
metadata:
name: posts-pod
spec:
containers:
- name: posts
image: micro-posts:1.0
ports:
- containerPort: 4000
apiVersion: v1
Which version of the Kubernetes API this object uses. Pods, Services and ConfigMaps are in the core group, v1. You will see apps/v1 for Deployments. Get it wrong and kubectl refuses the file.
kind: Pod
What you are creating. Later: kind: Deployment, kind: Service. Together, apiVersion + kind tell Kubernetes exactly which type of object this is.
metadata
Information about the object. Here only its name: posts-pod. That is the name you see in kubectl get pods. Labels also go here later.
spec
The desired configuration: "what should this Pod look like and how should it run?" Every object has a spec; it is the declarative part.
containers:
A list of the containers inside the Pod. Each item starts with -. One item = one container. Add a second item for a sidecar.
- name: posts
The name of the container, not the Pod. Pod name = posts-pod, container name = posts. You use the container name with kubectl logs posts-pod -c posts when a Pod has several containers.
image: micro-posts:1.0
The image to run: the one you built with docker build -t micro-posts:1.0 . Always give a tag; see the local image trap for why.
containerPort: 4000
Documents that the app listens on 4000, matching app.listen(4000) in your Node code. It does not expose the app to your laptop or the internet, unlike docker run -p. A Service does that.
The shape to remember
Do not memorise every option. Remember the skeleton and what each part answers:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-imageapiVersion → which Kubernetes API?
kind → what am I creating?
metadata → what is it called?
spec → what should it look like?
containers → what runs inside it?
image → which image?Environment variables, resource limits, probes, volumes and more containers all get added inside this same shape later.
Create and inspect it
Click through the commands in order. Output is from a typical cluster; names and times will differ.
$ kubectl apply -f pod.yaml
pod/posts-pod createdSend the file to Kubernetes: "this is the Pod I want". Run it again unchanged and you get pod/posts-pod unchanged.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
posts-pod 1/1 Running 0 10sThe quick overview. Add -o wide to also see the Pod IP and the node it runs on.
$ kubectl describe pod posts-pod
Name: posts-pod
Namespace: default
Node: lab-control-plane/172.18.0.2
Status: Running
IP: 10.244.0.5
Containers:
posts:
Image: micro-posts:1.0
Port: 4000/TCP
State: Running
Ready: True
Restart Count: 0
Conditions:
Ready True
Events:
Type Reason Message
Normal Scheduled Successfully assigned default/posts-pod to lab-control-plane
Normal Pulled Container image "micro-posts:1.0" already present on machine
Normal Created Created container posts
Normal Started Started container postsThe detailed view, and your first debugging tool. When a Pod is stuck, read the Events at the bottom first.
$ kubectl logs posts-pod
Posts service started on 4000Whatever your app prints with console.log. Same idea as docker logs posts-container. Add -f to follow, --previous to see the logs of a container that crashed.
$ kubectl exec -it posts-pod -- sh
/app # ls
node_modules package.json index.js
/app # env | grep HOSTNAME
HOSTNAME=posts-pod
/app # exitA shell inside the container, like docker exec -it posts-container sh. Note the --: everything after it is the command to run in the container.
kubectl get pods for a quick overview, kubectl describe pod posts-pod for the detail.What READY 1/1 means
READY is ready containers / total containers in the Pod.
NAME READY STATUS
posts-pod 1/1 RunningPod └── posts ✓ — 1 of 1 containers ready.
NAME READY STATUS
posts-pod 2/2 RunningPod ├── posts ✓ └── logging ✓ — both ready.
NAME READY STATUS
posts-pod 1/2 RunningPod ├── posts ✓ └── logging ✗ — the Pod is Running, but not every container is ready. A Service only sends traffic to Pods that are fully ready, so 1/2 is worth a kubectl describe.
The local image trap
You built micro-posts on your laptop. The cluster's nodes may not be able to see your local Docker images, and the Pod gets stuck in ErrImagePull. Two fixes, depending on your setup:
| Local cluster | Get your image into it |
|---|---|
| kind | kind load docker-image micro-posts:1.0 |
| minikube | minikube image load micro-posts:1.0 |
| Docker Desktop | Usually sees images you built locally already |
| A real cluster | Push to a registry (Docker Hub, GHCR, ECR…) and use the full name |
image: micro-posts (no tag, meaning :latest), Kubernetes defaults to imagePullPolicy: Always and tries to download it from Docker Hub, even though it is already loaded. micro-posts:1.0 defaults to IfNotPresent and uses the local copy.Docker → Kubernetes
You learned Docker first. Pick the Kubernetes equivalent:
docker logs posts-containerkubectl logs posts-podprints the container's output.docker exec -it posts-container shkubectl exec -it posts-pod -- sh.docker run -p 4000:4000(reach it from outside)containerPortonly documents the port. Exposing it is a Service's job.- Restarting a dead container by hand
You declare the state; the kubelet and controllers such as Deployments restore it. (There is no
kubectl restartfor Pods.)
| Docker | Kubernetes |
|---|---|
| Image | Image (the same one) |
| Container | Container inside a Pod |
docker run | Pods created from YAML, usually by a Deployment |
docker logs | kubectl logs |
docker exec | kubectl exec |
| Docker network | Cluster network, every Pod has an IP |
Port mapping -p | Service |
| Restart by hand | Controllers maintain the desired state |
Docker: Image → Container
Kubernetes: Image → Container → Pod → Deployment → Service
Recap
- Every object file has
apiVersion,kind,metadataandspec. - The Pod name is in
metadata.name; container names are insidespec.containers. containerPortdocuments a port; it does not expose it.applyto create,getfor overview,describefor detail,logsandexecto look inside.- READY counts ready containers out of all containers in the Pod.
- Load local images into your cluster and always tag them.
Next: the objects that manage Pods for you. ReplicaSet first, then Deployment, then Service.
Pod YAML: common questions
What does READY 1/1 mean in kubectl get pods?
Ready containers out of total containers in the Pod. 1/1 means its one container is ready; 1/2 means one of two containers is not ready yet.
Does containerPort expose my app?
No. containerPort documents the port the app listens on. To reach the app from outside the Pod you create a Service.