Containerization & Deployment

Introduction

Containers provide the isolation and portability that microservices need. Each service packages its dependencies, runs in its own environment, and can be deployed independently. From deploying microservices in production, I've learned that proper containerization is foundational to success.

This article covers Docker best practices, multi-stage builds, and Docker Compose for local development.

Why Containers for Microservices?

spinner
Benefit
Description

Isolation

Each service runs in its own environment

Portability

Works the same in dev, test, and prod

Resource efficiency

Share OS kernel, lighter than VMs

Fast startup

Seconds vs minutes for VMs

Immutability

Build once, run anywhere

Dockerfile Best Practices

Multi-Stage Build

# Dockerfile for Python FastAPI service

# Stage 1: Build dependencies
FROM python:3.11-slim AS builder

WORKDIR /app

# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

# Stage 2: Runtime image
FROM python:3.11-slim AS runtime

# Create non-root user
RUN groupadd -r appgroup && useradd -r -g appgroup appuser

WORKDIR /app

# Install runtime dependencies only
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Copy wheels from builder
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels

# Copy application code
COPY --chown=appuser:appgroup ./src ./src
COPY --chown=appuser:appgroup ./alembic ./alembic
COPY --chown=appuser:appgroup alembic.ini .

# Switch to non-root user
USER appuser

# Expose port
EXPOSE 8000

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8000/health/live || exit 1

# Run application
CMD ["python", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

Layer Caching Optimization

Environment-Specific Builds

Docker Compose for Local Development

Complete Microservices Stack

Development Overrides

Production Configuration

Service Startup Scripts

Entrypoint Script

Health-Aware Startup

Container Security

Security Scanning

Secure Dockerfile

Useful Commands

Key Takeaways

  1. Multi-stage builds - Smaller images, faster deployments

  2. Layer caching - Order instructions to maximize cache hits

  3. Non-root users - Security best practice

  4. Health checks - Enable container orchestration to manage lifecycle

  5. Docker Compose for dev - Mirror production locally

What's Next?

With containerized services, we need to manage service-to-service communication at scale. In Article 12: Service Mesh & Governance, we'll explore service mesh concepts, mTLS, and team organization patterns.


This article is part of the Microservice Architecture 101 series.

Last updated