Decode Kubernetes
In order to understand Kubernetes, you need to learn how Docker works. Docker is a base layer which helps you manage the life cycle of a container. But if you want to run multiple containers, it would become a nightmare to manage a docker-compose file.
Let's take an example of a docker-compose.yml file for a simple Rails app behind nginx:
version: '3.4'
services:
app:
build: .
command: bundle exec puma -C /app/config/puma.rb
working_dir: /app/
environment:
- RACK_ENV
- VIRTUAL_HOST
expose:
- 3000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/healthcheck"]
interval: 45s
timeout: 10s
retries: 3
start_period: 5s
nginx:
image: nginx:alpine
container_name: nginx
volumes:
- nginxdata:/etc/nginx
ports:
- 80:80
nginx_conf_generator:
image: jwilder/docker-gen
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./docker/templates/:/etc/docker-gen/templates
- nginxdata:/etc/nginx
volumes:
nginxdata:
A simple application behind a web server. Let's dive into the details of what docker-compose gives us:
1. We need a prebuilt image or local Dockerfile to start a container
2. Docker needs a command to start the container
3. We need a way to reach the containers, so we use expose & ports to serve the traffic
4. To retain the filesystem with a container, we use docker volumes or mount a local path
5. We need a way to configure services so containers start in a desired state
6. Once started, we ensure they're healthy with health checks
At production scale, you'll also need:
1. Deployment rollbacks
2. Flexibility to add more applications / components
3. Auto scaler
4. Auto rotation of servers in case of upgrades
5. Access controls
Docker is very good for local development purposes. But to deploy an application in production, we need to consider these factors above.
When do you need Kubernetes?
For a small organization with fewer than 5 services, you may not need Kubernetes. Simple bare metal servers using Terraform should work most of the time. As you start adding more and more services, your DevOps team will become a bottleneck to manage those servers.
Managing a server means setup, security, upgrades, and maintenance. Now if you have 20+ servers and more than 5 components, DevOps needs to pay attention to each component. Cycle time of any infrastructure change increases linearly. You'd end up heavily relying on DevOps.
Luckily, Kubernetes solves for all these problems. It's like a Swiss army knife for a small DevOps team — helps engineering teams maintain their own applications with little infrastructure knowledge. DevOps would only be required to manage overall infrastructure or to scale.
What is Kubernetes?
It's a software which helps you orchestrate large scale infrastructure using containerisation technology.
Pod & Service
The basic unit of Kubernetes is a pod, equivalent to a container in Docker terms. A pod can be part of a service. A service exposes the pod via DNS and port — meaning you can reach your app pod by making a request to http://service.foo.bar.svc.local:port within the Kubernetes network.
The only problem with a service is that you cannot expose it directly to external traffic, as it doesn't have the smarts to drain requests cleanly. We still need a load balancer like nginx here — which can itself run inside a pod.
Ingress
Now the real question: how do we link the app pod and the nginx pod? Requests should flow between the load balancer and pods via services. But the load balancer itself cannot discover a service since it runs inside a pod — and pods discover each other. Your load balancer should also be able to serve multiple apps.
We need something that can connect the load balancer and the pods. Hence we have the ingress resource — an object with a set of routing rules. But the ingress resource itself cannot route requests. The missing piece is: how does the load balancer learn about those routing rules?
What is a Controller?
A controller's only purpose is to watch for a change of state and reconcile it inside a loop (let that sink in). So far we have: an ingress object, a controller, and a load balancer. But we also need a store to save the current state — hence etcd, a simple key-value store. Every Kubernetes resource is stored in etcd.
Let's connect the dots: the controller sees a new ingress object that's missing in the store. Before reconciling the state, it syncs the routing rules to nginx. This means you can write your own controller for a specific purpose — you only need to define the action.
Example controllers:
• Nginx Ingress Controller — syncs ingress rules to nginx
• Deployment Controller — rolls pods with updated images
• ReplicaSet Controller — scales pods up/down
• DaemonSet Controller — ensures a pod runs on every node
• CronJob Controller — manages scheduled jobs
When you deploy an application, the deployment controller ensures pods are rotated in a rolling manner — bringing up a new pod with the updated image before taking down the old one. If your application needs to autoscale, the ReplicaSet controller handles that. If you want a pod running on every node, the DaemonSet controller helps.
Helm
In general, we no longer write raw Kubernetes specs — they require a lot of boilerplate. Helm helps you manage templates on top of Kubernetes specs, which you simply configure. It also handles versioning, rollbacks, and dependency management.
Summary
By applying first principles thinking, we traced how Kubernetes overcomes the shortfalls of Docker for production workloads — from a single container, to docker-compose, to a full orchestration platform with controllers, services, ingress, and etcd holding it all together.
I hope this gives you a solid mental model of Kubernetes. To fully internalize it, set up minikube locally and get your hands on the actual resources. Thanks for reading.