Docker Containerization: A Complete Developer's Guide
Docker has revolutionized how we develop, ship, and run applications. This comprehensive guide will take you from Docker basics to advanced containerization strategies.
What is Docker?
Docker is a platform that uses containerization technology to package applications and their dependencies into lightweight, portable containers that can run consistently across different environments.
Installing Docker
macOS and Windows
Download Docker Desktop from the official website and follow the installation wizard.
Linux (Ubuntu)
# Update package index
sudo apt update
# Install required packages
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# Add Docker repository
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# Install Docker
sudo apt update
sudo apt install docker-ce
Creating Your First Dockerfile
A Dockerfile is a text file that contains instructions for building a Docker image:
# Use official Node.js runtime as base image
FROM node:18-alpine
# Set working directory in container
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Create non-root user for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Change ownership of app directory
RUN chown -R nextjs:nodejs /app
USER nextjs
# Start the application
CMD ["npm", "start"]
Docker Commands Cheat Sheet
Building and Running
# Build image from Dockerfile
docker build -t my-app .
# Run container
docker run -p 3000:3000 my-app
# Run container in detached mode
docker run -d -p 3000:3000 --name my-app-container my-app
# Run with environment variables
docker run -e NODE_ENV=production -p 3000:3000 my-app
Managing Containers
# List running containers
docker ps
# List all containers
docker ps -a
# Stop container
docker stop container-name
# Remove container
docker rm container-name
# View container logs
docker logs container-name
# Execute command in running container
docker exec -it container-name bash
Docker Compose for Multi-Service Applications
Create a docker-compose.yml
file for complex applications:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/myapp
depends_on:
- db
- redis
volumes:
- ./uploads:/app/uploads
db:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
volumes:
postgres_data:
redis_data:
Best Practices for Production
1. Use Multi-Stage Builds
# 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 AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/index.js"]
2. Optimize Image Size
- Use Alpine Linux base images
- Remove unnecessary packages
- Use
.dockerignore
file - Minimize layers
3. Security Considerations
# Don't run as root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# Use specific image versions
FROM node:18.16.0-alpine
# Scan images for vulnerabilities
# docker scan my-app:latest
Docker Networking
Bridge Network (Default)
# Create custom bridge network
docker network create my-network
# Run containers on custom network
docker run --network my-network --name app1 my-app
docker run --network my-network --name app2 my-database
Host Network
# Use host networking
docker run --network host my-app
Persistent Data with Volumes
Named Volumes
# Create volume
docker volume create my-data
# Use volume in container
docker run -v my-data:/app/data my-app
# List volumes
docker volume ls
# Remove volume
docker volume rm my-data
Bind Mounts
# Mount host directory
docker run -v /host/path:/container/path my-app
# Mount current directory (development)
docker run -v $(pwd):/app my-app
Monitoring and Debugging
Container Stats
# View resource usage
docker stats
# View specific container stats
docker stats container-name
Health Checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CI/CD with Docker
GitHub Actions Example
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t my-app:${{ github.sha }} .
- name: Run tests
run: docker run my-app:${{ github.sha }} npm test
- name: Push to registry
run: |
docker tag my-app:${{ github.sha }} registry.com/my-app:latest
docker push registry.com/my-app:latest
Conclusion
Docker containerization provides consistency, portability, and scalability for modern applications. By following these practices and understanding core concepts, you can leverage Docker to streamline your development workflow and improve deployment reliability.
Start with simple Dockerfiles, progress to multi-stage builds, and eventually implement full orchestration with Docker Compose or Kubernetes for production environments.