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)
  • kubectl that 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, use minikube 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)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲