DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Kubernetes CSI Drivers
  • The Importance of Persistent Storage in Kubernetes- OpenEBS
  • Containerization and Helm Templatization Best Practices for Microservices in Kubernetes
  • Zero-Downtime Deployments for Java Apps on Kubernetes

Trending

  • AI Agents in Java: Architecting Intelligent Health Data Systems
  • Bringing Intelligence Closer to the Source: Why Real-Time Processing is the Heart of Edge AI
  • Detecting Bugs and Vulnerabilities in Java With SonarQube
  • Lambda-Driven API Design: Building Composable Node.js Endpoints With Functional Primitives
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Kubernetes 101: Understanding the Foundation and Getting Started

Kubernetes 101: Understanding the Foundation and Getting Started

Learn when Kubernetes truly solves your problems versus when it adds unnecessary complexity — and get hands-on with both local and production cluster setup.

By 
Muhammad Sarwar user avatar
Muhammad Sarwar
·
Dec. 19, 25 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
3.0K Views

Join the DZone community and get the full member experience.

Join For Free

About This Series

This is part 1 of a 5-part series on Kubernetes implementation. These posts cut through the hype to focus on practical decisions: what Kubernetes actually does, when you need it, and how to implement it effectively.

Series Outline

  • Kubernetes 101 – What it is, when you need it, and hands-on setup (this post)
  • Networking – Service discovery, ingress, and how pods actually talk
  • Deployment strategies – Rolling updates, blue-green, canary releases
  • Storage – Persistent volumes and running stateful workloads
  • Production operations – Monitoring, logging, scaling, and troubleshooting

What Kubernetes Actually Does

Kubernetes manages containerized workloads across a cluster of machines. You define the desired state in YAML; Kubernetes reconciles the actual state to match. Container crashes? Kubernetes restarts it. A node dies? Kubernetes reschedules pods elsewhere. Need to scale? Update the replica count.

It solves scheduling containers across multiple machines, auto-scaling based on CPU and memory metrics, self-healing when containers or nodes fail, service discovery and load balancing, rolling updates without downtime, and managing configuration and secrets.

Core Primitives

  • Pod – Smallest unit. One or more containers sharing network and storage. Usually a single container per pod unless you need sidecars like logging agents or proxies.
  • Deployment – Manages replica sets and rolling updates. Your primary workload controller for stateless apps.
  • Service – Stable DNS name and IP for pods. Pods get new IPs when recreated; services give you consistent endpoints.
  • Node – Worker machine (VM or bare metal) running kubelet, the container runtime, and kube-proxy.
  • ConfigMap / Secret – Decouple config from images. ConfigMaps for non-sensitive data, Secrets for credentials (base64-encoded, not encrypted by default).

Example Deployment

YAML
 
apiVersion: apps/v1 kind: Deployment metadata:  name: api-service  namespace: production spec:  replicas: 3  selector:    matchLabels:      app: api  template:    metadata:      labels:        app: api    spec:      containers:      - name: api        image: registry.example.com/api:v1.2.3        ports:        - containerPort: 8080        resources:          requests:            memory: "256Mi"            cpu: "250m"          limits:            memory: "512Mi"            cpu: "500m"        livenessProbe:          httpGet:            path: /health            port: 8080          initialDelaySeconds: 30          periodSeconds: 10


Apply with kubectl apply -f deployment.yaml. Kubernetes maintains three replicas. If one crashes, a new pod spins up automatically.

When You Actually Need Kubernetes

You’re probably in Kubernetes territory if you’re running 15+ microservices where manual coordination no longer scales. Multiple teams deploying independently need isolation and self-service capabilities. If your traffic spikes 10× unpredictably, the Horizontal Pod Autoscaler (HPA) handles it. When you’re on call for service reliability, self-healing reduces those 3 a.m. pages. Multi-region or multi-cloud deployments benefit from consistent deployment patterns across infrastructure. Having a platform team that maintains clusters and writes operators makes Kubernetes viable.

Skip Kubernetes if you have a team under five engineers where operational overhead kills velocity. A monolith or one to three services works fine with Docker Compose and systemd. Predictable traffic means static capacity planning is simpler. If you’re still learning containers, master Docker first. When shipping features matters more than infrastructure sophistication, a managed PaaS gets you moving faster. If no one wants to own Kubernetes operations, that’s your answer right there — clusters don’t run themselves.

Alternatives to Consider

Docker Compose with systemd handles single-host deployments with one to five services. AWS ECS or GCP Cloud Run provide managed container orchestration with less operational burden. HashiCorp Nomad offers simpler scheduling when Kubernetes feels too complex. Fly.io, Railway, or Render give you PaaS with container support and minimal configuration. Serverless options like Lambda or Cloud Functions work well for stateless, event-driven workloads.

Making the Decision

Ask yourself what specific problem you’re solving that you can’t solve with less complexity. If you’re deploying 50 times a day across 20 services and manual coordination is breaking, Kubernetes makes sense. If your reasoning is “everyone’s using Kubernetes” or “we might need to scale,” that’s not a concrete problem yet.

If you’re running microservices, getting paged for manual scaling, spending hours on deployment coordination, and your Docker Compose file is 500 lines long, Kubernetes operational complexity is likely less than your current operational pain. Before that point, you’re taking on complexity without the corresponding benefit.

Getting Started: Local Development Setup

You need a local Kubernetes environment for development. Pushing every change to a cloud cluster to test is slow and expensive. Local clusters let you iterate quickly, break things safely, and learn without worrying about cloud costs.

Minikube vs. kind

Minikube creates a single-node Kubernetes cluster in a VM. It’s the most mature option with good documentation and wide support. The downside is that it’s heavier — you’re running a full VM just for Kubernetes.

kind (Kubernetes in Docker) runs cluster nodes as Docker containers instead of VMs. It’s faster to start, uses fewer resources, and works well in CI/CD pipelines. The tradeoff is slightly less feature parity with production clusters.

For learning and development, either works fine.

Installing Minikube

On macOS with Homebrew:

Plain Text
 
brew install minikube


On Linux:

Plain Text
 
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube


Start your cluster:

Plain Text
 
minikube start --driver=docker --cpus=2 --memory=4096


This creates a single-node cluster with 2 CPUs and 4 GB of RAM. You can adjust these based on your machine. Minikube will download the Kubernetes components and start everything up. The first run takes a few minutes.

Verify it’s running:

Plain Text
 
kubectl get nodes


You should see one node in the Ready state. Minikube automatically configures kubectl to talk to your local cluster.

Installing kind

Install kind:

Plain Text
 
# macOS brew install kind 
# Linux curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 chmod +x ./kind sudo mv ./kind /usr/local/bin/kind


Create a cluster:

Plain Text
 
kind create cluster --name dev


For a multi-node cluster that better mimics production:

Plain Text
 
cat <<EOF | kind create cluster --name dev --config=- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker - role: worker EOF


This gives you one control plane node and two worker nodes, all running as Docker containers. Check the nodes:

Plain Text
 
kubectl get nodes


You’ll see three nodes. kind also automatically configures kubectl for you.

Essential kubectl Commands

kubectl is your primary interface to Kubernetes. Here are the commands you’ll use constantly.

Check cluster status:

Plain Text
 
kubectl cluster-info kubectl get nodes


View resources:

Plain Text
 
kubectl get pods kubectl get deployments kubectl get services kubectl get all  # Shows pods, services, deployments


Add -A to see resources across all namespaces:

Plain Text
 
kubectl get pods -A


Get detailed information:

Plain Text
 
kubectl describe pod <pod-name> kubectl describe node <node-name>


Watch resources update in real-time:

Plain Text
 
kubectl get pods -w


Check logs:

Plain Text
 
kubectl logs <pod-name> kubectl logs <pod-name> -f  # Follow logs kubectl logs <pod-name> -c <container-name>  # Specific container in pod


Execute commands in containers:

Plain Text
 
kubectl exec -it <pod-name> -- /bin/bash kubectl exec <pod-name> -- curl localhost:8080/health


Apply manifests:

Plain Text
 
kubectl apply -f deployment.yaml kubectl apply -f ./manifests/  # Apply directory of manifests


Delete resources:

Plain Text
 
kubectl delete pod <pod-name> kubectl delete -f deployment.yaml


Context switching (useful when you have multiple clusters):

Plain Text
 
kubectl config get-contexts kubectl config use-context minikube kubectl config use-context kind-dev


Deploying Your First Application

Let’s deploy a real application — a simple web server that we can actually interact with. Create a file called nginx-deployment.yaml:

YAML
 
apiVersion: apps/v1 kind: Deployment metadata:  name: nginx-web  labels:    app: nginx spec:  replicas: 3  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: nginx:1.25        ports:        - containerPort: 80        resources:          requests:            memory: "128Mi"            cpu: "100m"          limits:            memory: "256Mi"            cpu: "200m" --- apiVersion: v1 kind: Service metadata:  name: nginx-service spec:  selector:    app: nginx  ports:  - protocol: TCP    port: 80    targetPort: 80  type: LoadBalancer


This manifest creates a Deployment with three nginx replicas and a Service to access them. Apply it:

Plain Text
 
kubectl apply -f nginx-deployment.yaml


Watch the pods come up:

Plain Text
 
kubectl get pods -l app=nginx -w


You’ll see three pods go from Pending to ContainerCreating to Running. Once they’re all running, check the service:

Plain Text
 
kubectl get service nginx-service


For Minikube, get the URL:

Plain Text
 
minikube service nginx-service --url


For kind, you need port forwarding since LoadBalancer services don't work locally:

Plain Text
 
kubectl port-forward service/nginx-service 8080:80


Now hit the service:

Plain Text
 
curl localhost:8080  # for kind with port-forward # or curl $(minikube service nginx-service --url)  # for minikube


You should see the nginx welcome page HTML.

Experimenting With Self-Healing

Let's see K8s self-healing in action. List your pods:

Plain Text
 
kubectl get pods -l app=nginx


Delete one:

Plain Text
 
kubectl delete pod <pod-name>


Immediately check pods again:

Plain Text
 
kubectl get pods -l app=nginx


You’ll see the deleted pod terminating and a new pod being created. Kubernetes detected that the actual state (two pods) doesn’t match the desired state (three pods) and corrected it.

Scaling

Scale the deployment:

Plain Text
 
kubectl scale deployment nginx-web --replicas=5


Watch it scale up:

Plain Text
 
kubectl get pods -l app=nginx -w


Two new pods appear. Scale back down:

Plain Text
 
kubectl scale deployment nginx-web --replicas=2


Three pods terminate. This is the declarative model in action — you declare what you want, and Kubernetes makes it happen.

Moving to Managed Kubernetes

Local clusters are great for development, but production needs managed Kubernetes. The major cloud providers offer managed control planes where they handle upgrades, etcd backups, and control-plane availability. You manage worker nodes and workloads.

AWS EKS

Install eksctl (the EKS cluster management tool):

Plain Text
 
# macOS brew install eksctl 
# Linux curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp sudo mv /tmp/eksctl /usr/local/bin


Create a cluster:

Plain Text
 
eksctl create cluster \  --name dev-cluster \  --region us-west-2 \  --nodegroup-name standard-workers \  --node-type t3.medium \  --nodes 3 \  --nodes-min 1 \  --nodes-max 4 \  --managed


This creates an EKS control plane and a managed node group with three t3.medium instances. It takes about 15 minutes. eksctl configures kubectl automatically.

Verify:

Plain Text
 
kubectl get nodes


GCP GKE

Install the gcloud CLI, then create a cluster:

Plain Text
 
gcloud container clusters create dev-cluster \  --zone us-central1-a \  --num-nodes 3 \  --machine-type e2-medium \  --enable-autoscaling \  --min-nodes 1 \  --max-nodes 5


Get credentials:

Plain Text
 
gcloud container clusters get-credentials dev-cluster --zone us-central1-a


Azure AKS

Install Azure CLI, then:

Plain Text
 
az aks create \  --resource-group myResourceGroup \  --name dev-cluster \  --node-count 3 \  --node-vm-size Standard_B2s \  --enable-cluster-autoscaler \  --min-count 1 \  --max-count 5 \  --generate-ssh-keys


Get credentials:

Plain Text
 
az aks get-credentials --resource-group myResourceGroup --name dev-cluster


Deploying to Production Cluster

Once your managed cluster is running, deploying is identical to local. The same nginx manifest works:

Plain Text
 
kubectl apply -f nginx-deployment.yaml


The difference is that LoadBalancer services actually provision cloud load balancers. Check the service:

Plain Text
 
kubectl get service nginx-service


In EKS, you’ll see an AWS ELB hostname in the EXTERNAL-IP field. In GKE and AKS, you get an actual external IP. This takes a minute or two to provision.

Once you have the external endpoint, hit it:

Plain Text
 
curl <external-ip-or-hostname>


You’re now running nginx in a production Kubernetes cluster with a real load balancer in front.

Namespaces for Isolation

Production clusters typically run multiple environments or teams. Namespaces provide isolation. Create a namespace:

Plain Text
 
kubectl create namespace staging


Deploy to that namespace:

Plain Text
 
kubectl apply -f nginx-deployment.yaml -n staging


List resources in that namespace:

Plain Text
 
kubectl get all -n staging


Set the default namespace for your context:

Plain Text
 
kubectl config set-context --current --namespace=staging


Now all kubectl commands default to staging instead of the default namespace.

Configuration Management

You’ve got secrets and configuration that shouldn’t be in your images. ConfigMaps and Secrets handle this.

Create a ConfigMap:

Plain Text
 
kubectl create configmap app-config \  --from-literal=environment=production \  --from-literal=log_level=info


Create a Secret:

Plain Text
 
kubectl create secret generic db-credentials \  --from-literal=username=admin \  --from-literal=password=secretpass


Use them in a deployment:

YAML
 
apiVersion: apps/v1 kind: Deployment metadata:  name: app spec:  replicas: 2  selector:    matchLabels:      app: myapp  template:    metadata:      labels:        app: myapp    spec:      containers:      - name: app        image: myapp:latest        env:        - name: ENVIRONMENT          valueFrom:            configMapKeyRef:              name: app-config              key: environment        - name: DB_USER          valueFrom:            secretKeyRef:              name: db-credentials              key: username        - name: DB_PASS          valueFrom:            secretKeyRef:              name: db-credentials              key: password


The app container gets these as environment variables. Keep secrets out of your images and manifests this way.

Cleanup

Local clusters:

Plain Text
 
minikube delete kind delete cluster --name dev


Managed clusters:

Plain Text
 
# EKS eksctl delete cluster --name dev-cluster 
# GKE gcloud container clusters delete dev-cluster --zone us-central1-a 
# AKS az aks delete --resource-group myResourceGroup --name dev-cluster


Delete cloud clusters when you're not using them to avoid charges.

What's Next

In the next post, we’ll dive into Kubernetes networking — how services actually work, ClusterIP vs. NodePort vs. LoadBalancer, ingress controllers for HTTP routing, and network policies for security. Understanding networking is critical because most production issues involve the question: “Why can’t service A talk to service B?”

Kubernetes Docker (software) pods

Opinions expressed by DZone contributors are their own.

Related

  • Kubernetes CSI Drivers
  • The Importance of Persistent Storage in Kubernetes- OpenEBS
  • Containerization and Helm Templatization Best Practices for Microservices in Kubernetes
  • Zero-Downtime Deployments for Java Apps on Kubernetes

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook