[2/24] C is for Containers: Docker Fundamentals Before Kubernetes
π This is Post #2 in the Kubernetes A-to-Z Series
Reading Order: β Previous: Kubernetes Basics β Next: Pods β
Series Progress: 2/24 complete | Difficulty: Beginner | Time: 20 min | Part 1/6: Foundation Welcome back to our Kubernetes A-to-Z Series! In this second post, weβll explore container fundamentals and Docker basics. Understanding containers is crucial before diving deep into Kubernetes, as Kubernetes is essentially a container orchestration platform.
What Are Containers?
Containers are lightweight, standalone, executable packages that include everything needed to run a piece of software: code, runtime, system tools, system libraries, and settings.
Containers vs Traditional Virtualization
Traditional Virtualization:
βββββββββββββββββββββββββββββββββββββββ
β Physical Server β
βββββββββββββββββββββββββββββββββββββββ€
β Hypervisor β
βββββββββ¬ββββββββ¬ββββββββ¬ββββββββ¬βββββ€
β VM1 β VM2 β VM3 β VM4 β β
β(OS+App)β(OS+App)β(OS+App)β(OS+App)β β
βββββββββ΄ββββββββ΄ββββββββ΄ββββββββ΄βββββ
Containerization:
βββββββββββββββββββββββββββββββββββββββ
β Physical Server β
βββββββββββββββββββββββββββββββββββββββ€
β Host OS β
βββββββββββββββββββββββββββββββββββββββ€
β Container Runtime β
ββββββββ¬βββββββ¬βββββββ¬βββββββ¬βββββββββ€
β App1 β App2 β App3 β App4 β β
β(Container)β(Container)β(Container)β(Container)β β
ββββββββ΄βββββββ΄βββββββ΄βββββββ΄βββββββββ
Key Benefits of Containers
- Lightweight: Share the host OS kernel, no hypervisor overhead
- Portable: Run consistently across different environments
- Efficient: Faster startup times and better resource utilization
- Isolated: Each container has its own filesystem, processes, and network
- Scalable: Easy to create, destroy, and replicate
Docker: The Container Platform
Docker is the most popular container platform, providing tools to build, ship, and run containers. While Kubernetes supports multiple container runtimes, Docker concepts are fundamental to understanding containerization.
Docker Architecture
βββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Client β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β Docker Daemon β
ββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββββββ€
βImagesβContainersβNetworks βVolumes β
ββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββββββ
β
ββββββ΄βββββ
βΌ βΌ
ββββββββββ ββββββββββ
βContainer Runtimeβ
ββββββββββ
Essential Docker Concepts
1. Images vs Containers
Images are read-only templates that define whatβs in a container:
# List images
docker images
# Pull an image
docker pull nginx:latest
# Remove an image
docker rmi nginx:latest
Containers are running instances of images:
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Create and start a container
docker run nginx:latest
2. Dockerfile: Building Custom Images
A Dockerfile is a text file containing instructions to build an image:
# Example Dockerfile for a Node.js application
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Define command to run
CMD ["npm", "start"]
Build the image:
# Build image from Dockerfile
docker build -t my-node-app:latest .
# Tag image for registry
docker tag my-node-app:latest myregistry/my-node-app:v1.0
3. Container Lifecycle
# Create container (don't start)
docker create nginx:latest
# Start container
docker start <container-id>
# Stop container
docker stop <container-id>
# Remove container
docker rm <container-id>
# Run (create + start)
docker run -d --name my-nginx nginx:latest
Docker Networking Fundamentals
Network Types
# List networks
docker network ls
# Create custom network
docker network create my-network
# Inspect network
docker network inspect my-network
Network Types:
- bridge: Default network for containers
- host: Share hostβs network stack
- none: No networking
- overlay: For multi-host networking (Swarm)
Container Communication
# Run containers on same network
docker run -d --name web --network my-network nginx
docker run -d --name app --network my-network node-app
# Containers can communicate by name
# From app container: curl http://web
Port Mapping
# Map container port to host port
docker run -d -p 8080:80 --name web nginx
# Map to random host port
docker run -d -P --name web nginx
# Check port mapping
docker port web
Docker Storage Concepts
Volume Types
# Create named volume
docker volume create my-volume
# List volumes
docker volume ls
# Inspect volume
docker volume inspect my-volume
Storage Options:
- Volumes: Managed by Docker, best for persistent data
- Bind Mounts: Mount host directory into container
- tmpfs: Store data in host memory (temporary)
Using Volumes
# Mount volume to container
docker run -d -v my-volume:/data nginx
# Bind mount host directory
docker run -d -v /host/path:/container/path nginx
# Read-only mount
docker run -d -v my-volume:/data:ro nginx
Container Registry Management
Working with Registries
# Login to registry
docker login registry.example.com
# Push image to registry
docker push registry.example.com/my-app:v1.0
# Pull image from registry
docker pull registry.example.com/my-app:v1.0
Popular Registries
- Docker Hub: Default public registry
- Google Container Registry (GCR)
- Amazon Elastic Container Registry (ECR)
- Azure Container Registry (ACR)
- Harbor: Open-source private registry
Multi-Container Applications
Docker Compose
Docker Compose helps define and run multi-container applications:
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
- DB_HOST=db
- DB_USER=root
- DB_PASS=password
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=myapp
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:
Compose Commands:
# Start all services
docker-compose up -d
# Stop all services
docker-compose down
# View logs
docker-compose logs -f
# Scale services
docker-compose up -d --scale web=3
# Build and start
docker-compose up -d --build
Container Best Practices
1. Image Optimization
# β
Good: Multi-stage build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/server.js"]
2. Security Practices
# β
Good: Run as non-root user
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
COPY --chown=nextjs:nodejs . .
CMD ["node", "server.js"]
3. Layer Caching
# β
Good: Order matters for caching
COPY package*.json ./
RUN npm install
COPY . .
Container Orchestration Needs
As applications grow, manual container management becomes challenging:
Problems Docker Compose Canβt Solve
- Multi-host networking
- Service discovery across hosts
- Automatic scaling
- Self-healing capabilities
- Rolling updates with zero downtime
- Resource optimization across cluster
Why We Need Kubernetes
# Docker Compose (Single host)
version: '3.8'
services:
web:
image: myapp
deploy:
replicas: 3 # Only on single host!
# Kubernetes (Multi-host cluster)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 10 # Distributed across cluster!
template:
spec:
containers:
- name: web
image: myapp
Preparing for Kubernetes
Essential Concepts to Master
- Container Images: Understanding how to build efficient images
- Container Networking: How containers communicate
- Container Storage: Persistent data management
- Container Lifecycle: Health checks and restart policies
- Environment Variables: Configuration management
- Resource Limits: CPU and memory constraints
Kubernetes-Relevant Docker Skills
# Health checks
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/health || exit 1
# Resource constraints
docker run -m 512m --cpus=1.0 nginx
# Environment variables
docker run -e DATABASE_URL=postgres://user:pass@host/db myapp
Common Container Patterns
1. Init Containers
Containers that run before the main application:
# Example: Database migration
FROM alpine:latest
RUN apk add --no-cache postgresql-client
COPY migrate.sh .
CMD ["./migrate.sh"]
2. Sidecar Containers
Helper containers that extend functionality:
# Example: Logging sidecar
main-app:
image: myapp:latest
logging-sidecar:
image: fluentd:latest
volumes:
- shared-logs:/var/log
3. Adapter Containers
Containers that transform interfaces:
# Example: Metrics adapter
app:
image: legacy-app:latest
metrics-adapter:
image: prometheus-adapter:latest
ports:
- "9090:9090"
Troubleshooting Container Issues
Common Problems and Solutions
# Container won't start
docker logs <container-id> # Check logs
docker inspect <container-id> # Inspect configuration
# Container exits immediately
docker run -it <image> /bin/sh # Get shell to debug
# Network issues
docker network inspect <network> # Check network config
docker exec -it <container> ping <target> # Test connectivity
# Storage issues
docker volume inspect <volume> # Check volume details
docker exec -it <container> df -h # Check disk usage
Migration Path: Docker β Kubernetes
Step 1: Containerize Your Application
# Build production-ready image
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
Step 2: Define Kubernetes Manifests
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:v1.0
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
Step 3: Gradual Migration
- Start with simple deployments
- Add health checks and monitoring
- Implement configuration management
- Set up networking and services
- Add persistent storage if needed
Key Takeaways
- Containers are lightweight, portable application packages
- Docker provides tools to build, ship, and run containers
- Images are templates; containers are running instances
- Docker Compose helps with multi-container applications on single hosts
- Kubernetes solves multi-host orchestration challenges
- Master container fundamentals before diving into Kubernetes
Docker Command Reference
# Image Management
docker build -t myapp:latest . # Build image
docker images # List images
docker push myregistry/myapp:v1.0 # Push to registry
docker pull nginx:latest # Pull image
docker rmi <image-id> # Remove image
# Container Lifecycle
docker run -d --name web nginx # Run container
docker ps # List running containers
docker ps -a # List all containers
docker stop <container> # Stop container
docker start <container> # Start container
docker rm <container> # Remove container
docker logs <container> # View logs
docker exec -it <container> /bin/bash # Execute command
# Networking
docker network ls # List networks
docker network create my-network # Create network
docker run --network my-network nginx # Use network
docker port <container> # Show port mappings
# Storage
docker volume ls # List volumes
docker volume create my-volume # Create volume
docker run -v my-volume:/data nginx # Mount volume
docker run -v /host/path:/container/path nginx # Bind mount
# Compose
docker-compose up -d # Start services
docker-compose down # Stop services
docker-compose logs -f # View logs
docker-compose ps # List services
Next Steps
Now that you understand container fundamentals and Docker basics, youβre ready to explore Pods, the basic building blocks of Kubernetes. In the next post, weβll see how Kubernetes manages containers at scale and introduces new abstractions.
Resources for Further Learning
Series Navigation:
- Previous: K is for Kubernetes: Understanding the Basics and Architecture
- Next: P is for Pods: The Basic Building Blocks of Kubernetes
Complete Series: Kubernetes A-to-Z Series Overview