What containers are, and why Docker won
A container packages application code with every library and dependency it needs, so it runs unchanged from a laptop to production — whether the host is bare metal, a VM, or the cloud.
- Faster deployment through automation, lower cost, better resource utilization
- Portable across environments, and a natural fit for microservices
- Downsides: a compromised host OS affects every container on it, and managing thousands of them adds real operational complexity
| Platform | Known for |
|---|---|
| Docker | Most widely adopted, simple architecture |
| Podman | Daemon-less, more secure by design |
| LXC | Preferred for data-intensive workloads |
| Vagrant | Maximum isolation on a single machine |
Docker architecture
Docker follows a client-server model: the Docker client sends commands over the CLI or REST API to a host running the Docker daemon (dockerd), which builds, runs, and distributes containers, pulling and pushing images from a registry like Docker Hub.
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Each instruction becomes a read-only image layer; docker build packages them into a tagged image, and docker run starts a writable container from it:
docker build -t my-app:v1 .
docker run -p 3000:3000 my-app:v1
docker ps # list running containers
docker images # list local images
From containers to orchestration
Once you're running more than a handful of containers, container orchestration automates their lifecycle — deployment, networking, scaling, and availability — instead of leaving it to scripts.
| Tool | Notes |
|---|---|
| Marathon | Open-source cluster manager |
| Nomad | Lightweight, supports many workload types |
| Docker Swarm | Built into Docker, simplest to adopt |
| Kubernetes | The de facto standard |
Kubernetes (k8s), originally built by Google and now maintained by the CNCF, manages containerized applications declaratively: you describe the desired state, and Kubernetes continuously works to make reality match it — automated rollouts and rollbacks, storage orchestration, and horizontal scaling based on real metrics.
Kubernetes objects and kubectl
Every Kubernetes object has a spec (what you want) and a status (what's actually happening):
| Object | Role |
|---|---|
| Pod | The smallest unit — one or more tightly coupled containers |
| ReplicaSet | Keeps a set number of identical Pods running |
| Deployment | Manages rollouts and updates on top of ReplicaSets |
| Namespace | Isolates groups of resources within a cluster |
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels: { app: my-app }
template:
metadata:
labels: { app: my-app }
spec:
containers:
- name: my-app
image: my-app:v1
ports: [{ containerPort: 3000 }]
kubectl apply -f deployment.yml
kubectl get pods
kubectl describe deployment my-app
kubectl scale deployment my-app --replicas=5
Configuration, secrets, and self-healing
Apps get their configuration and sensitive data through dedicated objects:
kubectl create configmap app-config --from-literal=timeout=5000
kubectl create secret generic app-secret --from-literal=password=hunter2
A Service or Ingress then exposes a set of Pods to other workloads or external traffic. Underneath it all, the control plane relies on etcd as the cluster's source of truth, the kube-scheduler to place new Pods, and the kubelet on every node to keep containers running and healthy.
Key idea: this is what gives Kubernetes its defining trait — self-healing. It doesn't just run your containers; it continuously restarts, reschedules, or replaces them so the cluster's actual state always converges back to what you declared.
