When Kubernetes announced the deprecation of dockershim, a wave of panic swept through the DevOps community. "Is Kubernetes killing Docker?" headlines flashed across tech blogs. The reality is far less dramatic, but profoundly important for modern infrastructure engineering. Docker and Kubernetes remain the ultimate power duo for containerized applications.
1. The Dockershim Deprecation Myth
Let's clear the air immediately: Kubernetes did not drop Docker. It dropped dockershim.
To understand this, you have to understand that Docker is not a single tool; it is a full tech stack. It includes a CLI, an API, a container build tool, and a container runtime. In the early days of Kubernetes, Docker was the only viable way to run containers. Because Kubernetes needed to talk to the Docker engine, developers built a bridge called dockershim.
"The removal of dockershim does not mean your Dockerfiles are suddenly obsolete. An image built with docker build is a standard OCI image, which Kubernetes handles flawlessly."
2. How Kubernetes Actually Runs Containers
As Kubernetes matured, the community realized that maintaining a specific bridge for Docker was inefficient. Enter the Container Runtime Interface (CRI). The CRI allows Kubernetes to talk to any container runtime that supports the standard, such as containerd or CRI-O.
Interestingly, containerd was originally created by Docker! Docker spun it out as an independent project and donated it to the Cloud Native Computing Foundation (CNCF). Therefore, when Kubernetes pulls and runs your Docker image using containerd, it is using the exact same underlying runtime that Docker Desktop uses.
Image: Modern Kubernetes clusters utilize lightweight runtimes like containerd on worker nodes to maximize resource efficiency.
3. Building Docker Images for Kubernetes
Before Kubernetes can orchestrate your application, it must be containerized. Let's look at a highly optimized, multi-stage Dockerfile for a web application. Multi-stage builds are critical for Kubernetes because smaller images mean faster node scaling and reduced attack surfaces.
# Stage 1: Build the application
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production Image
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Once you build this image, you must push it to a registry that your Kubernetes cluster can access, such as Docker Hub, AWS ECR, or Google GCR.
4. Hands-On: Deploying a Docker Container to K8s
To deploy our newly created Docker image to Kubernetes, we need to write declarative YAML manifests. Kubernetes uses a Deployment to manage the pods containing our Docker containers.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: web-frontend
template:
metadata:
labels:
app: web-frontend
spec:
containers:
- name: nginx-container
image: your-dockerhub-username/my-k8s-app:1.0
ports:
- containerPort: 80
5. Advanced Orchestration Concepts
When running Docker on Kubernetes in a production environment, you unlock powerful orchestration capabilities:
- Self-Healing: If a Docker container crashes, Kubernetes automatically detects the failure and restarts the pod.
- Autoscaling: Kubernetes can automatically scale the number of Docker containers based on CPU traffic.
- Zero-Downtime: When you update your Docker image tag, Kubernetes performs a rolling update seamlessly.