Part 4 – From kubectl apply to a Running Spring Boot Application

What Really Happens Behind the Scenes?

“The most powerful thing about Kubernetes isn’t that it runs containers. It’s that it continuously works to make reality match your desired state.”


Friday Evening Deployment

It is Friday evening.

Sarah, a Senior Java Developer at Global Trust Bank, has just completed a new feature for the Payment Service. Over the past two weeks, her team has implemented support for scheduled international payments.

The feature has successfully passed every stage of the delivery pipeline.

  • Unit tests ✓
  • Integration tests ✓
  • Security scans ✓
  • Code review ✓
  • Performance testing ✓

The CI pipeline has already produced a Docker image and pushed it to the organization’s private container registry.

The release window has opened.

Sarah receives approval from the release manager.

She opens her terminal.

kubectl apply -f payment-service.yaml

A few seconds later…

deployment.apps/payment-service created

That’s it.

No SSH.

No copying JAR files.

No restarting Tomcat.

No calling Operations.

No updating load balancers manually.

No logging into production servers.

From Sarah’s perspective, deploying an enterprise banking application was just one command.

But behind that single command, Kubernetes has already started orchestrating dozens of independent components, each performing a very specific responsibility.

Before the first customer can make a payment using the new feature, Kubernetes must validate the deployment, persist it, schedule it, create containers, pull images, start the JVM, perform health checks and finally expose the application to the rest of the platform.

All of this happens automatically.

Understanding this journey is one of the biggest differences between someone who uses Kubernetes and someone who truly understands Kubernetes.

Today, we’ll follow that journey together.


Why This Matters

Many Java developers learn Kubernetes by memorizing commands.

kubectl apply

kubectl get pods

kubectl logs

kubectl describe

While these commands are important, they don’t explain what Kubernetes is actually doing.

Consider these questions.

  • Why does creating a Deployment also create a ReplicaSet?
  • Who decides which node runs the Pod?
  • When is the Docker image downloaded?
  • What starts the Java Virtual Machine?
  • At what point does Spring Boot begin accepting requests?
  • Why doesn’t traffic immediately reach the new Pod?
  • What happens if the node crashes halfway through deployment?

If you can confidently answer these questions, troubleshooting production issues becomes dramatically easier.

Instead of treating Kubernetes as a black box, you’ll understand the responsibilities of every component involved.


Meet Our Enterprise Platform

We’ll continue using the same banking platform introduced in previous articles.

                        Mobile Banking App
                                 │
                                 ▼
                          API Gateway
                                 │
        ┌────────────────────────┼────────────────────────┐
        ▼                        ▼                        ▼
 Payment Service         Customer Service         Fraud Service
        │
        │
        ├──────────────► Oracle Database
        │
        ├──────────────► Redis Cache
        │
        └──────────────► Solace Event Broker

Today’s deployment focuses only on the Payment Service, but remember that it is part of a much larger ecosystem.

A deployment in Kubernetes is rarely just about one application. Every service depends on other services, shared infrastructure, messaging platforms and databases.

This is why Kubernetes was designed as an orchestration platform rather than simply a container runtime.


The Deployment Manifest

Sarah isn’t deploying Java code.

She isn’t deploying a Docker container either.

She is submitting a declaration.

That declaration looks something like this.

apiVersion: apps/v1

kind: Deployment

metadata:
  name: payment-service

spec:
  replicas: 3

  selector:
    matchLabels:
      app: payment-service

  template:

    metadata:
      labels:
        app: payment-service

    spec:

      containers:

      - name: payment-service

        image: bank/payment-service:2.4.1

        ports:

        - containerPort: 8080

Notice something interesting.

There isn’t a single instruction saying:

  • Create Pod 1
  • Create Pod 2
  • Run on Node 5
  • Download the Docker image
  • Start the JVM
  • Restart if it crashes

The YAML doesn’t contain procedural instructions.

Instead, it describes the desired end state.

This is one of the most important concepts in Kubernetes.


Kubernetes Doesn’t Execute Instructions

Most software developers spend their careers writing imperative code.

For example, consider creating a customer.

Customer customer = new Customer();

customer.setName("Rahul");

customerRepository.save(customer);

These instructions must be executed one after another.

If you skip one step, the result changes.

This is known as imperative programming.

Kubernetes works differently.

It follows a declarative model.

Instead of telling Kubernetes how to create three Pods, we simply declare the desired outcome.

replicas: 3

That’s it.

We don’t care which nodes Kubernetes chooses.

We don’t care which order the Pods are created.

We don’t care how many controllers participate.

We simply describe the desired state.

The responsibility for achieving that state belongs entirely to Kubernetes.

This design choice is what enables Kubernetes to recover automatically from failures.


Desired State vs Current State

Every action inside Kubernetes revolves around one simple question.

“Does the current state match the desired state?”

Imagine Sarah requested three Payment Service Pods.

Desired State

Payment Pod 1

Payment Pod 2

Payment Pod 3

Initially, no Pods exist.

Current State

No Pods Running

Kubernetes immediately notices the difference.

It starts creating Pods until reality matches the declaration.

Later, suppose one worker node unexpectedly crashes.

The current state changes again.

Desired

Pod A

Pod B

Pod C

Current

Pod A

Pod B

Kubernetes doesn’t panic.

Nobody needs to log in and restart the application.

No Operations engineer receives a midnight call.

The control plane simply notices the mismatch.

Desired = 3

Current = 2

Difference = 1

A replacement Pod is created automatically.

This continuous reconciliation loop is the foundation of Kubernetes self-healing.

Notice something subtle.

Sarah never asked Kubernetes to recreate failed Pods.

She only declared that three Pods should always exist.

Everything else is Kubernetes fulfilling that promise.


The Journey Begins

When Sarah presses Enter, the deployment does not go to a worker node.

It doesn’t create a Pod.

It doesn’t even download a Docker image.

Instead, the very first destination is the brain of the Kubernetes cluster—the API Server.

Every interaction with Kubernetes, whether it’s creating a Deployment, scaling an application, updating a ConfigMap, or deleting a Pod, begins at exactly the same place.

The API Server is the single entry point into the Kubernetes control plane.

It validates every request, authenticates every user, authorizes every operation, persists the desired state, and coordinates the controllers that will eventually transform a simple YAML file into a running Spring Boot application.

In the next section, we’ll step inside the API Server and follow Sarah’s deployment request from the moment it enters the Kubernetes control plane, all the way until it is safely stored in etcd, ready for the orchestration process to begin.

Leave a Reply

Your email address will not be published. Required fields are marked *