Docker Deployment Guide

Overview

Two Dockerfile options are provided:

  1. Dockerfile - Dynamic linking (recommended for most use cases)
    • Runtime: cgr.dev/chainguard/glibc-dynamic
    • Size: ~20-30MB
    • Includes necessary shared libraries
  2. Dockerfile.static - Fully static binary (maximum security)
    • Runtime: cgr.dev/chainguard/static
    • Size: ~10-15MB
    • No dependencies, ultra-minimal

Why Chainguard Images?

  • Minimal attack surface - Only essential components
  • Daily updates - Automatic CVE patching
  • Non-root by default - Enhanced security
  • SBOM included - Software Bill of Materials
  • Signed with Sigstore - Supply chain security
  • No CVEs - Zero known vulnerabilities

Quick Start

1. Build the Image

Dynamic version (recommended):

docker build -t streamforge:latest .

Static version:

docker build -f Dockerfile.static -t streamforge:static .

2. Create Configuration

# Copy example config
cp config.example.json config.json

# Edit for your environment
vim config.json

3. Run the Container

docker run -d \
  --name streamforge \
  -v $(pwd)/config.json:/app/config/config.json:ro \
  -e RUST_LOG=info \
  --restart unless-stopped \
  streamforge:latest

4. Check Logs

docker logs -f streamforge

Docker Compose

Basic Usage

# Start with your config
docker-compose up -d

# View logs
docker-compose logs -f mirrormaker

# Stop
docker-compose down

With Local Kafka (for testing)

# Start Kafka + MirrorMaker
docker-compose --profile kafka up -d

# Check all services
docker-compose --profile kafka ps

Static Version

# Use the static build
docker-compose --profile static up -d mirrormaker-static

Configuration Options

Environment Variables

Variable Default Description
CONFIG_FILE /app/config/config.json Path to config file
RUST_LOG info Log level (trace, debug, info, warn, error)

Volume Mounts

docker run -d \
  --name streamforge \
  -v $(pwd)/config.json:/app/config/config.json:ro \  # Config (read-only)
  -v $(pwd)/logs:/app/logs \                          # Logs (optional)
  streamforge:latest

Network Modes

Bridge mode (default):

docker run --network bridge ...

Host mode (for local Kafka):

docker run --network host ...

Custom network:

docker network create kafka-network
docker run --network kafka-network ...

Resource Limits

docker run -d \
  --name streamforge \
  --cpus="2" \
  --memory="512m" \
  --memory-reservation="256m" \
  -v $(pwd)/config.json:/app/config/config.json:ro \
  streamforge:latest

In docker-compose.yml

deploy:
  resources:
    limits:
      cpus: '2'
      memory: 512M
    reservations:
      cpus: '1'
      memory: 256M

Health Checks

Built-in Health Check

The Dockerfile includes a health check:

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD pgrep -f streamforge || exit 1

Check Health Status

docker inspect --format='' streamforge | jq

Logging

View Logs

# Follow logs
docker logs -f streamforge

# Last 100 lines
docker logs --tail 100 streamforge

# With timestamps
docker logs -f --timestamps streamforge

Structured Logging

Set RUST_LOG for different verbosity:

# Info level (default)
docker run -e RUST_LOG=info ...

# Debug level
docker run -e RUST_LOG=debug ...

# Module-specific
docker run -e RUST_LOG=streamforge::kafka=debug,streamforge::processor=trace ...

Image Size Comparison

Image Size Security Use Case
Dynamic ~25MB High Production (recommended)
Static ~12MB Highest Maximum security
Java equivalent ~200MB+ Medium Legacy

Multi-Architecture Builds

Build for ARM64

docker buildx build \
  --platform linux/arm64 \
  -t streamforge:arm64 \
  .

Multi-arch Manifest

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t streamforge:latest \
  --push \
  .

Security Best Practices

1. Run as Non-Root ✅

Both Dockerfiles use non-root user by default.

# Verify
docker run --rm streamforge:latest id
# Should show: uid=65532(nonroot) gid=65532(nonroot)

2. Read-Only Root Filesystem

docker run -d \
  --read-only \
  --tmpfs /tmp \
  -v $(pwd)/config.json:/app/config/config.json:ro \
  streamforge:latest

3. Drop Capabilities

docker run -d \
  --cap-drop=ALL \
  --security-opt=no-new-privileges:true \
  streamforge:latest

4. Complete Secure Configuration

docker run -d \
  --name streamforge-secure \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=10m \
  --cap-drop=ALL \
  --security-opt=no-new-privileges:true \
  --cpus="2" \
  --memory="512m" \
  --pids-limit=100 \
  -v $(pwd)/config.json:/app/config/config.json:ro \
  streamforge:latest

Kubernetes Deployment

Basic Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: streamforge
spec:
  replicas: 3
  selector:
    matchLabels:
      app: streamforge
  template:
    metadata:
      labels:
        app: streamforge
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 65532
        fsGroup: 65532
      containers:
      - name: mirrormaker
        image: streamforge:latest
        imagePullPolicy: Always
        env:
        - name: CONFIG_FILE
          value: /app/config/config.json
        - name: RUST_LOG
          value: info
        resources:
          requests:
            memory: "256Mi"
            cpu: "500m"
          limits:
            memory: "512Mi"
            cpu: "2000m"
        volumeMounts:
        - name: config
          mountPath: /app/config
          readOnly: true
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
      volumes:
      - name: config
        configMap:
          name: mirrormaker-config

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: mirrormaker-config
data:
  config.json: |
    {
      "appid": "streamforge",
      "bootstrap": "kafka-broker:9092",
      "input": "source-topic",
      "output": "destination-topic",
      "offset": "latest",
      "threads": 4
    }

Troubleshooting

Container Won’t Start

# Check logs
docker logs streamforge

# Run interactively
docker run --rm -it \
  -v $(pwd)/config.json:/app/config/config.json:ro \
  streamforge:latest

Config Validation

# Test config file
docker run --rm \
  -v $(pwd)/config.json:/app/config/config.json:ro \
  streamforge:latest --help

Network Issues

# Test connectivity to Kafka
docker run --rm --network host nicolaka/netshoot \
  nc -zv kafka-broker 9092

Permission Issues

# Check file permissions
ls -l config.json

# Should be readable by all
chmod 644 config.json

Performance Monitoring

Container Stats

docker stats streamforge

Resource Usage

# CPU and memory
docker inspect streamforge | jq '.[0].HostConfig.Memory'

# Current usage
docker stats --no-stream --format "table \t\t" streamforge

CI/CD Integration

GitHub Actions Example

name: Build and Push Docker Image

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Build Docker image
      run: docker build -t streamforge:$ .

    - name: Run tests
      run: docker run --rm streamforge:$ cargo test

    - name: Push to registry
      run: |
        echo "$" | docker login -u "$" --password-stdin
        docker push streamforge:$

Best Practices Summary

✅ Use Chainguard base images for security ✅ Multi-stage builds to minimize size ✅ Run as non-root user (uid 65532) ✅ Mount config as read-only ✅ Set resource limits ✅ Use health checks ✅ Enable structured logging ✅ Read-only root filesystem ✅ Drop all capabilities ✅ Regular image updates

Image Registry

Push to Registry

# Tag
docker tag streamforge:latest your-registry.com/streamforge:latest

# Push
docker push your-registry.com/streamforge:latest

Pull from Registry

docker pull your-registry.com/streamforge:latest

Questions?

See:

  • README.md - Application overview
  • QUICKSTART.md - Getting started
  • IMPLEMENTATION_NOTES.md - Architecture details

Back to top

StreamForge — selective replication for Kafka, with Redpanda as a compatibility target. Apache 2.0 Licensed.

This site uses Just the Docs, a documentation theme for Jekyll.