All Articles
Docker

Docker Complete Guide: Containerization Mastery

Master Docker with containers, images, Docker Compose, and best practices

20-24 minutes

Docker Complete Guide: Containerization Mastery

Docker revolutionized application deployment by packaging applications and their dependencies into portable containers. This guide covers everything you need to master Docker containerization.

Why Docker?

  • 📦 Portability - Run anywhere: dev, test, production
  • 🚀 Fast Deployment - Start containers in seconds
  • 🔄 Consistency - Same environment everywhere
  • 💡 Isolation - Applications don't interfere with each other
  • 📈 Scalability - Easy horizontal scaling

Installation

# macOS
brew install --cask docker

# Ubuntu
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

# Verify installation
docker --version
docker run hello-world

Docker Basics

Running Containers

# Run a container
docker run nginx

# Run in detached mode
docker run -d nginx

# Run with port mapping
docker run -d -p 8080:80 nginx

# Run with name
docker run -d --name my-nginx -p 8080:80 nginx

# Run with environment variables
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql

# Run interactively
docker run -it ubuntu bash

# Run with volume mount
docker run -d -v /host/path:/container/path nginx

Container Management

# List running containers
docker ps

# List all containers
docker ps -a

# Stop container
docker stop container_id

# Start container
docker start container_id

# Restart container
docker restart container_id

# Remove container
docker rm container_id

# Remove all stopped containers
docker container prune

# View container logs
docker logs container_id

# Follow logs
docker logs -f container_id

# Execute command in running container
docker exec -it container_id bash

# Inspect container
docker inspect container_id

# View container stats
docker stats

Dockerfile

Basic Dockerfile

# Use official Node.js runtime as base image
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy application code
COPY . .

# Expose port
EXPOSE 3000

# Set environment variable
ENV NODE_ENV=production

# Define command to run app
CMD ["node", "server.js"]

Multi-Stage Build

# Build stage
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production stage
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

# Copy built assets from builder stage
COPY --from=builder /app/dist ./dist

EXPOSE 3000

CMD ["node", "dist/server.js"]

Dockerfile Best Practices

# Use specific version tags
FROM node:18.17.0-alpine

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# Optimize layer caching
COPY package*.json ./
RUN npm ci --only=production

# Copy application code last
COPY --chown=nodejs:nodejs . .

# Switch to non-root user
USER nodejs

# Use ENTRYPOINT for executable containers
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node", "server.js"]

# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

Building and Managing Images

# Build image
docker build -t myapp:1.0 .

# Build with build args
docker build --build-arg NODE_ENV=production -t myapp:1.0 .

# Build without cache
docker build --no-cache -t myapp:1.0 .

# List images
docker images

# Remove image
docker rmi image_id

# Remove unused images
docker image prune

# Tag image
docker tag myapp:1.0 myusername/myapp:1.0

# Push to Docker Hub
docker push myusername/myapp:1.0

# Pull image
docker pull nginx:latest

# Save image to tar file
docker save -o myapp.tar myapp:1.0

# Load image from tar file
docker load -i myapp.tar

Docker Compose

Basic docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db
    volumes:
      - ./src:/app/src
    networks:
      - app-network

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

volumes:
  postgres-data:

networks:
  app-network:
    driver: bridge

Full Stack Application

version: '3.8'

services:
  # Frontend
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - REACT_APP_API_URL=http://localhost:4000
    depends_on:
      - backend
    networks:
      - app-network

  # Backend API
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "4000:4000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/myapp
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET=your-secret-key
    depends_on:
      - postgres
      - redis
    volumes:
      - ./backend/src:/app/src
    networks:
      - app-network

  # PostgreSQL Database
  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - app-network

  # Redis Cache
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

  # Nginx Reverse Proxy
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - frontend
      - backend
    networks:
      - app-network

volumes:
  postgres-data:

networks:
  app-network:
    driver: bridge

Docker Compose Commands

# Start services
docker-compose up

# Start in detached mode
docker-compose up -d

# Build and start
docker-compose up --build

# Stop services
docker-compose down

# Stop and remove volumes
docker-compose down -v

# View logs
docker-compose logs

# Follow logs for specific service
docker-compose logs -f web

# List running services
docker-compose ps

# Execute command in service
docker-compose exec web bash

# Scale service
docker-compose up -d --scale web=3

# Restart service
docker-compose restart web

Docker Networking

Network Types

# Create bridge network
docker network create my-network

# Create network with subnet
docker network create --subnet=172.18.0.0/16 my-network

# List networks
docker network ls

# Inspect network
docker network inspect my-network

# Connect container to network
docker network connect my-network container_id

# Disconnect container from network
docker network disconnect my-network container_id

# Remove network
docker network rm my-network

Container Communication

# docker-compose.yml
version: '3.8'

services:
  app:
    image: myapp
    networks:
      - frontend
      - backend

  db:
    image: postgres
    networks:
      - backend

  nginx:
    image: nginx
    networks:
      - frontend

networks:
  frontend:
  backend:

Docker Volumes

Volume Management

# Create volume
docker volume create my-volume

# List volumes
docker volume ls

# Inspect volume
docker volume inspect my-volume

# Remove volume
docker volume rm my-volume

# Remove unused volumes
docker volume prune

# Run container with volume
docker run -d -v my-volume:/data nginx

Volume Types

version: '3.8'

services:
  app:
    image: myapp
    volumes:
      # Named volume
      - app-data:/app/data

      # Bind mount
      - ./src:/app/src

      # tmpfs mount (in-memory)
      - type: tmpfs
        target: /app/temp

volumes:
  app-data:

Real-World Examples

Node.js Application

# Dockerfile
FROM node:18-alpine

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy app
COPY . .

# Create non-root user
RUN addgroup -g 1001 nodejs && \
    adduser -S nodejs -u 1001 && \
    chown -R nodejs:nodejs /app

USER nodejs

EXPOSE 3000

CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    restart: unless-stopped

Python Flask Application

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy app
COPY . .

# Create non-root user
RUN useradd -m -u 1000 appuser && \
    chown -R appuser:appuser /app

USER appuser

EXPOSE 5000

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

.NET Application

# Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src

COPY ["MyApp.csproj", "./"]
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /app
COPY --from=build /app/publish .

EXPOSE 80

ENTRYPOINT ["dotnet", "MyApp.dll"]

Docker Security Best Practices

1. Use Official Images

# Good
FROM node:18-alpine

# Bad
FROM random-user/node

2. Scan for Vulnerabilities

# Scan image
docker scan myapp:latest

# Use Trivy
trivy image myapp:latest

3. Use .dockerignore

node_modules
npm-debug.log
.git
.env
.DS_Store
*.md
.vscode

4. Run as Non-Root User

FROM node:18-alpine

RUN addgroup -g 1001 nodejs && \
    adduser -S nodejs -u 1001

USER nodejs

5. Limit Resources

version: '3.8'

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

Performance Optimization

1. Minimize Layers

# Bad - Multiple layers
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2

# Good - Single layer
RUN apt-get update && \
    apt-get install -y package1 package2 && \
    rm -rf /var/lib/apt/lists/*

2. Use Build Cache

# Copy dependency files first
COPY package*.json ./
RUN npm ci

# Copy source code last
COPY . .

3. Multi-Stage Builds

FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html

Debugging

# View container logs
docker logs container_id

# Execute shell in container
docker exec -it container_id sh

# Copy files from container
docker cp container_id:/app/logs ./logs

# View container processes
docker top container_id

# Monitor container stats
docker stats container_id

Conclusion

Docker simplifies application deployment and ensures consistency across environments. Master these concepts to build, ship, and run applications efficiently.

Resources

Enjoyed this article?

Explore more deep dives into architecture, performance, and modern .NET.

View All Articles