Our microservices needed a gateway. Direct client access was chaos - no rate limiting, no auth, no caching.

Built Kong API gateway. 50K req/s, 70% less backend load. Here’s the architecture.

Table of Contents

The Problem

Before Gateway:

  • 15 microservices
  • Clients call services directly
  • No centralized auth
  • No rate limiting
  • No caching
  • Backend overload

Kong Setup

# docker-compose.yml
version: '3'
services:
  kong-database:
    image: postgres:12
    environment:
      POSTGRES_USER: kong
      POSTGRES_DB: kong
      POSTGRES_PASSWORD: kong
    
  kong-migration:
    image: kong:2.1
    command: kong migrations bootstrap
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
    depends_on:
      - kong-database
  
  kong:
    image: kong:2.1
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: 0.0.0.0:8001
    ports:
      - "8000:8000"  # Proxy
      - "8443:8443"  # Proxy SSL
      - "8001:8001"  # Admin API
    depends_on:
      - kong-database

Service Configuration

# Add service
curl -i -X POST http://localhost:8001/services \
  --data name=user-service \
  --data url=http://user-service:8080

# Add route
curl -i -X POST http://localhost:8001/services/user-service/routes \
  --data paths[]=/api/users \
  --data methods[]=GET \
  --data methods[]=POST

Python Configuration:

import requests

class KongAdmin:
    def __init__(self, admin_url='http://localhost:8001'):
        self.admin_url = admin_url
    
    def add_service(self, name, url):
        """Add service to Kong."""
        response = requests.post(
            f'{self.admin_url}/services',
            json={'name': name, 'url': url}
        )
        return response.json()
    
    def add_route(self, service_name, paths, methods):
        """Add route to service."""
        response = requests.post(
            f'{self.admin_url}/services/{service_name}/routes',
            json={'paths': paths, 'methods': methods}
        )
        return response.json()

# Usage
kong = KongAdmin()
kong.add_service('user-service', 'http://user-service:8080')
kong.add_route('user-service', ['/api/users'], ['GET', 'POST'])

Rate Limiting

# Add rate limiting plugin
curl -i -X POST http://localhost:8001/services/user-service/plugins \
  --data name=rate-limiting \
  --data config.minute=100 \
  --data config.hour=1000 \
  --data config.policy=local

Per-Consumer Rate Limiting:

# Create consumer
curl -i -X POST http://localhost:8001/consumers \
  --data username=mobile-app

# Add API key
curl -i -X POST http://localhost:8001/consumers/mobile-app/key-auth \
  --data key=mobile-app-key-123

# Add rate limit for consumer
curl -i -X POST http://localhost:8001/plugins \
  --data name=rate-limiting \
  --data consumer.username=mobile-app \
  --data config.minute=1000 \
  --data config.hour=10000

Authentication

# Enable JWT authentication
curl -i -X POST http://localhost:8001/services/user-service/plugins \
  --data name=jwt

# Create consumer with JWT
curl -i -X POST http://localhost:8001/consumers \
  --data username=api-client

curl -i -X POST http://localhost:8001/consumers/api-client/jwt \
  --data algorithm=HS256 \
  --data secret=my-secret-key

Client Usage:

import jwt
import requests

# Generate JWT
payload = {
    'iss': 'api-client',
    'exp': int(time.time()) + 3600
}
token = jwt.encode(payload, 'my-secret-key', algorithm='HS256')

# Make request
response = requests.get(
    'http://localhost:8000/api/users',
    headers={'Authorization': f'Bearer {token}'}
)

Caching

# Enable proxy caching
curl -i -X POST http://localhost:8001/services/user-service/plugins \
  --data name=proxy-cache \
  --data config.response_code=200 \
  --data config.request_method=GET \
  --data config.content_type=application/json \
  --data config.cache_ttl=300 \
  --data config.strategy=memory

Redis Caching:

# Use Redis for caching
curl -i -X POST http://localhost:8001/plugins \
  --data name=proxy-cache \
  --data config.strategy=redis \
  --data config.redis.host=redis \
  --data config.redis.port=6379 \
  --data config.cache_ttl=300

Load Balancing

# Create upstream
curl -i -X POST http://localhost:8001/upstreams \
  --data name=user-service-upstream

# Add targets
curl -i -X POST http://localhost:8001/upstreams/user-service-upstream/targets \
  --data target=user-service-1:8080 \
  --data weight=100

curl -i -X POST http://localhost:8001/upstreams/user-service-upstream/targets \
  --data target=user-service-2:8080 \
  --data weight=100

# Update service to use upstream
curl -i -X PATCH http://localhost:8001/services/user-service \
  --data host=user-service-upstream

Request Transformation

# Add request transformer
curl -i -X POST http://localhost:8001/services/user-service/plugins \
  --data name=request-transformer \
  --data config.add.headers=X-Gateway:Kong \
  --data config.add.headers=X-Request-ID:$(uuidgen) \
  --data config.remove.headers=X-Internal-Header

Monitoring

# Enable Prometheus plugin
curl -i -X POST http://localhost:8001/plugins \
  --data name=prometheus

Metrics:

# Request rate
rate(kong_http_status[5m])

# Latency
histogram_quantile(0.95, rate(kong_latency_bucket[5m]))

# Bandwidth
rate(kong_bandwidth[5m])

Results

Performance:

  • Throughput: 50K req/s
  • Latency: +2ms (gateway overhead)
  • Cache hit rate: 70%
  • Backend load: -70%

Security:

  • Centralized auth: ✅
  • Rate limiting: ✅
  • API key management: ✅
  • JWT validation: ✅

Cost:

  • Backend servers: 20 → 6 (-70%)
  • Monthly savings: $5000

Lessons Learned

  1. Gateway essential: Centralized control
  2. Caching huge win: 70% cache hit rate
  3. Rate limiting prevents abuse: No more DDoS
  4. Load balancing works: Even distribution
  5. Monitoring critical: Track everything

Conclusion

Kong API gateway transformed our microservices. 50K req/s, 70% less backend load, $5K/month savings.

Key takeaways:

  1. Throughput: 50K req/s
  2. Backend load: -70%
  3. Cache hit rate: 70%
  4. Cost savings: $5K/month
  5. Centralized security

Build an API gateway. Your microservices need it.