← Back to the section

Deployment used to work like this: you wrote a script, logged in over ssh, stopped the process, copied over the new jar, and started it again. If a node went down — that was it, the service was down, and someone had to go fix it by hand. If you needed five copies — you did the same thing five times.

Kubernetes solves this problem differently: you describe what should be running, and the cluster decides how on its own — and keeps that state going continuously.

The main idea: desired state

Kubernetes works like a constant observer. You tell it: "I want three copies of this service, each needs 512 MB of memory, port 8080 faces outward." Kubernetes compares this with what is actually there and eliminates the differences.

If one copy goes down, Kubernetes creates a new one. If a whole node dies, the pods move to live machines. You do nothing by hand: the cluster maintains the desired picture itself.

An important rule follows from this: you change the cluster state by editing declarations, not by running manual commands. Logging in and tweaking something "real quick" is the same as editing a file on a running server while bypassing your version control system.

Pod: one process (or several side by side)

A Pod is the smallest unit in Kubernetes. In most cases a pod is a single container with your application.

Two properties of a pod are important to understand from the very beginning:

A pod is disposable. It doesn't get healed — it gets killed and a new one is created. If the application crashes, Kubernetes doesn't try to fix it: it just starts a fresh container from the image. It follows that no important state should live inside a pod. Files on disk will disappear. The IP address will change.

A pod's IP is not permanent. While the pod is alive it has an address. When the pod is recreated the address is different. You can't rely on it. For a permanent address there is a Service — more on that below.

Sometimes a pod holds several containers: for example, your application plus a log-collection agent. They share one network and one disk. This is called the sidecar pattern, but a beginner rarely needs it — in 90% of cases a pod = one container.

Deployment: who manages the pods

Pods are almost never created by hand. A Deployment is a declaration: "run N copies of this pod and keep them in working order."

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  selector:
    matchLabels: { app: order-service }
  template:
    metadata:
      labels: { app: order-service }
    spec:
      containers:
        - name: app
          image: registry.local/order-service:1.42.0
          ports:
            - containerPort: 8080
          resources:
            requests: { cpu: "500m", memory: "768Mi" }
            limits: { memory: "768Mi" }

A Deployment gives you three things:

  • Self-healing — a replica goes down, Kubernetes creates a new one.
  • Scaling — you change replicas: 3 to replicas: 5, and within a few seconds there are five.
  • Zero-downtime updates — when the image changes, the Deployment replaces pods in batches, without taking the whole service down.

How does a Deployment find its pods? Not by names — by labels. In the example above all pods have the label app: order-service, and the Deployment looks for exactly those through selector.matchLabels. The same mechanism is used in a Service.

Service: a permanent address over changing pods

Pods come and go, their addresses change. A Service is a stable name and a virtual IP that always points to live pods:

apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector: { app: order-service }
  ports:
    - port: 80
      targetPort: 8080

Now any other service in the cluster reaches it by the name http://order-service — and Kubernetes routes the request to one of the live pods with the label app: order-service.

An important detail: a pod gets added to the list of available addresses only when its readiness probe returns success. If the application hasn't warmed up yet or is temporarily unavailable — traffic doesn't go to it. How to configure a probe is covered in the next article.

How a request from the internet reaches a pod

The full chain looks like this:

Internet → LoadBalancer → Ingress controller → Service → Pod

Ingress is a routing declaration: "send requests for api.example.com/orders to the Service order-service." TLS is also configured there. This declaration is carried out by the Ingress controller (for example, nginx or traefik) — that's what actually listens to incoming requests.

When something doesn't work, you check this chain link by link: Is the LoadBalancer up? Is Ingress configured correctly? Does the Service see the pods? Are the pods responding to the probe?

Namespace: separating environments

A namespace is a folder for cluster objects. Usually you create a separate namespace for each environment: payments-prod, payments-staging.

Isolation within a namespace is administrative: access rights, resource quotas. A namespace does not provide network isolation — services from different namespaces can talk to each other unless explicitly forbidden through a NetworkPolicy.

The minimal set of commands

Most of a developer's tasks are covered by four commands:

kubectl -n payments-prod get pods
kubectl -n payments-prod describe pod order-service-7d4b9c-x2x4v
kubectl -n payments-prod logs deploy/order-service --tail=200
kubectl -n payments-prod port-forward svc/order-service 8080:80

The first — to see what's running. The second — to figure out why a pod won't start or is down. The third — to read the logs. The fourth — to temporarily forward a port to yourself so you can check the service works locally.

What Kubernetes doesn't do for you

Kubernetes replaces crashed pods — but it doesn't fix the code inside. If the application crashes because of a bug, Kubernetes will restart the pod endlessly (this is called CrashLoopBackOff) — but you have to find the cause in the logs and fix it yourself.

Kubernetes doesn't make a service fault-tolerant on its own. Retries, timeouts, automatically cutting off a broken neighbor — that's still the developer's job. Kubernetes only makes sure that the right number of pods are alive.

A database in Kubernetes is a separate, complex topic (StatefulSet, operators). For most teams it's simpler to keep a managed database outside the cluster.

Object cheat sheet

ObjectWhat it does
PodRuns one or several containers together
DeploymentMakes sure the right number of pods is always running
ServiceA permanent name and address over changing pods
IngressRoutes external traffic to the right Service
ConfigMap / SecretConfiguration and secrets separate from the image
NamespaceA group of objects with rights and quotas

In short

  • Kubernetes maintains the desired state: you declare it, it executes and recovers on failures.
  • A Pod is a disposable unit. It isn't healed, it's recreated. No important state inside.
  • A Deployment manages N copies of a pod, providing self-healing, scaling and zero-downtime updates.
  • Labels are the connecting mechanism: a Deployment finds its pods by labels, and so does a Service.
  • A Service is a permanent address over live pods. Traffic goes only to pods with a green readiness probe.
  • Ingress routes external traffic to the right Service.
  • A Namespace separates environments administratively, not by network.
  • Kubernetes replaces crashed pods, but it doesn't fix the code or make a service fault-tolerant on its own.

Further reading

  • Spring Boot in Kubernetes — probes, resources and graceful shutdown: what the developer is responsible for.
  • Networking and traffic — Service types, DNS, Ingress and Gateway API in detail.
  • Deployment and configuration — manifests, Helm, rolling update.
  • Operations and debugging — kubectl and typical incidents.