Day 26: Performance Optimization and Tuning

Learning Objectives: By the end of this lesson, you will be able to:

  • Analyze and optimize Docker container performance
  • Implement resource management and limits for optimal efficiency
  • Optimize Docker images for size and startup time
  • Configure networking and storage for maximum performance
  • Monitor and troubleshoot performance bottlenecks

1. Welcome and Introduction (5 minutes)

Icebreaker Activity

“Your containerized application is running, but it’s slower than expected. Users are complaining about response times, your cloud bills are skyrocketing, and containers are consuming more resources than anticipated. You need to squeeze every bit of performance out of your system while reducing costs. Today we’ll learn how to turn sluggish containers into high-performance powerhouses through systematic optimization techniques.”

Lesson Overview

  • Duration: 90 minutes
  • Prerequisites: Docker fundamentals, container monitoring, resource management basics
  • Materials Needed: Docker environment, monitoring tools, sample applications for optimization

2. Learning Objectives and Session Outline (5 minutes)

What We’ll Cover Today:

  1. Performance Analysis - Identifying bottlenecks and measuring performance
  2. Resource Optimization - CPU, memory, and I/O tuning
  3. Image Optimization - Reducing size and improving startup times
  4. Runtime Optimization - Container configuration and runtime tuning
  5. Advanced Techniques - Caching, networking, and storage optimization

Success Criteria:

By the end of this session, you should be able to systematically analyze container performance, implement optimization strategies, and achieve significant improvements in resource utilization and application responsiveness.

3. Foundation Knowledge (15-20 minutes)

Core Concepts

Performance Metrics That Matter

Understanding key performance indicators for containerized applications:

  1. Resource Utilization: CPU, memory, disk, and network usage
  2. Application Metrics: Response time, throughput, error rates
  3. Container Metrics: Startup time, restart frequency, health status
  4. Infrastructure Metrics: Host utilization, storage I/O, network latency

Real-World Analogy: Think of performance optimization like tuning a race car. You need to monitor engine performance (CPU), fuel efficiency (memory), aerodynamics (network), and tire wear (storage I/O). Each component affects overall performance, and optimizing one area might impact others. The goal is finding the perfect balance for your specific race conditions (workload).

Why This Matters: Poor container performance leads to increased costs, poor user experience, and inefficient resource utilization. Systematic optimization can reduce costs by 30-70% while improving application responsiveness.

Performance Optimization Principles

  • Measure First: Establish baselines before making changes
  • Profile Everything: Understand where time and resources are spent
  • Incremental Changes: Make one change at a time and measure impact
  • Trade-off Awareness: Optimizing one metric may impact others
  • Right-sizing: Match resources to actual needs, not peak estimates

Common Performance Bottlenecks

  • CPU Bound: Application logic, inefficient algorithms
  • Memory Bound: Large data sets, memory leaks, inefficient caching
  • I/O Bound: Database queries, file operations, network calls
  • Network Bound: API calls, service-to-service communication
  • Container Overhead: Large images, slow startup, unnecessary processes

Key Terminology

  • Hot Path: Code paths executed frequently during normal operation
  • Cold Start: Time required to start a container from a stopped state
  • Working Set: Amount of memory actively used by an application
  • IOPS: Input/Output Operations Per Second for storage performance
  • Latency vs Throughput: Response time vs. number of requests handled

4. Detailed Explanation and Examples (25-30 minutes)

Understanding Container Performance Characteristics

Containers introduce both benefits and challenges for performance:

Benefits

  • Isolation: Predictable resource allocation
  • Density: Higher utilization than VMs
  • Consistency: Same environment across deployments
  • Orchestration: Automatic scaling and load distribution

Challenges

  • Overhead: Container runtime and orchestration layers
  • Resource Sharing: Containers compete for host resources
  • Network Virtualization: Additional network hops and processing
  • Storage Abstraction: Volume mounts and overlay filesystems

Resource Management and Limits

CPU Optimization

Understanding CPU allocation and optimization:

# Monitor CPU usage
docker stats --no-stream

# Set CPU limits (Docker)
docker run --cpus="1.5" --cpu-period=100000 --cpu-quota=150000 myapp

# Set CPU requests and limits (Kubernetes)
resources:
  requests:
    cpu: 100m      # 0.1 CPU cores
  limits:
    cpu: 500m      # 0.5 CPU cores

Memory Optimization

Memory management is critical for container performance:

# Monitor memory usage
docker exec container_name cat /proc/meminfo

# Set memory limits
docker run --memory="512m" --memory-swap="1g" myapp

# Enable memory accounting
docker run --memory="512m" --oom-kill-disable=false myapp

I/O Optimization

Storage and network I/O optimization:

# Monitor I/O
docker exec container_name iostat -x 1

# Set I/O limits
docker run --device-read-bps /dev/sda:1mb --device-write-bps /dev/sda:1mb myapp

# Use tmpfs for temporary data
docker run --tmpfs /tmp:rw,noexec,nosuid,size=100m myapp

Image Optimization Strategies

Multi-stage Build Optimization

Reduce image size with efficient multi-stage builds:

# Optimized multi-stage build
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Remove development dependencies and unnecessary files
FROM node:16-alpine AS runtime
RUN apk add --no-cache dumb-init && \
    addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001 -G nodejs

WORKDIR /app
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .

# Remove unnecessary files
RUN rm -rf .git .gitignore README.md Dockerfile* tests/ && \
    npm prune --production

USER nextjs
EXPOSE 3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]

Base Image Selection

Choose optimal base images:

# Size comparison of base images
# ubuntu:20.04     ~72MB
# alpine:latest    ~5MB
# distroless       ~2MB
# scratch         0MB (for static binaries)

# Distroless example for smaller images
FROM node:16-alpine AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build

FROM gcr.io/distroless/nodejs:16
COPY --from=builder /app/dist /app
EXPOSE 3000
CMD ["index.js"]

Runtime Performance Optimization

Container Configuration

Optimize container runtime settings:

version: '3.8'

services:
  optimized-app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
    environment:
      - NODE_ENV=production
      - NODE_OPTIONS=--max-old-space-size=400
    volumes:
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100M
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

JVM Optimization (for Java applications)

FROM openjdk:11-jre-slim

ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"

COPY app.jar /app.jar
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app.jar"]

Common Misconceptions

  1. Misconception: Smaller containers are always faster Reality: Startup time and runtime performance depend on application design, not just image size

  2. Misconception: More CPU cores always improve performance Reality: Single-threaded applications can’t utilize multiple cores effectively

5. Live Coding Demonstration (20-25 minutes)

Example 1: Performance Monitoring and Analysis

#!/bin/bash
# performance-analysis.sh - Comprehensive performance monitoring

CONTAINER_NAME="$1"

if [ -z "$CONTAINER_NAME" ]; then
    echo "Usage: $0 <container_name>"
    exit 1
fi

echo "=== Container Performance Analysis for $CONTAINER_NAME ==="

echo "1. Basic Container Stats:"
docker stats --no-stream "$CONTAINER_NAME"

echo -e "\n2. Detailed Resource Usage:"
docker exec "$CONTAINER_NAME" cat /proc/meminfo | grep -E "(MemTotal|MemFree|MemAvailable|Cached)"
docker exec "$CONTAINER_NAME" cat /proc/loadavg

echo -e "\n3. Process Information:"
docker exec "$CONTAINER_NAME" ps aux --sort=-%cpu | head -10

echo -e "\n4. Network Statistics:"
docker exec "$CONTAINER_NAME" cat /proc/net/dev

echo -e "\n5. Disk I/O Statistics:"
docker exec "$CONTAINER_NAME" cat /proc/diskstats

echo -e "\n6. Container Limits:"
CONTAINER_ID=$(docker inspect --format='{{.Id}}' "$CONTAINER_NAME")
echo "Memory Limit: $(cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.limit_in_bytes)"
echo "CPU Shares: $(cat /sys/fs/cgroup/cpu/docker/$CONTAINER_ID/cpu.shares)"

echo -e "\n7. Application-Specific Metrics:"
if docker exec "$CONTAINER_NAME" command -v curl > /dev/null; then
    echo "Health Check Response Time:"
    time docker exec "$CONTAINER_NAME" curl -s http://localhost:8080/health > /dev/null
fi

Example 2: Image Optimization Techniques

# Before optimization (large image)
FROM ubuntu:20.04

RUN apt-get update && apt-get install -y \
    nodejs \
    npm \
    git \
    curl \
    wget \
    vim \
    && npm install -g nodemon

WORKDIR /app
COPY . .
RUN npm install

EXPOSE 3000
CMD ["npm", "start"]
# After optimization (smaller, faster image)
FROM node:16-alpine AS dependencies

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

FROM node:16-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production

FROM node:16-alpine AS runtime

# Add non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001 -G nodejs

# Install only runtime dependencies
RUN apk add --no-cache dumb-init

WORKDIR /app

# Copy production dependencies and built application
COPY --from=dependencies --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=nextjs:nodejs /app/dist ./dist
COPY --from=build --chown=nextjs:nodejs /app/package.json ./

USER nextjs

EXPOSE 3000

# Use dumb-init for proper signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]

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

Example 3: Resource Optimization with Monitoring

# docker-compose.performance.yml
version: '3.8'

services:
  app:
    build: .
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    environment:
      - NODE_ENV=production
      - NODE_OPTIONS=--max-old-space-size=800
    volumes:
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 256M
      - app-cache:/app/cache
    networks:
      - app-network
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 30s

  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.1'
          memory: 128M
    volumes:
      - redis-data:/data
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - nginx-cache:/var/cache/nginx
    ports:
      - "80:80"
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
        reservations:
          cpus: '0.1'
          memory: 64M
    networks:
      - app-network

  # Performance monitoring
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    networks:
      - monitoring

networks:
  app-network:
    driver: bridge
  monitoring:
    driver: bridge

volumes:
  app-cache:
  redis-data:
  nginx-cache:
  prometheus-data:

Optimized Nginx Configuration:

# nginx.conf - Performance optimized
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Performance optimizations
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 100;

    # Compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss;

    # Caching
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m;

    upstream app_backend {
        least_conn;
        server app:3000 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    server {
        listen 80;
        server_name localhost;

        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        # Static file caching
        location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        # API proxy with caching
        location /api/ {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_cache_bypass $http_upgrade;

            # Caching for GET requests
            proxy_cache app_cache;
            proxy_cache_valid 200 5m;
            proxy_cache_use_stale error timeout invalid_header updating;
        }

        # Main application
        location / {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
}

Example 4: Application-Level Optimization

// optimized-server.js - Performance optimized Node.js application
const express = require('express');
const compression = require('compression');
const helmet = require('helmet');
const redis = require('redis');
const cluster = require('cluster');
const os = require('os');

const app = express();
const port = process.env.PORT || 3000;

// Cluster for multi-core utilization
if (cluster.isMaster && process.env.NODE_ENV === 'production') {
    const numCPUs = os.cpus().length;
    console.log(`Master ${process.pid} is running`);

    // Fork workers
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`Worker ${worker.process.pid} died`);
        cluster.fork();
    });
} else {
    // Worker process
    
    // Redis connection with connection pooling
    const redisClient = redis.createClient({
        host: process.env.REDIS_HOST || 'redis',
        port: process.env.REDIS_PORT || 6379,
        retry_strategy: (options) => {
            if (options.error && options.error.code === 'ECONNREFUSED') {
                return new Error('The server refused the connection');
            }
            if (options.total_retry_time > 1000 * 60 * 60) {
                return new Error('Retry time exhausted');
            }
            if (options.attempt > 10) {
                return undefined;
            }
            return Math.min(options.attempt * 100, 3000);
        }
    });

    // Middleware for performance
    app.use(helmet()); // Security headers
    app.use(compression()); // Gzip compression
    app.use(express.json({ limit: '1mb' })); // Limit request size

    // Response time middleware
    app.use((req, res, next) => {
        const start = Date.now();
        res.on('finish', () => {
            const duration = Date.now() - start;
            console.log(`${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`);
        });
        next();
    });

    // Caching middleware
    const cache = (duration) => {
        return async (req, res, next) => {
            const key = `cache:${req.originalUrl}`;
            try {
                const cached = await redisClient.get(key);
                if (cached) {
                    return res.json(JSON.parse(cached));
                }
                
                const originalSend = res.json;
                res.json = function(data) {
                    redisClient.setex(key, duration, JSON.stringify(data));
                    originalSend.call(this, data);
                };
                next();
            } catch (error) {
                next();
            }
        };
    };

    // Health check endpoint
    app.get('/health', (req, res) => {
        res.status(200).json({
            status: 'healthy',
            timestamp: new Date().toISOString(),
            uptime: process.uptime(),
            memory: process.memoryUsage(),
            pid: process.pid
        });
    });

    // API endpoints with caching
    app.get('/api/data', cache(300), async (req, res) => {
        // Simulate expensive operation
        const data = await fetchExpensiveData();
        res.json(data);
    });

    // Graceful shutdown
    process.on('SIGTERM', () => {
        console.log('SIGTERM received, shutting down gracefully');
        server.close(() => {
            redisClient.quit();
            process.exit(0);
        });
    });

    const server = app.listen(port, () => {
        console.log(`Worker ${process.pid} listening on port ${port}`);
    });

    // Keep-alive timeout
    server.keepAliveTimeout = 65000;
    server.headersTimeout = 66000;
}

async function fetchExpensiveData() {
    // Simulate database query or API call
    return new Promise(resolve => {
        setTimeout(() => {
            resolve({ data: 'expensive data', timestamp: Date.now() });
        }, 100);
    });
}

Example 5: Performance Testing and Benchmarking

#!/bin/bash
# performance-benchmark.sh - Automated performance testing

APP_URL="http://localhost:80"
CONTAINER_NAME="myapp_app_1"

echo "=== Performance Benchmark Starting ==="

# Baseline metrics
echo "1. Collecting baseline metrics..."
docker stats --no-stream "$CONTAINER_NAME" > baseline-stats.txt

# Load testing with different scenarios
echo "2. Running load tests..."

# Light load test
echo "Light load (10 concurrent users):"
ab -n 1000 -c 10 "$APP_URL/" > light-load.txt

# Medium load test
echo "Medium load (50 concurrent users):"
ab -n 5000 -c 50 "$APP_URL/" > medium-load.txt

# Heavy load test
echo "Heavy load (100 concurrent users):"
ab -n 10000 -c 100 "$APP_URL/" > heavy-load.txt

# Stress test
echo "Stress test (200 concurrent users):"
ab -n 20000 -c 200 "$APP_URL/" > stress-test.txt

# Collect metrics during load
echo "3. Collecting performance metrics..."
for i in {1..60}; do
    docker stats --no-stream "$CONTAINER_NAME" >> load-stats.txt
    sleep 1
done

# Memory usage over time
echo "4. Memory usage analysis..."
docker exec "$CONTAINER_NAME" cat /proc/meminfo | grep MemAvailable >> memory-usage.txt

# Generate performance report
echo "5. Generating performance report..."
cat > performance-report.md << EOF
# Performance Test Report
Generated: $(date)

## Test Environment
- Container: $CONTAINER_NAME
- Target URL: $APP_URL
- Test Duration: 60 seconds

## Load Test Results

### Light Load (10 concurrent users)
\`\`\`
$(grep -E "(Requests per second|Time per request|Transfer rate)" light-load.txt)
\`\`\`

### Medium Load (50 concurrent users)
\`\`\`
$(grep -E "(Requests per second|Time per request|Transfer rate)" medium-load.txt)
\`\`\`

### Heavy Load (100 concurrent users)
\`\`\`
$(grep -E "(Requests per second|Time per request|Transfer rate)" heavy-load.txt)
\`\`\`

### Stress Test (200 concurrent users)
\`\`\`
$(grep -E "(Requests per second|Time per request|Transfer rate)" stress-test.txt)
\`\`\`

## Resource Utilization
\`\`\`
$(tail -5 load-stats.txt)
\`\`\`

## Recommendations
- Review CPU usage patterns
- Analyze memory consumption trends
- Consider scaling if response times degrade
EOF

echo "Performance benchmark completed!"
echo "Report saved as: performance-report.md"

6. Hands-On Practice (30-40 minutes)

Exercise 1: Image Optimization Challenge (15 minutes)

Objective: Optimize a bloated Docker image for size and performance

Instructions:

  1. Start with a provided “bloated” Dockerfile
  2. Apply optimization techniques to reduce image size by at least 50%
  3. Improve container startup time
  4. Maintain all application functionality
  5. Measure and compare before/after metrics

Bloated Dockerfile:

FROM ubuntu:20.04

RUN apt-get update && apt-get install -y \
    nodejs \
    npm \
    python3 \
    python3-pip \
    git \
    curl \
    wget \
    vim \
    nano \
    htop \
    net-tools \
    build-essential

WORKDIR /app
COPY . .
RUN npm install
RUN pip3 install flask requests

EXPOSE 3000 5000 8080
CMD ["npm", "start"]

Optimization Goals:

  • Reduce image size from ~1GB to <200MB
  • Improve startup time by at least 30%
  • Remove unnecessary packages and dependencies
  • Use multi-stage builds effectively

Exercise 2: Resource Optimization (20 minutes)

Objective: Optimize resource allocation for a multi-service application

Instructions:

  1. Deploy a multi-service application without resource limits
  2. Generate load and monitor resource usage
  3. Set appropriate CPU and memory limits based on actual usage
  4. Implement caching to reduce resource consumption
  5. Measure performance improvements

Application Services:

  • Web frontend (Nginx)
  • API backend (Node.js)
  • Database (PostgreSQL)
  • Cache (Redis)

Performance Targets:

  • Reduce overall memory usage by 40%
  • Improve response times by 25%
  • Maintain 99% uptime under load

Exercise 3: Application Performance Tuning (5 minutes)

Objective: Optimize application-level performance

Challenge:

  • Profile application bottlenecks
  • Implement connection pooling
  • Add appropriate caching layers
  • Optimize database queries
  • Configure runtime parameters

Extension Ideas:

  • Implement horizontal pod autoscaling
  • Set up performance monitoring and alerting
  • Create performance regression testing

7. Common Pitfalls and Troubleshooting (10 minutes)

Frequent Performance Issues

  1. Problem: High memory usage despite setting limits Cause: Memory leaks or inefficient garbage collection Solution: Profile application memory usage and tune garbage collection settings

  2. Problem: Poor CPU performance despite available resources Cause: Single-threaded applications or CPU throttling Solution: Implement multi-threading or clustering, check CPU limits

  3. Problem: Slow container startup times Cause: Large images, unnecessary initialization, or cold starts Solution: Optimize images, implement health checks, and use warm-up strategies

Performance Debugging Tools

# Container resource monitoring
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"

# Application profiling
docker exec -it container_name top -p $(docker exec container_name pgrep node)

# Network performance testing
docker exec container_name iperf3 -c target_host

# Disk I/O monitoring
docker exec container_name iostat -x 1

# Memory analysis
docker exec container_name cat /proc/meminfo
docker exec container_name pmap $(docker exec container_name pgrep node)

Optimization Checklist

  • Image size optimized (multi-stage builds, minimal base images)
  • Resource limits set based on actual usage
  • Application-level caching implemented
  • Database connections pooled
  • Unnecessary processes removed
  • Logging optimized (avoid excessive logging)
  • Health checks configured properly
  • Graceful shutdown implemented

8. Best Practices and Professional Tips (5 minutes)

Image Optimization

  • Multi-stage Builds: Separate build and runtime environments
  • Base Image Selection: Choose the smallest viable base image
  • Layer Optimization: Minimize layers and combine RUN commands
  • Build Context: Use .dockerignore to exclude unnecessary files
  • Dependency Management: Remove development dependencies in production

Resource Management

  • Right-sizing: Monitor actual usage and set appropriate limits
  • Quality of Service: Use different QoS classes for different workload types
  • Resource Requests: Set realistic resource requests for scheduling
  • Limit Ratios: Maintain reasonable ratios between requests and limits
  • Monitoring: Continuously monitor and adjust based on actual usage patterns

Application-Level Optimization

  • Connection Pooling: Reuse database and HTTP connections
  • Caching Strategies: Implement appropriate caching at multiple levels
  • Async Processing: Use asynchronous processing for I/O-bound operations
  • Lazy Loading: Load resources only when needed
  • Compression: Enable compression for data transfer

9. Summary and Review (5 minutes)

Key Takeaways

  1. Measure First: Always establish baselines before optimizing
  2. Holistic Approach: Consider image, runtime, and application optimizations
  3. Trade-offs: Understand the balance between different performance metrics
  4. Continuous Monitoring: Performance optimization is an ongoing process

Quick Review Questions

  1. What are the key metrics to monitor for container performance optimization?
  2. How would you optimize a Docker image that’s 2GB in size?
  3. What’s the difference between CPU requests and CPU limits, and how do they affect performance?

For Instructors

Teaching Tips

  • Use real applications with measurable performance issues
  • Demonstrate both successful optimizations and trade-offs
  • Show the cost impact of performance improvements
  • Emphasize the importance of monitoring and measuring changes

Assessment Ideas

  • Optimize provided “slow” applications for specific performance targets
  • Design resource allocation strategies for given workload patterns
  • Troubleshoot performance bottlenecks in complex applications

Adaptation Notes

  • Beginners: Focus on basic image optimization and resource limits
  • Advanced: Cover advanced topics like JVM tuning, kernel parameters, and custom metrics
  • Time Management: Balance hands-on optimization exercises with monitoring setup
  • Real-world Focus: Use performance scenarios relevant to students’ applications