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.
Join the DZone community and get the full member experience.
Join For FreeAbout 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
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:
brew install minikube
On Linux:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube
Start your cluster:
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:
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:
# 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:
kind create cluster --name dev
For a multi-node cluster that better mimics production:
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:
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:
kubectl cluster-info kubectl get nodes
View resources:
kubectl get pods kubectl get deployments kubectl get services kubectl get all # Shows pods, services, deployments
Add -A to see resources across all namespaces:
kubectl get pods -A
Get detailed information:
kubectl describe pod <pod-name> kubectl describe node <node-name>
Watch resources update in real-time:
kubectl get pods -w
Check logs:
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:
kubectl exec -it <pod-name> -- /bin/bash kubectl exec <pod-name> -- curl localhost:8080/health
Apply manifests:
kubectl apply -f deployment.yaml kubectl apply -f ./manifests/ # Apply directory of manifests
Delete resources:
kubectl delete pod <pod-name> kubectl delete -f deployment.yaml
Context switching (useful when you have multiple clusters):
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:
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:
kubectl apply -f nginx-deployment.yaml
Watch the pods come up:
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:
kubectl get service nginx-service
For Minikube, get the URL:
minikube service nginx-service --url
For kind, you need port forwarding since LoadBalancer services don't work locally:
kubectl port-forward service/nginx-service 8080:80
Now hit the service:
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:
kubectl get pods -l app=nginx
Delete one:
kubectl delete pod <pod-name>
Immediately check pods again:
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:
kubectl scale deployment nginx-web --replicas=5
Watch it scale up:
kubectl get pods -l app=nginx -w
Two new pods appear. Scale back down:
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):
# 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:
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:
kubectl get nodes
GCP GKE
Install the gcloud CLI, then create a cluster:
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:
gcloud container clusters get-credentials dev-cluster --zone us-central1-a
Azure AKS
Install Azure CLI, then:
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:
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:
kubectl apply -f nginx-deployment.yaml
The difference is that LoadBalancer services actually provision cloud load balancers. Check the service:
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:
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:
kubectl create namespace staging
Deploy to that namespace:
kubectl apply -f nginx-deployment.yaml -n staging
List resources in that namespace:
kubectl get all -n staging
Set the default namespace for your context:
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:
kubectl create configmap app-config \ --from-literal=environment=production \ --from-literal=log_level=info
Create a Secret:
kubectl create secret generic db-credentials \ --from-literal=username=admin \ --from-literal=password=secretpass
Use them in a deployment:
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:
minikube delete kind delete cluster --name dev
Managed clusters:
# 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?”
Opinions expressed by DZone contributors are their own.
Comments