Building a High-Performance API Gateway with Kong
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
- Gateway essential: Centralized control
- Caching huge win: 70% cache hit rate
- Rate limiting prevents abuse: No more DDoS
- Load balancing works: Even distribution
- Monitoring critical: Track everything
Conclusion
Kong API gateway transformed our microservices. 50K req/s, 70% less backend load, $5K/month savings.
Key takeaways:
- Throughput: 50K req/s
- Backend load: -70%
- Cache hit rate: 70%
- Cost savings: $5K/month
- Centralized security
Build an API gateway. Your microservices need it.