DevOps

🐳 Docker Containers & Images

Docker is a platform for building, shipping, and running applications in lightweight, isolated containers. Unlike virtual machines, containers share the host OS kernel, making them fast to start and efficient in resource usage.

Core Concepts

  • Image β€” A read-only, layered template built from a Dockerfile. Images are stored in registries.
  • Container β€” A running instance of an image. Containers are ephemeral unless volumes are used.
  • Layer β€” Each Dockerfile instruction creates an immutable layer. Layers are cached and shared between images.
  • Registry β€” A repository for images (Docker Hub, AWS ECR, GitHub Container Registry).
  • Volume β€” A persistent data store that survives container restarts and removals.

Writing Dockerfiles

A Dockerfile is a text file with instructions that Docker uses to build an image. Each instruction adds a new layer to the image.

πŸ’‘

Layer Caching Order instructions from least to most frequently changed. Put COPY package.json and RUN npm install before COPY . . to maximize cache reuse.

dockerfile
# Dockerfile β€” Node.js API (optimized for caching)
FROM node:20-alpine AS base
WORKDIR /app

# Install dependencies first (cached until package.json changes)
COPY package.json package-lock.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy source code
COPY src/ ./src/
COPY config/ ./config/

# Create non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Runtime configuration
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
  CMD wget -qO- http://localhost:8080/health || exit 1

CMD ["node", "src/index.js"]

Multi-Stage Builds

Multi-stage builds drastically reduce final image size by using separate build and runtime stages. Build tools, compilers, and test dependencies stay in the build stage and never reach production.

dockerfile
# Multi-stage build β€” Go application
# Stage 1: Build
FROM golang:1.22-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
  -ldflags="-w -s" \
  -o /app/server ./cmd/server

# Stage 2: Production image (~12MB vs ~350MB)
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

Networking

Docker provides three built-in network drivers:

  • bridge β€” Default. Isolated network on the host. Containers can communicate by name.
  • host β€” Container shares the host's network stack. Maximum performance, no isolation.
  • none β€” No networking. Used for batch jobs that don't need network access.
bash
# Create an isolated network
docker network create --driver bridge app-network

# Run containers on the same network (they resolve by name)
docker run -d --name postgres --network app-network postgres:16-alpine
docker run -d --name api --network app-network \
  -e DB_HOST=postgres \
  -p 8080:8080 \
  registry.maxiscomputers.com/api:2.1.0

# Inspect network
docker network inspect app-network

Docker Compose

Docker Compose defines and runs multi-container applications using a YAML file. Ideal for local development and integration testing.

yaml
# docker-compose.yml
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: builder
    ports:
      - "8080:8080"
    environment:
      - NODE_ENV=development
      - DB_URL=postgresql://user:pass@postgres:5432/appdb
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./src:/app/src:ro  # hot reload in dev
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: appdb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d appdb"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:

Container Security

⚠️

Never run as root Always create a dedicated non-root user in your Dockerfile. Running as root inside a container is a critical security risk.

  • Use distroless or Alpine base images to minimize the attack surface.
  • Scan images with docker scout, trivy, or grype in your CI pipeline.
  • Use read-only filesystems where possible: --read-only flag.
  • Never bake secrets into images. Use environment variables or secret mounts.
  • Set --cap-drop=ALL and add only required Linux capabilities.
  • Enable Docker Content Trust (DOCKER_CONTENT_TRUST=1) for image signing verification.

Docker CLI Reference

bash
# Build & Tag
docker build -t registry.maxiscomputers.com/api:2.1.0 --platform linux/amd64 .
docker tag registry.maxiscomputers.com/api:2.1.0 registry.maxiscomputers.com/api:latest

# Push & Pull
docker push registry.maxiscomputers.com/api:2.1.0
docker pull registry.maxiscomputers.com/api:2.1.0

# Run
docker run -d --name api -p 8080:8080 --restart unless-stopped \
  -e NODE_ENV=production \
  registry.maxiscomputers.com/api:2.1.0

# Inspect & Debug
docker ps -a
docker logs -f --tail 200 api
docker exec -it api sh
docker inspect api | jq '.[0].NetworkSettings'
docker stats  # live resource usage

# Cleanup
docker system prune -af --volumes   # remove ALL unused resources
docker image prune -f               # remove dangling images only
πŸ“± Install MC Wiki
Add to your home screen for offline access.