Docker for Development Environments - Set up consistent development environments using Docker containers for any project or team. Master D...
Development Tools

Docker for Development Environments

Set up consistent development environments using Docker containers for any project or team. Master Docker for development workflows.

TechDevDex Team
12/1/2024
22 min
#Docker#Development Environment#Containers#DevOps#Consistency#Team Collaboration

Docker for Development Environments

Docker revolutionizes development by providing consistent, isolated environments that work identically across different machines and operating systems. This guide covers everything you need to know about using Docker for development.

Why Docker for Development?

Benefits

  • Consistency: Same environment on all machines
  • Isolation: No conflicts between projects
  • Reproducibility: Easy to recreate exact environment
  • Team Collaboration: Everyone works with identical setup
  • Easy Onboarding: New team members can start immediately
  • Cross-Platform: Works on Windows, macOS, and Linux

Common Use Cases

  • Microservices Development: Each service in its own container
  • Database Development: Isolated database instances
  • Testing: Consistent test environments
  • CI/CD: Reproducible build environments
  • Legacy Applications: Modernize old applications

Basic Docker Setup

Installation

bash
# Ubuntu/Debian
sudo apt update
sudo apt install docker.io docker-compose
sudo systemctl start docker
sudo systemctl enable docker

# Add user to docker group
sudo usermod -aG docker $USER

# macOS (using Homebrew)
brew install --cask docker

# Windows
# Download Docker Desktop from docker.com

Basic Commands

bash
# Check Docker version
docker --version
docker-compose --version

# Run hello world
docker run hello-world

# List running containers
docker ps

# List all containers
docker ps -a

# List images
docker images

# Remove containers
docker rm <container_id>

# Remove images
docker rmi <image_id>

Development Dockerfile

Node.js Development

dockerfile
# Dockerfile
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Expose port
EXPOSE 3000

# Start development server
CMD ["npm", "run", "dev"]

Python Development

dockerfile
# Dockerfile
FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# Copy requirements
COPY requirements.txt .

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

# Copy source code
COPY . .

# Expose port
EXPOSE 8000

# Start development server
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

Multi-Stage Development

dockerfile
# Multi-stage Dockerfile
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM base AS development
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]

FROM base AS production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

Docker Compose for Development

Basic Compose File

yaml
# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

Development Override

yaml
# docker-compose.override.yml
version: '3.8'

services:
  web:
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - DEBUG=true
      - LOG_LEVEL=debug
    command: npm run dev

  db:
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=myapp_dev

Advanced Development Patterns

Hot Reloading

dockerfile
# Dockerfile with hot reload
FROM node:18-alpine

WORKDIR /app

# Install nodemon globally
RUN npm install -g nodemon

COPY package*.json ./
RUN npm ci

COPY . .

EXPOSE 3000

# Use nodemon for hot reloading
CMD ["nodemon", "src/index.js"]

Volume Mounting

yaml
# docker-compose.yml
services:
  web:
    build: .
    volumes:
      # Mount source code
      - .:/app
      # Exclude node_modules
      - /app/node_modules
      # Mount specific files
      - ./config:/app/config
    ports:
      - "3000:3000"

Environment Variables

yaml
# docker-compose.yml
services:
  web:
    build: .
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://user:password@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    env_file:
      - .env.development

Database Development

PostgreSQL Setup

yaml
# docker-compose.yml
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: developer
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

  adminer:
    image: adminer
    ports:
      - "8080:8080"
    depends_on:
      - db

volumes:
  postgres_data:

MySQL Setup

yaml
# docker-compose.yml
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: developer
      MYSQL_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
  mysql_data:

Microservices Development

Service Architecture

yaml
# docker-compose.yml
version: '3.8'

services:
  api-gateway:
    build: ./api-gateway
    ports:
      - "3000:3000"
    depends_on:
      - user-service
      - order-service

  user-service:
    build: ./user-service
    ports:
      - "3001:3000"
    depends_on:
      - user-db

  order-service:
    build: ./order-service
    ports:
      - "3002:3000"
    depends_on:
      - order-db

  user-db:
    image: postgres:15
    environment:
      POSTGRES_DB: users
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password

  order-db:
    image: postgres:15
    environment:
      POSTGRES_DB: orders
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password

Development Tools Integration

VS Code Integration

json
// .vscode/settings.json
{
  "docker.defaultRegistryPath": "localhost:5000",
  "docker.showStartPage": false,
  "docker.containers.sortBy": "CreatedTime"
}

Debugging

dockerfile
# Dockerfile with debugging
FROM node:18-alpine

WORKDIR /app

# Install debugging tools
RUN npm install -g node-inspector

COPY package*.json ./
RUN npm ci

COPY . .

# Expose debug port
EXPOSE 9229

# Start with debugging
CMD ["node", "--inspect=0.0.0.0:9229", "src/index.js"]

Logging

yaml
# docker-compose.yml
services:
  web:
    build: .
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Performance Optimization

Multi-Stage Builds

dockerfile
# Multi-stage build for production
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

Layer Caching

dockerfile
# Optimize layer caching
FROM node:18-alpine

WORKDIR /app

# Copy package files first (for better caching)
COPY package*.json ./
RUN npm ci

# Copy source code last
COPY . .

EXPOSE 3000
CMD ["npm", "start"]

Development Workflows

Git Hooks

bash
#!/bin/sh
# .git/hooks/pre-commit

# Build Docker image
docker build -t myapp:dev .

# Run tests in container
docker run --rm myapp:dev npm test

if [ $? -ne 0 ]; then
    echo "Tests failed. Commit aborted."
    exit 1
fi

CI/CD Integration

yaml
# .github/workflows/docker.yml
name: Docker Build

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Build Docker image
      run: docker build -t myapp:${{ github.sha }} .
    
    - name: Run tests
      run: docker run --rm myapp:${{ github.sha }} npm test

Troubleshooting

Common Issues

bash
# Container won't start
docker logs <container_id>

# Permission issues
sudo chown -R $USER:$USER .

# Port conflicts
docker-compose down
docker-compose up -d

# Clean up
docker system prune -a

Debugging Commands

bash
# Enter running container
docker exec -it <container_id> /bin/bash

# Check container logs
docker logs -f <container_id>

# Inspect container
docker inspect <container_id>

# Check resource usage
docker stats

Best Practices

Security

dockerfile
# Use non-root user
FROM node:18-alpine

# Create app user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Set user
USER nextjs

WORKDIR /app
COPY --chown=nextjs:nodejs . .

Resource Limits

yaml
# docker-compose.yml
services:
  web:
    build: .
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
        reservations:
          memory: 256M
          cpus: '0.25'

Conclusion

Docker for development environments provides consistency, isolation, and reproducibility that significantly improves team collaboration and development workflows. By following the patterns and practices outlined in this guide, you can create robust, maintainable development environments that work seamlessly across different machines and team members.

The key to successful Docker development is understanding the balance between containerization benefits and development convenience, choosing the right patterns for your team's needs, and maintaining good practices for security and performance.