Microservices form the foundation of scalable AI systems. Instead of building a monolithic system, you break your AI pipeline into small, independent services that communicate via APIs. This enables scaling, redundancy, and failover at enterprise level.

The Problem: Monolith vs. Microservice

Monolithic AI App (NOT SCALABLE):

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Flask App (Single Instance) β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Embedding Generation       β”‚
β”‚ β€’ Vector Search              β”‚
β”‚ β€’ LLM Inference              β”‚
β”‚ β€’ Document Processing        β”‚
β”‚ β€’ Cache Management           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Problem: One component overloaded β†’ entire app becomes unresponsive.

Microservice Architecture (SCALABLE):

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Embedding       β”‚       β”‚  Vector          β”‚       β”‚  LLM Inference   β”‚
β”‚  Service         β”‚       β”‚  Database Proxy  β”‚       β”‚  Service         β”‚
β”‚  (CPU-bound)     β”‚       β”‚  (I/O-bound)     β”‚       β”‚  (GPU-bound)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        ↑                           ↑                           ↑
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚   API Gateway         β”‚
                         β”‚  β€’ Rate Limiting      β”‚
                         β”‚  β€’ Load Balancing     β”‚
                         β”‚  β€’ Fallback Routing   β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Advantage: Each service runs independently. If LLM service is overloaded, embedding and vector search continue working.

1. Model Serving as a Service

Model serving means exposing your LLMs as HTTP/gRPC services, not as libraries in app code.

Why Model Serving?

  1. Independent Scaling: GPU servers can scale without starting app servers
  2. Language Agnostic: Services can be written in different languages
  3. Simple Deployment: Model updates without app restart
  4. Resource Isolation: OOM in one service doesn't crash everything

vLLM: The Production Standard

vLLM is today's industry standard for high-performance LLM serving. Features:

  • Continuous Batching: New requests processed immediately, not after request ends
  • PagedAttention: KV-cache managed efficiently like OS Virtual Memory
  • Multi-LoRA Support: Multiple adapters loaded in parallel
  • OpenAI-compatible API: Drop-in replacement for OpenAI Client

Docker Deployment for vLLM:

FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04

RUN apt-get update && apt-get install -y python3.11 python3-pip
RUN pip install vllm

EXPOSE 8000

CMD ["python", "-m", "vllm.entrypoints.openai.api_server", \
     "--model", "meta-llama/Llama-2-7b-hf", \
     "--tensor-parallel-size", "1", \
     "--gpu-memory-utilization", "0.9", \
     "--host", "0.0.0.0", \
     "--port", "8000"]

Health Check and Graceful Shutdown:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 30s

stop_grace_period: 60s  # Time for running requests

2. API Gateway for LLMs

The API Gateway is the traffic cop of your system. It's not just load balancer, but:

  • Authentication (API Keys, OAuth)
  • Rate Limiting (Token-aware, not just request-count)
  • Request Validation
  • Routing to different model versions
  • Fallback Chains (llama β†’ mistral β†’ gpt-4)
  • Cost Tracking
  • Caching Layer

Gateway Architecture

Client
  ↓
[API Gateway]
  β”œβ”€ Auth Check (API Key valid?)
  β”œβ”€ Rate Limit Check (tokens/min exceeded?)
  β”œβ”€ Request Validation (JSON schema)
  β”œβ”€ Routing Decision
  β”‚  β”œβ”€ Size-based: "small" β†’ llama-7b, "large" β†’ llama-70b
  β”‚  β”œβ”€ Cost-based: budget exhausted β†’ use cheaper model
  β”‚  β”œβ”€ Latency-SLA: need <500ms β†’ use fastest available
  β”œβ”€ Cache Lookup (semantic cache)
  └─ Load Balance
     β”œβ”€ Instance 1 (vLLM on GPU-A)
     β”œβ”€ Instance 2 (vLLM on GPU-B)
     β”œβ”€ Instance 3 (Fallback: API Provider)

LiteLLM: Gateway with LLM Abstractions

LiteLLM is a Python library that speaks all LLM APIs uniformly:

from litellm import completion
import litellm

# Define fallback chain
litellm.model_list = [
    {
        "model_name": "gpt-4",
        "litellm_params": {
            "model": "gpt-4",
            "api_key": "$OPENAI_API_KEY"
        }
    },
    {
        "model_name": "gpt-4",
        "litellm_params": {
            "model": "claude-3-opus-20240229",
            "api_key": "$ANTHROPIC_API_KEY"
        }
    },
    {
        "model_name": "gpt-4",
        "litellm_params": {
            "model": "llama-2-70b",
            "api_base": "http://localhost:8000/v1"
        }
    }
]

# Use first available
response = completion(
    model="gpt-4",
    messages=[{"role": "user", "content": "Hello!"}],
    fallbacks=[("claude-opus", 2), ("llama-70b", 1)]  # Retry strategy
)

3. Sidecar Pattern for AI

The sidecar pattern places a helper container next to your main container. For AI systems, perfect for:

  • LLM-Sidecar: Embeddings, inference locally
  • Cache-Sidecar: Redis for token/response caching
  • Monitoring-Sidecar: Prometheus metrics, token counting
  • Auth-Sidecar: OAuth, API Key validation

Practical Example: RAG App with LLM Sidecar

version: '3.8'
services:
  app:
    image: my-rag-app:latest
    environment:
      VLLM_HOST: localhost:8000
      EMBEDDING_HOST: localhost:8001
      CACHE_HOST: cache:6379
    ports:
      - "5000:5000"
    depends_on:
      - vllm
      - embedding-server
      - cache

  # Sidecar 1: LLM Inference
  vllm:
    image: vllm/vllm:latest
    environment:
      MODEL: meta-llama/Llama-2-13b-hf
      TENSOR_PARALLEL_SIZE: 1
      GPU_MEMORY_UTILIZATION: 0.9
    volumes:
      - hf-cache:/root/.cache/huggingface
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # Sidecar 2: Embedding Server
  embedding-server:
    image: vllm/vllm:latest
    environment:
      MODEL: all-MiniLM-L6-v2
    ports:
      - "8001:8000"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # Sidecar 3: Cache
  cache:
    image: redis:7-alpine
    command: redis-server --maxmemory 4gb --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

volumes:
  hf-cache:

App Code (Python/Flask):

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

VLLM_HOST = "http://localhost:8000"
EMBEDDING_HOST = "http://localhost:8001"

@app.route("/api/chat", methods=["POST"])
def chat():
    data = request.json
    prompt = data.get("prompt")

    # 1. Embedding (parallel query)
    embedding_response = requests.post(
        f"{EMBEDDING_HOST}/v1/embeddings",
        json={"input": prompt, "model": "all-MiniLM-L6-v2"},
        timeout=5
    )

    # 2. LLM Inference
    llm_response = requests.post(
        f"{VLLM_HOST}/v1/chat/completions",
        json={
            "model": "Llama-2-13b",
            "messages": [{"role": "user", "content": prompt}],
            "max_tokens": 512,
            "temperature": 0.7
        },
        timeout=30
    )

    return jsonify({
        "response": llm_response.json()["choices"][0]["message"]["content"]
    })

4. gRPC vs REST for Inference

For performance-critical scenarios, gRPC is faster than REST:

Aspect REST (HTTP/1.1) gRPC (HTTP/2)
Serialization JSON (Text) Protobuf (Binary)
Connection New TCP per request Multiplexed HTTP/2
Latency ~50-200ms ~5-20ms
Payload Size 2-5x larger Compact
Streaming Chunked Built-in bidirectional

gRPC gives 10x latency reduction for streaming use cases.

5. Health Checks and Graceful Shutdown

A robust system needs health checks and graceful shutdown:

from flask import Flask, jsonify
import time

app = Flask(__name__)
start_time = time.time()

@app.route("/health", methods=["GET"])
def health():
    """Minimal health check"""
    return jsonify({"status": "ok"}), 200

@app.route("/healthz", methods=["GET"])
def healthz():
    """Detailed health check for Kubernetes"""
    gpu_memory = get_gpu_memory_usage()
    cpu_usage = psutil.cpu_percent(interval=1)

    if gpu_memory > 95 or cpu_usage > 90:
        return jsonify({
            "status": "degraded",
            "gpu_memory_percent": gpu_memory
        }), 503

    return jsonify({"status": "healthy"}), 200

@app.route("/ready", methods=["GET"])
def ready():
    """Readiness check"""
    try:
        if not check_model_loaded():
            return jsonify({"ready": False}), 503
        return jsonify({"ready": True}), 200
    except Exception as e:
        return jsonify({"ready": False, "error": str(e)}), 503

Kubernetes Deployment Pattern

For large systems, Kubernetes orchestration is essential:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: llm-service

  template:
    metadata:
      labels:
        app: llm-service

    spec:
      containers:
      - name: vllm
        image: vllm/vllm:v0.3.0
        ports:
        - containerPort: 8000
        resources:
          requests:
            nvidia.com/gpu: 2
            memory: "40Gi"
          limits:
            nvidia.com/gpu: 2
            memory: "45Gi"

        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10

        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 20
          periodSeconds: 5

        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 60"]

Summary: Microservices for AI

Pattern When Example
Model Serving Always when using LLMs vLLM, TGI, Triton
API Gateway Multiple models or fallbacks Kong, LiteLLM, Traefik
Sidecar Separated concerns Redis Sidecar, Embedding Server
gRPC Performance critical (<20ms) Multi-token streams
Kubernetes Production scale (>10 replicas) HPA, LoadBalancing, Monitoring

The art is choosing the right combination for your problem, not using all patterns at once. Start with Model Serving + API Gateway. Everything else comes as you need it.