Docker & Kubernetes with ScoutQuest

Learn how to containerize your ScoutQuest services with Docker and deploy them to Kubernetes clusters with automatic service discovery and health monitoring.

Overview

This tutorial covers deploying ScoutQuest in containerized environments:

  • Dockerizing ScoutQuest services
  • Creating Docker Compose deployments
  • Deploying to Kubernetes
  • Service discovery in container orchestration
  • Health checks and monitoring

Dockerizing Services

Let's start by containerizing a Node.js service:

Dockerfile (Node.js Service)

# Use Node.js 22 Alpine for smaller image size
FROM node:22-alpine

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production && npm cache clean --force

# Copy application code
COPY . .

# Create non-root user for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

# Start the application
CMD ["node", "index.js"]

healthcheck.js

const http = require('http');

const options = {
    hostname: 'localhost',
    port: process.env.PORT || 3000,
    path: '/health',
    method: 'GET',
    timeout: 2000
};

const request = http.request(options, (res) => {
    if (res.statusCode === 200) {
        process.exit(0);
    } else {
        process.exit(1);
    }
});

request.on('error', () => {
    process.exit(1);
});

request.on('timeout', () => {
    request.destroy();
    process.exit(1);
});

request.end();

Dockerfile (Rust Service)

# Multi-stage build for Rust
FROM rust:1.70 as builder

WORKDIR /app
COPY . .

# Build the application
RUN cargo build --release

# Runtime stage
FROM debian:bookworm-slim

# Install ca-certificates for HTTPS requests
RUN apt-get update && apt-get install -y \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Create app user
RUN useradd -m -u 1001 appuser

WORKDIR /app

# Copy the binary from builder stage
COPY --from=builder /app/target/release/rust-service /app/rust-service

# Change ownership to app user
RUN chown -R appuser:appuser /app
USER appuser

EXPOSE 3000

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

CMD ["./rust-service"]

Docker Compose Setup

Create a complete development environment with Docker Compose:

docker-compose.yml

version: '3.8'

services:
  # ScoutQuest Server
  scoutquest-server:
    image: scoutquest/server:latest
    ports:
      - "8080:8080"
    environment:
      - RUST_LOG=info
      - HOST=0.0.0.0
      - PORT=8080
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - scoutquest-network

  # User Service
  user-service:
    build:
      context: ./user-service
      dockerfile: Dockerfile
    ports:
      - "3001:3000"
    environment:
      - PORT=3000
      - SCOUT_URL=http://scoutquest-server:8080
      - NODE_ENV=production
    depends_on:
      scoutquest-server:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "node", "healthcheck.js"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - scoutquest-network

  # Product Service
  product-service:
    build:
      context: ./product-service
      dockerfile: Dockerfile
    ports:
      - "3002:3000"
    environment:
      - PORT=3000
      - SCOUT_URL=http://scoutquest-server:8080
      - NODE_ENV=production
    depends_on:
      scoutquest-server:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "node", "healthcheck.js"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - scoutquest-network

  # Order Service (Rust)
  order-service:
    build:
      context: ./order-service
      dockerfile: Dockerfile
    ports:
      - "3003:3000"
    environment:
      - PORT=3000
      - SCOUT_URL=http://scoutquest-server:8080
      - RUST_LOG=info
    depends_on:
      scoutquest-server:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - scoutquest-network

  # API Gateway
  api-gateway:
    build:
      context: ./api-gateway
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
      - SCOUT_URL=http://scoutquest-server:8080
      - NODE_ENV=production
    depends_on:
      - user-service
      - product-service
      - order-service
    healthcheck:
      test: ["CMD", "node", "healthcheck.js"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - scoutquest-network

networks:
  scoutquest-network:
    driver: bridge

Running with Docker Compose

# Start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Check service health
docker-compose ps

# Stop all services
docker-compose down

Kubernetes Deployment

Deploy ScoutQuest to Kubernetes with proper service discovery:

k8s/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: scoutquest
  labels:
    name: scoutquest

k8s/scoutquest-server.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scoutquest-server
  namespace: scoutquest
spec:
  replicas: 2
  selector:
    matchLabels:
      app: scoutquest-server
  template:
    metadata:
      labels:
        app: scoutquest-server
    spec:
      containers:
      - name: scoutquest-server
        image: scoutquest/server:latest
        ports:
        - containerPort: 8080
        env:
        - name: HOST
          value: "0.0.0.0"
        - name: PORT
          value: "8080"
        - name: RUST_LOG
          value: "info"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
  name: scoutquest-server
  namespace: scoutquest
spec:
  selector:
    app: scoutquest-server
  ports:
  - port: 8080
    targetPort: 8080
  type: ClusterIP

k8s/user-service.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: scoutquest
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: scoutquest/user-service:latest
        ports:
        - containerPort: 3000
        env:
        - name: PORT
          value: "3000"
        - name: SCOUT_URL
          value: "http://scoutquest-server:8080"
        - name: NODE_ENV
          value: "production"
        - name: SERVICE_NAME
          value: "user-service"
        - name: SERVICE_HOST
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: scoutquest
spec:
  selector:
    app: user-service
  ports:
  - port: 3000
    targetPort: 3000
  type: ClusterIP

k8s/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: scoutquest-ingress
  namespace: scoutquest
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  - host: scoutquest.local
    http:
      paths:
      - path: /api/users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 3000
      - path: /api/products
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 3000
      - path: /api/orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 3000
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-gateway
            port:
              number: 3000

ConfigMaps and Secrets

Manage configuration and secrets in Kubernetes:

k8s/configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: scoutquest-config
  namespace: scoutquest
data:
  scout_url: "http://scoutquest-server:8080"
  log_level: "info"
  health_check_interval: "30"
  service_timeout: "5000"

k8s/secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: scoutquest-secrets
  namespace: scoutquest
type: Opaque
data:
  # Base64 encoded values
  api_key: Y2hhbmdlLW1lLXBsZWFzZQ==
  db_password: c3VwZXItc2VjcmV0LXBhc3M=

Using ConfigMaps and Secrets

spec:
  containers:
  - name: user-service
    image: scoutquest/user-service:latest
    envFrom:
    - configMapRef:
        name: scoutquest-config
    - secretRef:
        name: scoutquest-secrets
    env:
    - name: SERVICE_HOST
      valueFrom:
        fieldRef:
          fieldPath: status.podIP

Horizontal Pod Autoscaling

Automatically scale services based on resource usage:

k8s/hpa.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: user-service-hpa
  namespace: scoutquest
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: user-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15

Monitoring and Observability

Set up monitoring with Prometheus and Grafana:

k8s/service-monitor.yaml

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: scoutquest-metrics
  namespace: scoutquest
  labels:
    app: scoutquest
spec:
  selector:
    matchLabels:
      app: scoutquest-server
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Adding metrics to your service

const promClient = require('prom-client');

// Create metrics
const httpRequestDuration = new promClient.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.1, 0.5, 1, 2, 5]
});

const serviceDiscoveryRequests = new promClient.Counter({
  name: 'service_discovery_requests_total',
  help: 'Total number of service discovery requests',
  labelNames: ['service_name', 'operation']
});

// Middleware to collect metrics
app.use((req, res, next) => {
  const start = Date.now();

  res.on('finish', () => {
    const duration = (Date.now() - start) / 1000;
    httpRequestDuration
      .labels(req.method, req.route?.path || req.path, res.statusCode)
      .observe(duration);
  });

  next();
});

// Metrics endpoint
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', promClient.register.contentType);
  res.end(await promClient.register.metrics());
});

Deployment Scripts

Automate deployment with scripts:

scripts/deploy.sh

#!/bin/bash

set -e

NAMESPACE="scoutquest"
CONTEXT="production"

echo "🚀 Deploying ScoutQuest to Kubernetes..."

# Set kubectl context
kubectl config use-context "$CONTEXT"

# Create namespace if it doesn't exist
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -

# Apply all Kubernetes manifests
echo "📦 Applying Kubernetes manifests..."
kubectl apply -f k8s/ -n "$NAMESPACE"

# Wait for deployments to be ready
echo "⏳ Waiting for deployments to be ready..."
kubectl wait --for=condition=available --timeout=300s deployment/scoutquest-server -n "$NAMESPACE"
kubectl wait --for=condition=available --timeout=300s deployment/user-service -n "$NAMESPACE"
kubectl wait --for=condition=available --timeout=300s deployment/product-service -n "$NAMESPACE"
kubectl wait --for=condition=available --timeout=300s deployment/order-service -n "$NAMESPACE"

echo "✅ Deployment completed successfully!"

# Show status
echo "📊 Current status:"
kubectl get pods -n "$NAMESPACE"
kubectl get services -n "$NAMESPACE"

scripts/build-and-push.sh

#!/bin/bash

set -e

REGISTRY="your-registry.com"
TAG=${1:-latest}

services=("user-service" "product-service" "order-service" "api-gateway")

echo "🔨 Building and pushing Docker images..."

for service in "${services[@]}"; do
    echo "Building $service..."
    docker build -t "$REGISTRY/scoutquest/$service:$TAG" "./$service"
    docker push "$REGISTRY/scoutquest/$service:$TAG"
    echo "✅ $service pushed successfully"
done

echo "🎉 All images built and pushed successfully!"

Production Best Practices

  • Multi-stage builds: Use multi-stage Dockerfiles for smaller images
  • Health checks: Always implement proper health checks
  • Resource limits: Set appropriate CPU and memory limits
  • Secrets management: Use Kubernetes secrets for sensitive data
  • Network policies: Implement network segmentation
  • Monitoring: Set up comprehensive monitoring and alerting
  • Backup strategy: Implement backup and disaster recovery

Troubleshooting

Common issues and their solutions:

Check service registration

# Check if services are registered
kubectl exec -it deployment/scoutquest-server -n scoutquest -- curl localhost:8080/services

# Check service logs
kubectl logs deployment/user-service -n scoutquest --tail=50

# Debug service connectivity
kubectl exec -it deployment/user-service -n scoutquest -- nslookup scoutquest-server

Port forwarding for local debugging

# Forward ScoutQuest server port
kubectl port-forward service/scoutquest-server 8080:8080 -n scoutquest

# Forward service port
kubectl port-forward service/user-service 3001:3000 -n scoutquest

Next Steps

  • Set up CI/CD pipelines for automated deployment
  • Implement comprehensive monitoring
  • Configure service mesh (Istio/Linkerd) for advanced traffic management
  • Set up centralized logging with ELK stack
  • Implement canary deployments