2026年11月14日
2026年11月14日
Kubernetes Pods, Deployments and Services Explained with Manifests
This article is a localized edition of the Japanese original. Read the Japanese original
When you run an app on Kubernetes, the first three objects to understand are the Pod, the Deployment and the Service. This article explains what each one does and how the three are connected through "labels" and "selectors," using manifests you can try as-is on your own cluster.
What the three objects do
| Object | Role | Do you create it directly? |
|---|---|---|
| Pod | The smallest unit Kubernetes runs: a group of one or more containers. Containers in a Pod share an IP address and ports | Rarely, except for learning |
| Deployment | Declares how many Pods to run and which image to use, and handles keeping that count, rolling updates and rollbacks (it creates a ReplicaSet internally) | Usually, yes: this is what you create |
| Service | Gives a stable name and IP address to a set of Pods selected by label, and distributes traffic to them | Created alongside a Deployment |
A Pod gets a new IP address every time it is recreated. So the work is split: the Deployment keeps the number of Pods, and the Service delivers traffic to whichever Pods are currently running.
What ties the three together is not names but labels. A Deployment puts a label such as app: myapp on the Pods it creates, and a Service uses its selector to find Pods with the same label. If the label is off by even one character, the Service will not connect to any Pod.
What you need
- A running Kubernetes cluster (for learning, minikube, kind, k3s and so on)
kubectlthat can connect to the cluster
Check the connection with the following:
kubectl cluster-info
kubectl get nodes
For how to set up a cluster on a single VPS, see Installing Kubernetes on a VPS with k3s.
Run a single Pod on its own
First, create just one Pod to see what a Pod is. Save the following as pod.yaml.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp-test
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
Apply it and check its status.
kubectl apply -f pod.yaml
kubectl get pods -o wide
kubectl describe pod myapp-pod
kubectl logs myapp-pod
When STATUS shows Running, it has started. Once you have checked it, delete it.
kubectl delete -f pod.yaml
A Pod created on its own is not recreated if it is deleted or its node stops. In real operation, you let a Deployment create Pods, as shown next.
Manage Pods with a Deployment
Save the following as deployment.yaml.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
Set spec.selector.matchLabels and spec.template.metadata.labels to the same values. If they do not match, applying the manifest fails with an error.
kubectl apply -f deployment.yaml
kubectl rollout status deployment/myapp
kubectl get deployments
kubectl get replicasets
kubectl get pods -l app=myapp
After confirming that three Pods are running, delete one of them.
kubectl delete pod "$(kubectl get pods -l app=myapp -o jsonpath='{.items[0].metadata.name}')"
kubectl get pods -l app=myapp
A new Pod is created right away, bringing the count back to three. This is the Deployment (more precisely, the ReplicaSet under it) keeping the count.
Change the number of replicas
kubectl scale deployment/myapp --replicas=5
kubectl scale is an on-the-spot change. The next time you run kubectl apply on deployment.yaml, the count goes back to the replicas value, so write any change you want to keep into the manifest and apply it.
Update the image and roll it back
If you change image in deployment.yaml to nginx:1.28 and apply it, the Pods are replaced with new ones a few at a time (the default update strategy is a rolling update).
kubectl apply -f deployment.yaml
kubectl rollout status deployment/myapp
kubectl rollout history deployment/myapp
If a problem appears after the update, you can go back to the previous state.
kubectl rollout undo deployment/myapp
undo only reverts the state on the cluster side. Your local deployment.yaml is not changed, so edit the file to match what you rolled back to.
Access Pods through a Service
Save the following as service.yaml. Set selector to the same label you put on the Deployment's Pods, app: myapp.
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
port is the port the Service listens on, and targetPort is the port the container in the Pod listens on. If you omit type, the Service becomes ClusterIP (an address usable only inside the cluster).
kubectl apply -f service.yaml
kubectl get services
kubectl get endpointslices -l kubernetes.io/service-name=myapp
If the EndpointSlice lists the Pods' IP addresses, the Service and the Pods are connected. To try it from your own computer, use port forwarding.
kubectl port-forward service/myapp 8080:80
In another terminal, run curl http://localhost:8080/; if nginx responds, it works. From other Pods in the cluster, the Service is reachable by the name http://myapp (when in the same namespace).
Service types
| type | Reachable from |
|---|---|
| ClusterIP (default) | Inside the cluster only |
| NodePort | From outside, via each node's IP address and a fixed port (by default in the range 30000-32767) |
| LoadBalancer | Assigns an external load balancer. Requires a cloud integration or something like k3s ServiceLB or MetalLB |
| ExternalName | An alias for a DNS name outside the cluster |
Notes on using your own images
An image you built locally with docker build is not visible to the cluster's nodes as-is. In the manifest's image, specify an image stored somewhere the nodes can pull from.
- Push it to a container registry (such as Docker Hub) and use that image name
- With kind, load it onto the nodes with
kind load docker-image myapp:1.0.3; with minikube, useminikube image load myapp:1.0.3
The latest tag keeps the same name even when the contents change, so you lose track of which version is running. Using tags that show the version, such as nginx:1.27 or myapp:1.0.3, also makes rollbacks easier.
Common problem states and how to check them
In the table, replace POD_NAME and SERVICE_NAME with the names shown by kubectl get pods and kubectl get services.
| Symptom | Common cause | Command to check |
|---|---|---|
Pod is in ImagePullBackOff |
Wrong image name or tag, or missing credentials for a private registry | Events in kubectl describe pod POD_NAME |
Pod is in CrashLoopBackOff |
The app exits right after the container starts | kubectl logs POD_NAME --previous |
Pod stays Pending |
Not enough CPU or memory on the nodes | Events in kubectl describe pod POD_NAME |
| No response when connecting to the Service | The selector does not match the Pod labels, or targetPort is wrong |
kubectl get endpointslices -l kubernetes.io/service-name=SERVICE_NAME and kubectl get pods --show-labels |
Clean up
kubectl delete -f service.yaml
kubectl delete -f deployment.yaml
Summary
- A Pod is the smallest unit that runs containers, and on its own it is not recreated
- A Deployment manages the number and version of Pods, and handles rolling updates and rollbacks with
kubectl rollout undo - A Service gives a fixed name and IP address to the Pods selected by label
- The three are connected by labels and selectors, so when they do not connect, check first that the labels match
How to pass settings and passwords to Pods is covered in Kubernetes ConfigMaps and Secrets, and how to keep data in Kubernetes PersistentVolumes.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)