Shortcuts
BlogNovember 20, 2024

Mohamed Elbarry
Containerization with Docker
Docker is a containerization platform that allows you to package applications and their dependencies into lightweight, portable containers. Unlike virtual machines, containers share the host OS kernel, making them more efficient and faster to start.
Bash
# Image: A template for creating containers
docker build -t my-app:1.0 .

# Container: A running instance of an image
docker run -p 3000:3000 my-app:1.0
Bash
# Build an image
docker build -t my-app .

# Run a container
docker run -d -p 3000:3000 --name my-app-container my-app

# List running containers
docker ps

# Stop a container
docker stop my-app-container

# Remove a container
docker rm my-app-container

# List images
docker images

# Remove an image
docker rmi my-app
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 . .

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

# Change ownership of the app directory
RUN chown -R nextjs:nodejs /app
USER nextjs

# Expose port
EXPOSE 3000

# Start the application
CMD ["npm", "start"]
Dockerfile
# Build stage
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install all dependencies (including dev dependencies)
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Production stage
FROM node:18-alpine AS runner

WORKDIR /app

# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy built application from builder stage
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

# Switch to non-root user
USER nextjs

# Expose port
EXPOSE 3000

# Start the application
CMD ["node", "server.js"]
Yaml
version: '3.8'

services:
  # Frontend service
  frontend:
    build: ./frontend
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=development
      - REACT_APP_API_URL=http://localhost:8000
    volumes:
      - ./frontend:/app
      - /app/node_modules
    depends_on:
      - backend

  # Backend service
  backend:
    build: ./backend
    ports:
      - '8000:8000'
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db

  # Database service
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - '5432:5432'

volumes:
  postgres_data:
Yaml
version: '3.8'

services:
  # Frontend service
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      - REACT_APP_API_URL=https://api.myapp.com
    depends_on:
      - backend

  # Backend service
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile.prod
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - db

  # Database service
  db:
    image: postgres:15-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
Yaml
# docker-compose.yml
version: '3.8'

services:
  frontend:
    build: ./frontend
    networks:
      - frontend-network
      - backend-network

  backend:
    build: ./backend
    networks:
      - backend-network
      - database-network

  db:
    image: postgres:15-alpine
    networks:
      - database-network

networks:
  frontend-network:
    driver: bridge
  backend-network:
    driver: bridge
  database-network:
    driver: bridge
Dockerfile
# Scan image for vulnerabilities
docker scan my-app:latest

# Use specific base images
FROM node:18.17.0-alpine3.18

# Remove package manager cache
RUN apk add --no-cache curl \
    && rm -rf /var/cache/apk/*

# Use non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
  • Start simple - Begin with basic Dockerfiles and gradually add complexity
  • Use multi-stage builds - Optimize image size and security
  • Implement proper networking - Design your container communication carefully
  • Secure your containers - Follow security best practices
  • Monitor and log - Implement proper observability
  • Plan for production - Design with scalability in mind
Remember, containerization is not just about packaging applications—it's about creating a consistent, reliable, and scalable development and deployment environment.
Share this post: