[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:

  1. Volumes: Managed by Docker, best for persistent data
  2. Bind Mounts: Mount host directory into container
  3. 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
  • 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

  1. Container Images: Understanding how to build efficient images
  2. Container Networking: How containers communicate
  3. Container Storage: Persistent data management
  4. Container Lifecycle: Health checks and restart policies
  5. Environment Variables: Configuration management
  6. 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

  1. Start with simple deployments
  2. Add health checks and monitoring
  3. Implement configuration management
  4. Set up networking and services
  5. 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:

Complete Series: Kubernetes A-to-Z Series Overview