ZMedia Purwodadi

Kubernetes Basics for Developers: Running Containers Beyond One Machine

Table of Contents

 


Kubernetes Basics

Container Management Beyond One Server

Modern applications are no longer always deployed as one simple file on one server. A real production application may have a frontend, backend API, database connection, cache, background worker, authentication service, logging system, monitoring setup, and multiple running copies of the same service.

Docker helps developers package an application into a container. A container includes the application code, runtime, libraries, and required environment so that the application can run more consistently across different machines.

Docker Compose helps run multiple containers together on a single machine. For example, a developer may use Docker Compose to run a backend, frontend, database, and Redis cache during local development.

But production is more complex than local development.

A company may need to run containers across many machines. It may need automatic restart when containers fail. It may need scaling when traffic increases. It may need safe updates without downtime. It may need traffic routing, service discovery, health checks, configuration management, secrets, storage, and monitoring.

This is where Kubernetes becomes important.

Kubernetes is a platform used to manage containerized applications across a cluster of machines. It helps developers and DevOps teams run, scale, update, and recover applications in a more controlled way.

Kubernetes is not the first DevOps tool beginners need to learn. Before Kubernetes, it is better to understand Linux basics, Git, Docker, Dockerfile, Docker Compose, networking basics, CI/CD, cloud deployment, and monitoring. But once an application grows beyond one machine, Kubernetes becomes an important concept.

Why Kubernetes Exists

Running one container manually is simple.

A developer can run:

docker run nginx

This starts one Nginx container.

But a production system needs more than starting one container.

A production team may ask:

How many copies of the backend should run?
What happens if one copy crashes?
How does traffic reach the correct container?
How can a new version be deployed without downtime?
How can the application scale during high traffic?
How can the team roll back if a release fails?
Where should configuration and passwords be stored?
How can logs and health checks be monitored?

These are operational questions.

Kubernetes exists to solve these problems. It does not write your application code. It manages how your containerized application runs in production-like environments.

A simple way to understand Kubernetes:

Docker packages the application.
Kubernetes manages the application containers in a cluster.

Docker answers, “How do I build and run this container?”

Kubernetes answers, “How do I keep this containerized application running, scaling, updating, and recovering across machines?”

Link to:Web application security

The Core Idea: Desired State

The most important Kubernetes idea is desired state.

In Kubernetes, you describe what you want. Kubernetes tries to keep the system matching that description.

For example, you may say:

Run three copies of my backend.
Use this container image.
Expose the application on port 80.
Restart failed containers.
Send traffic only to healthy pods.

Kubernetes continuously compares the current state with the desired state.

If you want three backend pods and one crashes, Kubernetes creates another one. If a node fails, Kubernetes can move workloads to another available node. If a new image is deployed, Kubernetes can gradually replace old pods with new ones.

This is different from manually managing containers.

Instead of repeatedly starting containers by hand, you define the desired state and let Kubernetes maintain it.

Kubernetes Cluster


Kubernetes Cluster

A Kubernetes cluster is a group of machines used to run containerized applications.

A cluster usually has two major parts:

Control plane
Worker nodes

The control plane manages the cluster. It makes decisions, watches the cluster state, and coordinates resources.

Worker nodes run the actual application workloads. These workloads run inside pods, and pods contain containers.

For local learning, tools like Minikube or Kind can create a small Kubernetes cluster on your computer. In production, a cluster may run on cloud providers such as AWS, Google Cloud, Azure, or private infrastructure.

A small cluster may have one worker node. A production cluster may have many worker nodes.

Control Plane

The control plane is the brain of Kubernetes.

It does not usually run normal application traffic in production setups. Its job is to manage the cluster.

The control plane checks what should be running and what is actually running. If something is missing, unhealthy, or not matching the desired state, it takes action.

For example, if a Deployment says three pods should run and only two are currently running, the control plane works to create another pod.

This management role is what makes Kubernetes powerful.

Worker Node

A worker node is a machine that runs application workloads.

A worker node can be a virtual machine, cloud instance, or physical server. It provides CPU, memory, networking, and storage resources for pods.

When you deploy an application, Kubernetes schedules pods onto available worker nodes. It considers available resources and cluster rules before deciding where pods should run.

If one node becomes unhealthy, Kubernetes can reschedule workloads to other nodes depending on the setup.

For a beginner, remember this:

Control plane manages.
Worker nodes run applications.

Pod

A pod is the smallest deployable unit in Kubernetes.

A pod usually contains one container. In some advanced cases, a pod may contain multiple closely related containers that need to share the same network and storage context.

Most beginner projects use one container per pod.

Example:

A Spring Boot backend container runs inside a backend pod.
A React frontend container runs inside a frontend pod.
A Python worker container runs inside a worker pod.

Pods are temporary. They can be created, deleted, restarted, or replaced. Because of this, production systems usually do not depend directly on individual pod IP addresses.

Developers normally manage pods through higher-level objects such as Deployments.

Link to:Authentication Password Security

Container Inside a Pod

Kubernetes does not remove the need for containers.

Your application still runs inside a container. Kubernetes manages that container through a pod.

For example, a backend application may be packaged as a Docker image:

my-backend:v1

Kubernetes can run that image inside a pod.

The container runs the application. The pod provides the Kubernetes environment around it, including networking, lifecycle management, and optional storage.

This is one of the most important beginner points:

Docker creates container images.
Kubernetes runs those images inside pods.

Deployment

A Deployment is a Kubernetes object used to manage application pods.

A Deployment defines:

Which container image should run
How many replicas should exist
Which labels identify the pods
How updates should happen

For example, you can create a Deployment that says:

Run three backend pods using image backend:v1.

If one pod crashes, Kubernetes creates another pod. If you change the image to backend:v2, Kubernetes can perform a rolling update.

Deployments are commonly used for stateless applications such as APIs, frontend services, and background workers.

Link to: AI Agent

Replica

A replica is one running copy of an application pod.

If a backend Deployment has three replicas, Kubernetes runs three backend pods.

Multiple replicas help with availability and traffic handling. If one pod fails, the other pods can still serve requests. If traffic increases, more replicas can be added.

Example:

1 replica  = useful for learning
3 replicas = better availability
10 replicas = higher traffic handling

The number depends on traffic, resources, and application design.

Stateless Applications

A stateless application does not store important permanent data inside the running container.

Examples include:

Backend API
Frontend service
Authentication service
Notification worker
File processing worker

Stateless applications are easier to run in Kubernetes because pods can be restarted or replaced without losing important data.

For example, a backend API should not store user orders inside the container file system. It should store them in a database or external storage system.

Kubernetes works especially well with stateless services.

Link to: AI Hallucination

Stateful Applications

A stateful application stores important data or depends on stable identity.

Examples include:

Database
Message broker
Storage system
Distributed database cluster

Stateful workloads are more complex because data must be preserved when pods restart or move.

Kubernetes supports stateful workloads using StatefulSets, Persistent Volumes, and Persistent Volume Claims. However, for many beginner and small production projects, using a managed database service outside Kubernetes is easier and safer.

A good beginner rule:

Run your app services in Kubernetes first.
Be careful before running production databases inside Kubernetes.

Service

A Service gives stable network access to pods.

Pods are temporary. Their IP addresses can change when they restart or get replaced. If another application connects directly to a pod IP, that connection can break.

A Service solves this by providing a stable name and stable access point.

Example:

A backend Deployment has three backend pods.
A backend Service sends traffic to those pods.
The frontend connects to the Service instead of individual pod IP addresses.

A Service selects pods using labels.

This is how Kubernetes makes networking reliable between changing pods.

Link to : Rag AI

ClusterIP Service

ClusterIP is the default Service type.

It exposes the Service only inside the Kubernetes cluster.

Example:

A frontend pod can call an internal backend Service.
A backend pod can call an internal payment Service.
A worker pod can call an internal queue Service.

ClusterIP is useful for internal communication.

It is not directly accessible from outside the cluster.

For beginners, remember:

ClusterIP = internal access inside the cluster

NodePort Service

NodePort exposes a Service on a port of each worker node.

This allows external traffic to reach the Service using the node IP and port.

NodePort is useful for learning and local testing. For example, with Minikube, you can expose a sample application and open it in a browser.

But in production, NodePort is usually not the cleanest approach. Production systems often use Ingress or a cloud LoadBalancer.

For beginners:

NodePort = simple external access for practice

LoadBalancer Service

A LoadBalancer Service asks the cloud provider to create an external load balancer.

This is common in managed Kubernetes environments.

The load balancer receives public traffic and forwards it to the Kubernetes Service.

Example:

User visits your application.
Cloud load balancer receives traffic.
Traffic is sent to the Kubernetes Service.
Service forwards traffic to healthy pods.

LoadBalancer is useful in production, but it can add cloud cost because external load balancers are usually paid resources.

Link to: AI vs Rule based systems

Ingress

Ingress manages external HTTP and HTTPS traffic into the cluster.

It can route traffic based on domain names and paths.

Example:

example.com        -> frontend service
example.com/api    -> backend service
example.com/admin  -> admin service

Ingress is commonly used for web applications because it provides cleaner routing than exposing every Service separately.

Ingress can also help with HTTPS, domain-based routing, and path-based routing.

However, Ingress needs an Ingress Controller to work.

Ingress Controller

An Ingress resource only defines routing rules. An Ingress Controller actually applies those rules and handles traffic.

Common Ingress controllers include:

Nginx Ingress Controller
Traefik
Cloud-specific Ingress controllers

Without an Ingress Controller, Ingress rules alone will not route traffic.

For beginners, this is an important confusion point:

Ingress = routing rules
Ingress Controller = component that makes the rules work

ConfigMap

A ConfigMap stores non-sensitive configuration values.

Examples:

Application mode
Log level
Public API URL
Feature flag
Service name
Application profile

ConfigMaps help separate configuration from the container image.

For example, the same backend image can run in development, staging, and production with different configuration values.

ConfigMap should not store passwords, tokens, or private keys. Sensitive values should be stored in Secrets.

Link to: Fine Tuning AI

Secret

A Secret stores sensitive values.

Examples:

Database password
API key
JWT secret
Private token
Cloud access credential

Secrets help keep sensitive configuration separate from application images.

However, Kubernetes Secrets still need careful access control. Developers should not expose Secrets in logs, screenshots, public repositories, or article examples with real values.

A safe teaching example should use fake values only.

Environment Variables in Kubernetes

Kubernetes can pass ConfigMap and Secret values into containers as environment variables.

Example:

The backend image stays the same.
The database URL comes from a ConfigMap.
The database password comes from a Secret.
The application profile comes from an environment variable.

This makes deployments flexible.

Instead of rebuilding the Docker image for every environment, you can change the runtime configuration.

Volumes and Persistent Storage

By default, container storage is temporary. If the container is restarted or replaced, data inside the container may disappear.

A Volume provides storage to a pod.

For simple stateless applications, volumes may not be needed. But for applications that process files, store temporary data, or run stateful workloads, volumes become important.

A Persistent Volume is storage that can exist beyond the life of a pod.

A Persistent Volume Claim is a request for storage.

Example:

A database pod needs 10GB storage.
The pod uses a Persistent Volume Claim.
Kubernetes connects the claim to available storage.

Storage in Kubernetes should be planned carefully, especially for production.

YAML Manifest

Kubernetes resources are usually defined using YAML files.

A YAML manifest describes the desired state.

You can define resources such as:

Deployment
Service
ConfigMap
Secret
Ingress
Persistent Volume Claim

This makes infrastructure configuration repeatable and version-controlled.

Instead of clicking buttons manually, the team can store YAML files in Git and apply them whenever needed.

Practical Example: Creating a Deployment

Let us create a simple Nginx Deployment.

Create a file named sample-deployment.yaml.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: sample-container
          image: nginx:1.25
          ports:
            - containerPort: 80

This file tells Kubernetes to run three pods. Each pod runs the Nginx container on port 80.

Apply the file:

kubectl apply -f sample-deployment.yaml

Check the Deployment:

kubectl get deployments

Check the pods:

kubectl get pods

If one pod fails, the Deployment tries to create another pod to maintain three replicas.

This is the desired-state model in action.

Practical Example: Creating a ClusterIP Service

Now create a Service so the pods can be accessed through a stable internal address.

Create a file named sample-service.yaml.

apiVersion: v1
kind: Service
metadata:
  name: sample-service
spec:
  type: ClusterIP
  selector:
    app: sample-app
  ports:
    - port: 80
      targetPort: 80

Apply the Service:

kubectl apply -f sample-service.yaml

Check Services:

kubectl get services

Describe the Service:

kubectl describe service sample-service

This Service selects pods with the label app: sample-app. If pods are replaced, the Service still forwards traffic to the correct matching pods.

Practical Example: Exposing the App with NodePort

For local practice, NodePort is easy to understand.

Create a file named sample-nodeport-service.yaml.

apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport-service
spec:
  type: NodePort
  selector:
    app: sample-app
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080

Apply it:

kubectl apply -f sample-nodeport-service.yaml

Check it:

kubectl get svc

If you are using Minikube, open the service:

minikube service sample-nodeport-service

NodePort is useful for practice. For production web applications, Ingress or LoadBalancer is usually better.

Useful kubectl Commands

kubectl is the command-line tool used to interact with Kubernetes.

Common commands:

kubectl get pods

Shows running pods.

kubectl get deployments

Shows deployments.

kubectl get services

Shows services.

kubectl describe pod <pod-name>

Shows detailed information about a pod.

kubectl logs <pod-name>

Shows application logs from a pod.

kubectl delete -f sample-deployment.yaml

Deletes resources defined in a YAML file.

These commands help beginners move from theory to practice.

Link to: Embeddings AI

Deployment Update with a New Image

In real projects, applications are updated frequently.

For example, a backend image may change from:

backend:v1

to:

backend:v2

In Kubernetes, you can update the image in the Deployment YAML and apply it again.

Example:

containers:
  - name: backend-container
    image: my-backend:v2

Then run:

kubectl apply -f backend-deployment.yaml

Kubernetes can gradually replace old pods with new pods using a rolling update.

This helps reduce downtime.

Rolling Update

A rolling update gradually replaces old pods with new pods.

Example:

Your backend has three pods running version 1.
You deploy version 2.
Kubernetes starts one new pod with version 2.
When it is ready, Kubernetes removes one old pod.
This continues until all pods are updated.

This is safer than stopping all old pods at once.

Rolling updates are useful because users can continue accessing the application while the update happens.

Link to: Types of Machine Learning

Rollback

A rollback means returning to a previous working version.

If a new deployment causes errors, Kubernetes can roll back to an older ReplicaSet.

Check rollout history:

kubectl rollout history deployment sample-app

Roll back:

kubectl rollout undo deployment sample-app

Rollback is useful, but it is not magic. If your application update includes database changes, rollback must be planned carefully.

A good production deployment considers both application code and database compatibility.

Readiness Probe

A readiness probe checks whether a pod is ready to receive traffic.

A container may be running, but the application may not be ready yet.

For example, a backend service may need time to connect to the database or load configuration.

If readiness probe fails, Kubernetes does not send traffic to that pod.

Example:

readinessProbe:
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 5
  periodSeconds: 10

Readiness probes help prevent users from reaching an application that is still starting.

Liveness Probe

A liveness probe checks whether a container is still alive.

Sometimes an application may become stuck but the process does not exit. A liveness probe can detect this and restart the container.

Example:

livenessProbe:
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 15
  periodSeconds: 20

Liveness probes are useful for self-healing.

However, they should be configured carefully. If the check is too strict, Kubernetes may restart healthy containers unnecessarily.

Startup Probe

Some applications take longer to start.

A startup probe helps Kubernetes understand that the application is still starting and should not be killed too early.

Example:

startupProbe:
  httpGet:
    path: /
    port: 80
  failureThreshold: 30
  periodSeconds: 10

Startup probes are useful for applications with slow startup, such as large Java services or applications that load many resources before becoming ready.

Resource Requests and Limits

Kubernetes allows you to define how much CPU and memory a container needs.

A request tells Kubernetes the expected minimum resource requirement.

A limit defines the maximum resource usage allowed.

Example:

resources:
  requests:
    memory: "128Mi"
    cpu: "250m"
  limits:
    memory: "256Mi"
    cpu: "500m"

Requests help Kubernetes schedule pods properly.

Limits prevent one container from consuming too many resources.

But limits should be realistic. If memory limit is too low, the application may be killed frequently.

Horizontal Pod Autoscaler

Horizontal Pod Autoscaler, or HPA, automatically adjusts the number of pod replicas based on metrics such as CPU usage.

Example:

If CPU usage increases, Kubernetes can add more backend pods.
If traffic decreases, Kubernetes can reduce the number of pods.

Autoscaling helps applications handle changing traffic.

Example command:

kubectl autoscale deployment sample-app --cpu-percent=70 --min=2 --max=10

This tells Kubernetes to keep CPU usage around 70% by scaling between 2 and 10 pods.

Autoscaling is powerful, but it should be tested carefully. More pods may increase database connections, memory use, and cloud cost.

ConfigMap Example

Create a file named app-config.yaml.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_MODE: "production"
  LOG_LEVEL: "info"

Apply it:

kubectl apply -f app-config.yaml

Use it in a Deployment:

env:
  - name: APP_MODE
    valueFrom:
      configMapKeyRef:
        name: app-config
        key: APP_MODE

This keeps non-sensitive configuration separate from the container image.

Secret Example

Create a Secret using kubectl:

kubectl create secret generic app-secret --from-literal=DB_PASSWORD=fakepassword123

Use it in a Deployment:

env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: app-secret
        key: DB_PASSWORD

In real projects, do not expose real secrets in public repositories, tutorials, or screenshots.

Secrets should be managed carefully with proper access control.

Full Stack Application in Kubernetes

A full stack application in Kubernetes may contain:

Frontend Deployment
Frontend Service
Backend Deployment
Backend Service
ConfigMap
Secret
Ingress
Monitoring
Logging

Example flow:

A user visits the domain.
Ingress receives the request.
Frontend service serves the UI.
Frontend calls backend API.
Backend service routes traffic to backend pods.
Backend reads configuration from ConfigMap.
Backend reads database password from Secret.
Database may run outside the cluster as a managed service.

This structure is common in modern cloud-native applications.

Kubernetes for Microservices

Kubernetes is often used with microservices.

A microservice application may include:

User service
Order service
Payment service
Notification service
Inventory service

Each service can have its own Deployment and Service.

This allows teams to scale services separately. For example, payment service may need fewer replicas, while product search service may need more replicas during high traffic.

Kubernetes provides service discovery, internal networking, rolling updates, and scaling for these services.

However, microservices also increase complexity. Kubernetes helps manage the infrastructure, but teams still need good logging, monitoring, API design, security, and communication between services.

Kubernetes in DevOps

Kubernetes is important in DevOps because it supports automation and reliability.

A CI/CD pipeline can build a Docker image, push it to a registry, update the Kubernetes Deployment, and monitor rollout status.

Example deployment flow:

Developer pushes code to Git.
CI pipeline runs tests.
Docker image is built.
Image is pushed to registry.
Kubernetes Deployment is updated.
Rolling update starts.
Monitoring checks application health.

This makes deployment more repeatable than manual server updates.

Kubernetes and Docker Compose Difference

Docker Compose is simpler and useful for local development or small single-server setups.

Kubernetes is more powerful and designed for clusters.

Docker Compose is good when:

You are learning containers.
You want to run frontend, backend, and database locally.
You have a simple deployment on one machine.

Kubernetes is useful when:

You need scaling.
You need self-healing.
You need rolling updates.
You run many services.
You manage production workloads across multiple machines.

A beginner should not jump into Kubernetes before understanding Docker and Docker Compose.

When Kubernetes Is Useful

Kubernetes is useful when applications need:

Multiple replicas
Self-healing
Rolling updates
Service discovery
Autoscaling
Cluster-level container management
Microservice deployment
Cloud-native operations
Production-like reliability

For a growing product, Kubernetes can bring structure and automation.

For example, an e-commerce backend may need multiple API replicas during festival sales. A fintech application may need safe updates and health checks. A SaaS product may need several internal services communicating reliably.

Kubernetes is valuable when the operational need is real.

Link to: Vector Database

When Kubernetes May Be Too Much

Kubernetes is not always needed.

A small blog, simple portfolio, early-stage API, or small business website may run well on normal hosting, VPS, Docker Compose, or managed platforms.

Kubernetes adds complexity. It needs cluster management, monitoring, security, resource planning, deployment strategy, and debugging knowledge.

Using Kubernetes only because it is popular can waste time.

A good developer chooses tools based on the project need.

Simple project: simple deployment.
Growing production system: Kubernetes may help.

This practical decision-making is more important than tool hype.


Link to: Token AI

Beginner Confusion: localhost in Kubernetes

One common beginner confusion is using localhost incorrectly.

Inside a Kubernetes pod, localhost means the same pod, not your laptop and not another service.

If a backend pod needs to connect to a database service, it should use the Kubernetes Service name, not localhost.

Example:

Wrong in many cases:
localhost:5432

Better inside cluster:
database-service:5432

Understanding this saves a lot of debugging time.

Beginner Confusion: latest Image Tag

Using latest image tag may feel easy, but it can cause confusion in production.

Example:

image: my-backend:latest

The problem is that latest does not clearly show which version is running.

A better approach is to use versioned tags:

image: my-backend:v1.2.3

Versioned tags make rollback, debugging, and release tracking easier.

Beginner Confusion: Secrets in Plain YAML

Beginners sometimes put passwords directly inside YAML files.

That is risky, especially if the file is pushed to GitHub.

Bad practice:

DB_PASSWORD: "mypassword123"

A safer approach is to use Kubernetes Secrets, external secret managers, and proper access control.

Even with Kubernetes Secrets, teams should avoid exposing sensitive values in logs, screenshots, public demos, or tutorials.

Kubernetes Debugging Mindset

When something fails in Kubernetes, do not guess immediately.

Start with simple checks.

Check pods:

kubectl get pods

Describe the pod:

kubectl describe pod <pod-name>

Check logs:

kubectl logs <pod-name>

Check services:

kubectl get svc

Check events:

kubectl get events

Many Kubernetes issues are caused by image pull errors, wrong ports, failed health checks, missing environment variables, resource limits, or incorrect service selectors.

A calm debugging process is better than random changes.

Production Mindset for Kubernetes

Kubernetes can run applications, but production readiness needs more than YAML files.

A production Kubernetes setup should consider:

Monitoring
Logging
Security
Resource limits
Health probes
Rollback strategy
Backup plan
Secret management
Network access rules
Cost control
CI/CD pipeline
Alerting

Kubernetes gives the platform, but the team still needs good engineering practices.

A cluster without monitoring is risky. A Deployment without resource limits can affect other workloads. A public dashboard without security can become dangerous. A database without backup can create serious loss.

Kubernetes is powerful, but it must be managed responsibly.

Practical Kubernetes Understanding

Kubernetes helps manage containers in a reliable and scalable way.

Pods run containers.
Deployments manage pods.
Services provide stable access.
Ingress routes external traffic.
ConfigMaps store non-sensitive configuration.
Secrets store sensitive values.
Volumes handle storage.
Probes check health.
Autoscaling adjusts capacity.
kubectl helps developers interact with the cluster.

For developers, Kubernetes becomes easier when every concept is connected to a real problem.

Pods solve container execution.
Deployments solve replica management and updates.
Services solve changing pod IPs.
Ingress solves web routing.
ConfigMaps and Secrets solve configuration separation.
Probes solve health checking.
Autoscaling solves changing traffic.

Kubernetes is not just a collection of difficult terms. It is a system designed to keep containerized applications running in real production environments.

A developer does not need to master every advanced feature on day one. The important first step is to understand how applications move from a single container to a managed cluster.

Post a Comment