API Gateway #
API Gateway adalah titik masuk tunggal (single entry point) bagi semua permintaan klien (client requests) yang ditujukan ke arsitektur microservices di backend. Daripada membiarkan klien menghubungi setiap layanan secara langsung melalui alamat IP dan port yang berbeda-beda—yang meningkatkan kompleksitas keamanan dan koordinasi di sisi klien—kita menempatkan sebuah gateway di depan seluruh sistem. Gateway ini bertanggung jawab penuh untuk mengelola perutean lalu lintas (traffic routing), transformasi permintaan/respons (request/response transformation), autentikasi terpusat, pembatasan laju (rate limiting), pemantauan kesehatan layanan (health checks), penanganan kegagalan (circuit breaker), hingga penyatuan CORS (Cross-Origin Resource Sharing) dan logging terpadu.
Caddy merupakan pilihan server tepi (edge server) yang luar biasa tangguh sekaligus ringan untuk bertindak sebagai API Gateway. Dengan keunggulannya yang mencakup manajemen sertifikat TLS otomatis secara bawaan (out-of-the-box), performa tinggi berbasis goroutines Go, konfigurasi deklaratif yang adaptif melalui Caddyfile maupun REST API, serta konsumsi memori yang minimal, Caddy mampu mereduksi overhead operasional yang biasanya ditemukan pada solusi gateway yang lebih kompleks seperti Kong atau APISIX.
Arsitektur API Gateway #
Di dalam arsitektur sistem berbasis microservices, API Gateway berfungsi sebagai tameng sekaligus distributor. Klien dari internet—baik itu aplikasi web SPA (Single Page Application), aplikasi mobile, maupun sistem pihak ketiga—hanya perlu mengetahui satu alamat endpoint publik (misalnya api.example.com). Caddy akan menerima permintaan tersebut, memproses kebijakan keamanan di edge, lalu meneruskannya ke layanan backend yang sesuai di jaringan internal.
flowchart TD
Client["Klien (Web/Mobile)"] -->|"api.example.com (HTTPS)"| Gateway["Caddy API Gateway"]
subgraph GatewayInternal["Proses Internal Gateway"]
Auth["Autentikasi & JWT"]
CORS["CORS & Security Headers"]
Limit["Rate Limiting"]
end
Gateway --> GatewayInternal
GatewayInternal -->|"/v1/users"| UserSvc["User Service (:3001)"]
GatewayInternal -->|"/v1/products"| ProdSvc["Product Service (:3002)"]
GatewayInternal -->|"/v1/orders"| OrderSvc["Order Service (:3003)"]
GatewayInternal -->|"/v1/auth"| AuthSvc["Auth Service (:3004)"]
GatewayInternal -->|"/v1/search"| SearchSvc["Search Service (:3005)"]
style Gateway stroke:#0288d1,stroke-width:2px
style GatewayInternal stroke:#7b1fa2,stroke-width:2pxDengan pola arsitektur seperti ini, layanan backend kita (seperti User Service, Product Service, dll.) dapat diisolasi sepenuhnya di dalam jaringan privat (VPC) tanpa perlu mengekspos port ke internet. Hal ini secara signifikan memperkecil bidang serangan (attack surface) sistem kita.
Routing ke Multiple Microservices #
Tugas utama dari sebuah API Gateway adalah merutekan permintaan berdasarkan informasi yang ada pada HTTP request, seperti jalur URL (path-based routing) atau nama host (host-based routing).
Path-based Routing vs Host-based Routing #
Path-based Routing: Klien mengirim semua permintaan ke satu host, misalnya
api.example.com, dan Caddy memilah tujuan berdasarkan prefiks jalur URL:api.example.com/api/v1/users-> dirutekan keUser Serviceapi.example.com/api/v1/products-> dirutekan keProduct ServicePendekatan ini sangat populer karena klien hanya perlu mengonfigurasi satu base URL dan satu sertifikat SSL/TLS.
Host-based Routing: Setiap layanan diidentifikasi oleh subdomain unik:
users.api.example.com-> dirutekan keUser Serviceproducts.api.example.com-> dirutekan keProduct ServicePendekatan ini berguna untuk memisahkan domain kepemilikan antar tim, namun memerlukan manajemen DNS dan SSL wildcard yang lebih teliti.
Dalam implementasi produksi, kita sering menggunakan kombinasi keduanya dengan menitikberatkan pada path-based routing untuk kemudahan konsumsi API oleh klien. Berikut adalah konfigurasi Caddyfile komprehensif yang menangani path-based routing, pembersihan prefiks (path stripping), penanganan CORS terpusat, dan konfigurasi security headers:
# Konfigurasi API Gateway Produksi
api.example.com {
# ── Global Middleware & Logging ───────────────────────────────
log {
output file /var/log/caddy/api-gateway.log {
roll_size 50mb
roll_keep 10
roll_keep_days 7
}
format json
}
# Kompresi respons dinamis
encode gzip zstd
# ── Centralized CORS (Cross-Origin Resource Sharing) ──────────
# Menangani request OPTIONS (preflight) dari browser secara terpusat
@options method OPTIONS
handle @options {
header Access-Control-Allow-Origin "https://app.example.com"
header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS"
header Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key, X-Request-ID"
header Access-Control-Expose-Headers "X-Request-ID, X-Gateway-Response-Time"
header Access-Control-Allow-Credentials "true"
header Access-Control-Max-Age "86400"
respond "" 204
}
# Header CORS untuk request biasa (non-preflight)
header Access-Control-Allow-Origin "https://app.example.com"
header Access-Control-Allow-Credentials "true"
header Vary "Origin"
# ── Security Hardening ────────────────────────────────────────
header {
# Menyembunyikan identitas server tepi
-Server
-X-Powered-By
# Proteksi browser klien
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
}
# ── Perutean Layanan (Service Routing) ────────────────────────
# Rute User Service
handle /api/v1/users* {
# ANTI-PATTERN: Mengirim request beserta prefiks gateway ke backend
# reverse_proxy user-service:3001
# BENAR: Menghapus prefiks /api/v1 agar backend menerima path bersih /users
uri strip_prefix /api/v1
reverse_proxy user-service:3001 {
# Menyuntikkan pelacakan IP klien asli
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Rute Product Service
handle /api/v1/products* {
uri strip_prefix /api/v1
reverse_proxy product-service:3002 {
# Optimasi connection pool untuk katalog produk
transport http {
dial_timeout 2s
keepalive_interval 30s
}
}
}
# Rute Order Service
handle /api/v1/orders* {
uri strip_prefix /api/v1
reverse_proxy order-service:3003
}
# Rute Search Service (Pencarian berat dengan timeout lebih longgar)
handle /api/v1/search* {
uri strip_prefix /api/v1
reverse_proxy search-service:3005 {
transport http {
# Memberi waktu ekstra bagi engine pencari untuk merespons
response_header_timeout 15s
read_buffer_size 8192
}
}
}
# ── Penanganan Jalur Default (Fallback 404) ──────────────────
handle {
header Content-Type "application/json"
respond `{"error": "Resource Not Found", "code": 404}` 404
}
}
[!WARNING] Ketika kita menggunakan
uri strip_prefix, pastikan backend kita tidak menghasilkan URL absolut dalam bodi responsnya (seperti tautan redirect atau tautan pagination yang merujuk langsung ke IP backend). Backend harus selalu menghasilkan relative path atau menghormati headerX-Forwarded-*yang dikirim oleh Caddy.
API Versioning #
Mengelola siklus hidup API memerlukan strategi versi (versioning) yang matang agar tidak memutus integrasi klien yang masih menggunakan versi lama (breaking changes). API Gateway memainkan peran krusial dalam mengabstraksi struktur versi ini.
Strategi API Versioning di Gateway #
Ada beberapa cara umum untuk menyajikan versi API:
- URI Path:
/api/v1/usersvs/api/v2/users. Paling mudah dikonfigurasi dan ramah terhadap caching. - Custom Header: Klien mengirim header
Accept-Version: v2atauX-API-Version: 2. - Accept Header (Content Negotiation):
Accept: application/vnd.example.v2+json.
Di Caddy, perutean versi berbasis URI Path dapat diimplementasikan dengan membagi blok handle secara modular. Kita juga bisa menggunakan taktik penulisan ulang jalur reguler (regexp rewrite) untuk membuat rute alias seperti /api/latest/ yang selalu merujuk pada versi stabil terbaru.
Berikut adalah contoh konfigurasi Caddyfile untuk skenario multi-versi, rute alias, dan penyuntikan header informasi penghentian (deprecation/sunset) sesuai standar RFC 8594:
api.example.com {
encode gzip zstd
# ── API Rute Versi 1 (Stabil - Deprecated) ────────────────────
handle /api/v1/* {
# Menyisipkan header peringatan bahwa versi ini akan segera dihentikan
header Deprecation "true"
header Sunset "Thu, 31 Dec 2026 23:59:59 GMT"
header Link "</api/v2/>; rel=\"successor-version\""
# Teruskan ke klaster layanan lama
uri strip_prefix /api/v1
reverse_proxy user-service-v1:3001
}
# ── API Rute Versi 2 (Terbaru - Aktif) ────────────────────────
handle /api/v2/* {
uri strip_prefix /api/v2
reverse_proxy user-service-v2:3011 {
# Deteksi kesalahan pasif untuk versi baru
fail_duration 10s
max_fails 3
}
}
# ── API Rute Alias (/api/latest/) ─────────────────────────────
# Request ke /api/latest/users diubah secara internal menjadi /api/v2/users
# tanpa mengubah URL di browser/klien
handle /api/latest/* {
# Menulis ulang path secara dinamis
uri replace /api/latest/ /api/v2/
# Eksekusi ulang pencocokan setelah rewrite untuk mengarahkan ke handle v2
# atau langsung proxy ke backend v2 di sini
uri strip_prefix /api/v2
reverse_proxy user-service-v2:3011
}
# Fallback default
handle {
header Content-Type "application/json"
respond `{"error": "Unsupported API Version", "code": 400}` 400
}
}
Dengan pendekatan ini, kita dapat mematikan layanan lama secara bertahap tanpa harus memaksa semua tim klien melakukan pembaruan kode secara serentak. Klien yang lambat melakukan migrasi tetap dapat menggunakan /api/v1/ dengan mendapatkan header peringatan Deprecation, sementara klien baru dapat langsung menggunakan /api/v2/ atau /api/latest/.
Autentikasi Terpusat di Gateway #
Mengimplementasikan pemeriksaan keamanan dan autentikasi pada setiap microservice secara individual merupakan anti-pattern yang memboroskan waktu dan rentan terhadap inkonsistensi. Caddy memungkinkan kita untuk melakukan pemeriksaan token autentikasi di satu pintu (centralized edge authentication).
sequenceDiagram
participant Client as Klien
participant Gateway as Caddy API Gateway
participant UserSvc as User Service
Client->>Gateway: GET /api/v1/users/me dengan Token JWT
Note over Gateway: Gateway memvalidasi Token JWT secara kriptografis
Gateway->>Gateway: Validasi Signature JWT
alt Token Valid
Gateway->>UserSvc: GET /users/me dengan Header X-User-ID
UserSvc-->>Gateway: HTTP 200 OK Data Profil
Gateway-->>Client: HTTP 200 OK Data Profil
else Token Tidak Valid / Kadaluarsa
Gateway-->>Client: HTTP 401 Unauthorized
endOpsi Autentikasi di Caddy #
- Basic Auth Natively: Cocok untuk membatasi endpoint internal atau konsol admin internal.
- Token API Key Validation (via Map): Memetakan API key ke client ID tertentu secara statis di memori.
- JWT (JSON Web Token) Validation: Memanfaatkan modul eksternal seperti
caddy-jwt(dari biner kustom xcaddy) untuk memverifikasi tanda tangan kriptografi token (HMAC atau RSA/Asymmetric) secara lokal di pintu gerbang sebelum request diteruskan ke backend.
Berikut adalah demonstrasi praktis penerapan validasi token API Key sederhana menggunakan direktif map, penolakan token tidak dikenal, serta simulasi integrasi token JWT terpusat dengan penerusan informasi klaim pengguna (user identity header forwarding) ke layanan hilir (downstream services):
api.example.com {
encode gzip zstd
# ── Skenario 1: API Key Validation dengan Map (Untuk Pihak Ketiga) ──
# Kita memetakan header Authorization 'Bearer <key>' ke nama klien/aplikasi.
# Jika key tidak cocok, variabel {client_identity} akan bernilai kosong ("").
map {header.Authorization} {client_identity} {
"Bearer key_prod_9a8b7c" "Client-Frontend-App"
"Bearer key_partner_3x2y1z" "Partner-Logistics-Corp"
default ""
}
# Endpoint publik (tidak butuh API Key)
handle /api/v1/public/* {
uri strip_prefix /api/v1
reverse_proxy public-service:3006
}
# Rute API Partner yang mewajibkan API Key valid
handle /api/v1/partners* {
# ANTI-PATTERN: Mengizinkan request kosong lewat dan membiarkan backend crash
# reverse_proxy partner-service:3009
# BENAR: Tolak request di tingkat gateway jika client_identity kosong
@invalid_key expression `{client_identity} == ""`
handle @invalid_key {
header Content-Type "application/json"
respond `{"error": "Invalid or missing API Key", "code": 401}` 401
}
# Jika lolos, teruskan request dan sematkan identitas klien untuk audit di backend
uri strip_prefix /api/v1
reverse_proxy partner-service:3009 {
header_up X-Client-Name {client_identity}
}
}
# ── Skenario 2: JWT Validation Terpusat (Simulasi Menggunakan Modul Security) ──
# Catatan: Memerlukan plugin 'caddy-security' / 'caddy-jwt'
# Dalam konfigurasi ini, kita berasumsi plugin terinstal dan mengamankan jalur /api/v2/*
# Di sini kita menyimulasikan deteksi token JWT secara konseptual.
# Gateway memeriksa keberadaan header Authorization dan melakukan validasi.
# Untuk backend, kita menyuntikkan data user ID dan perannya yang diekstrak oleh modul JWT.
handle /api/v2/secure/* {
# Autentikasi JWT dilakukan di sini (konseptual):
# jwt {
# primary_key "rahasia_tanda_tangan_jwt_kita"
# }
uri strip_prefix /api/v2
reverse_proxy backend-secure:3022 {
# Meneruskan klaim JWT yang sudah didekripsi ke downstream service
# Backend tidak perlu lagi melakukan query database hanya untuk memvalidasi user
header_up X-User-ID {http.auth.user.id}
header_up X-User-Email {http.auth.user.email}
header_up X-User-Role {http.auth.user.role}
header_up X-Auth-Method "gateway-jwt"
}
}
}
Dengan mengalihkan tugas otentikasi JWT ke Caddy API Gateway, kita menghemat daya komputasi di tingkat microservice hilir (karena tidak perlu mengulang verifikasi kriptografi signature yang memakan CPU) dan memastikan bahwa semua layanan hilir terlindungi di belakang kebijakan otentikasi yang seragam.
Rate Limiting per Service #
Melindungi layanan backend kita dari serangan penolakan layanan (Denial of Service - DoS), upaya brute-force kredensial, atau pengikisan data (web scraping) yang agresif adalah tanggung jawab penting dari API Gateway. Kita perlu membatasi jumlah permintaan yang boleh dilakukan oleh satu klien dalam satuan waktu tertentu.
Algoritma Rate Limiting #
- Token Bucket: Mengizinkan akumulasi burst request dengan mengisi token secara konstan ke dalam ember virtual.
- Leaky Bucket: Meratakan lalu lintas dengan mengeluarkan request ke backend dengan laju yang konstan, menahan burst di antrean.
- Sliding Window: Menghitung secara presisi batas request dalam jendela waktu berjalan (rolling window).
Untuk mengimplementasikan pembatasan laju di Caddy, kita menggunakan modul kompilasi eksternal caddy-ratelimit yang menggunakan algoritma sliding window berbasis memori (atau terintegrasi dengan Redis untuk klaster multi-instansi).
Berikut adalah contoh konfigurasi di mana kita menerapkan batas ketat untuk operasi berat (expensive operations) seperti generate laporan, batas sedang untuk API umum, serta pengecualian (whitelist) bagi IP internal developer:
api.example.com {
# ── Konfigurasi Rate Limit Terpusat ───────────────────────────
# Catatan: Memerlukan modul 'caddy-ratelimit' yang dikompilasi via xcaddy
# Definisikan Named Matchers
@expensive_ops path /api/v1/reports* /api/v1/export*
@general_api path /api/v1/*
# 1. Batasan untuk Endpoint Berat (Maksimal 5 request per menit)
rate_limit @expensive_ops {
zone reports_limit {
# Identifikasi klien berdasarkan IP publik asli
key {remote_host}
window 1m
events 5
}
}
# 2. Batasan untuk API Umum (Maksimal 120 request per menit)
rate_limit @general_api {
zone general_limit {
key {remote_host}
window 1m
events 120
}
}
# ── Pengecualian Rate Limit (Bypass untuk Dev / Trusted IP) ──
# Kita menggunakan block expression untuk melewati rate limit jika IP berasal dari LAN kantor
@trusted_office remote_ip 192.168.10.0/24 10.0.0.0/8
# ── Routing Layanan ───────────────────────────────────────────
handle @expensive_ops {
uri strip_prefix /api/v1
reverse_proxy report-service:3007
}
handle @general_api {
uri strip_prefix /api/v1
reverse_proxy main-backend:3000
}
# Penanganan jika terjadi rate limit (HTTP 429 Too Many Requests)
# Modul caddy-ratelimit secara otomatis mengembalikan status 429 ke klien
# dengan header Retry-After yang sesuai.
}
[!TIP] Jika API Gateway kita berada di belakang CDN seperti Cloudflare atau load balancer eksternal AWS ALB, pastikan kita telah mengatur konfigurasi
trusted_proxiespada opsi global Caddy. Jika tidak,{remote_host}akan mengidentifikasi IP server CDN sebagai klien tunggal, yang berakibat pada terblokirnya seluruh traffic pengguna global secara tidak sengaja (false positive).
Request dan Response Transformation #
API Gateway bertindak sebagai jembatan penerjemah. Terkadang, format permintaan yang dikirim oleh klien tidak sama persis dengan yang diharapkan oleh microservice hilir, atau ada informasi penting di level HTTP header yang harus dibersihkan demi alasan keamanan sebelum respons dikirim kembali ke klien.
Di Caddy, manipulasi ini ditangani dengan sangat efisien lewat sub-direktif header_up (memodifikasi request yang naik ke backend) dan header_down (memodifikasi respons yang turun ke klien) di dalam blok reverse_proxy.
Berikut adalah contoh praktis transformasi header:
- Penyuntikan Request ID: Menambahkan header pelacakan UUID unik (
{http.request.uuid}) agar riwayat log request dapat ditelusuri dari gateway hingga ke basis data terdalam microservices. - Pembersihan Informasi Sensitif: Menghapus header seperti
X-Powered-By,X-AspNet-Version, atauServermilik server backend untuk mencegah serangan pengintaian versi (version scanning). - Pengukuran Performa: Menambahkan header respons
X-Gateway-Response-Timeyang menghitung berapa lama waktu pemrosesan internal gateway menggunakan placeholder{duration}.
api.example.com {
encode gzip zstd
handle /api/v1/* {
uri strip_prefix /api/v1
reverse_proxy app-server:3000 {
# 1. Transformasi Request ke Backend (Upstream)
# Menyuntikkan IP klien asli untuk logging backend
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
# Menyuntikkan ID penelusuran terpadu (Correlation ID / Trace ID)
header_up X-Request-ID {http.request.uuid}
# Memberi tahu backend bahwa request melalui Caddy Gateway
header_up X-Gateway-Server "Caddy-Edge"
# Hapus header berbahaya yang coba disuntikkan klien nakal
header_up -X-Admin-Override
# 2. Transformasi Respons ke Klien (Downstream)
# Menghapus identitas internal framework backend demi keamanan
header_down -X-Powered-By
header_down -Server
header_down -X-Source-Branch
# Menyematkan ID penelusuran ke klien agar klien dapat merujuk
# ID ini jika menghubungi tim support saat terjadi error
header_down X-Request-ID {http.request.uuid}
# Menyematkan metrik latensi proses hilir (durasi eksekusi reverse proxy)
header_down X-Gateway-Execution-Time "{duration}s"
}
}
}
Penyuntikan X-Request-ID adalah salah satu langkah terpenting dalam arsitektur terdistribusi. Tanpa ID ini, mengaitkan log error di User Service dengan request lambat di gateway akan menjadi tugas yang sangat menguras waktu dan tenaga.
Circuit Breaker dan Health Checks #
Dalam sistem terdistribusi, kegagalan adalah kepastian. Jika satu microservice (misalnya Payment Service) melambat atau mati total karena lonjakan beban, kita tidak ingin seluruh sistem kita ikut macet karena thread pemrosesan di gateway tersumbat menunggu respons. Kita membutuhkan pola Circuit Breaker.
Siklus Kerja Circuit Breaker #
- Closed (Normal): Saklar tertutup, semua request dialirkan langsung ke backend utama.
- Open (Terputus): Terjadi kegagalan berturut-turut yang melebihi ambang batas. Saklar terbuka, gateway langsung menolak request (atau mengarahkan ke backend cadangan) tanpa membebani backend utama yang sedang sekarat.
- Half-Open (Uji Coba): Setelah periode waktu tertentu, gateway mencoba mengalirkan sebagian kecil request. Jika sukses, saklar kembali ke
Closed. Jika gagal, saklar kembali keOpen.
Di Caddy, fitur Circuit Breaker dasar diimplementasikan secara terintegrasi melalui Passive Health Check dikombinasikan dengan Active Health Check.
- Passive Health Check: Caddy memantau status kegagalan request riil saat memproses lalu lintas. Jika backend gagal merespons sebanyak
max_failskali, Caddy akan mengisolasinya selamafail_duration. - Active Health Check: Caddy secara berkala mengirimkan probe khusus ke URL kesehatan (misalnya
/health) untuk mendeteksi kapan backend tersebut siap menerima lalu lintas kembali.
Berikut adalah konfigurasi Caddyfile dengan klaster penyeimbang beban (load balancer) yang memiliki kebijakan prioritas failover, proteksi sirkuit pasif, dan pemantauan aktif yang terpadu:
api.example.com {
encode gzip zstd
handle /api/v1/checkout* {
uri strip_prefix /api/v1
# Mengarahkan utama ke payment-prod, dan cadangan ke payment-backup
reverse_proxy payment-prod:3008 payment-backup:3018 {
# ── Kebijakan Load Balancing ──
# Selalu gunakan server pertama (prod). Hanya gunakan server kedua
# (backup) jika server pertama dinyatakan tidak sehat oleh health checker.
lb_policy first
# ── Passive Health Check (Circuit Breaker) ──
# Jika server utama gagal merespons sebanyak 3 kali berturut-turut,
# Caddy akan membuka sirkuit (isolasi) dan mem-bypass server tersebut selama 30 detik.
max_fails 3
fail_duration 30s
# ── Active Health Check (Pemulihan Sirkuit) ──
# Secara aktif memeriksa status kesehatan server setiap 10 detik.
health_uri /api/healthz
health_interval 10s
health_timeout 3s
# Hanya anggap sehat jika mengembalikan HTTP status 200
health_status 200
# Pengaturan timeout dial ke upstream
transport http {
dial_timeout 2s
response_header_timeout 5s
}
}
}
}
Dengan mengombinasikan lb_policy first dengan deteksi sirkuit di atas, kita memastikan bahwa klien mendapatkan pengalaman transaksi yang mulus tanpa interupsi kegagalan, bahkan saat server utama kita mengalami kegagalan sistem mendadak.
Service Discovery Sederhana dengan File Konfigurasi #
Dalam lingkungan kontainer modern atau lingkungan cloud, alamat IP dari microservices dapat berubah secara dinamis setiap kali terjadi pembaruan aplikasi (rolling update) atau penskalaan otomatis (autoscaling). API Gateway harus mampu mendeteksi perubahan lokasi backend ini (Service Discovery).
Meskipun solusi enterprise menggunakan DNS SRV record atau plugin Consul, untuk skala kecil hingga menengah kita bisa mengotomatiskan pembaruan konfigurasi Caddy secara dinamis menggunakan skrip integrasi sederhana yang membaca service registry (misalnya dari docker metadata atau file JSON eksternal) dan memuat ulang (reload) konfigurasi Caddy tanpa ada downtime sama sekali memanfaatkan API Admin Caddy.
Berikut adalah alur skrip otomatisasi Python yang membaca berkas repositori layanan services.json, menyusun konfigurasi Caddyfile baru berbasis template, memvalidasinya secara defensif, lalu mengirimkan perintah reload aman ke Caddy:
#!/bin/bash
# update-gateway.sh — Skrip otomatisasi pembaruan backend Caddy
set -e
REGISTRY_FILE="/etc/caddy/services.json"
TEMPLATE_FILE="/etc/caddy/Caddyfile.template"
OUTPUT_FILE="/etc/caddy/Caddyfile"
# Simulasi file registry dinamis (/etc/caddy/services.json) jika tidak ada
if [ ! -f "$REGISTRY_FILE" ]; then
cat << 'EOF' > "$REGISTRY_FILE"
{
"user-service": "10.0.1.50:3001",
"product-service": "10.0.1.60:3002",
"order-service": "10.0.1.70:3003"
}
EOF
fi
echo "[+] Membaca registry layanan dan membangun Caddyfile..."
python3 << 'EOF'
import json
with open('/etc/caddy/services.json') as f:
services = json.load(f)
# Baca template dasar Caddyfile
# Template berisi konfigurasi global, logging, dan CORS
try:
with open('/etc/caddy/Caddyfile.template', 'r') as t:
template = t.read()
except FileNotFoundError:
# Fallback template jika file template tidak ada
template = """# Global Options & Base Site
api.example.com {
encode gzip zstd
# DYNAMIC_ROUTES_PLACEHOLDER
handle {
respond "Gateway: Target Not Found" 404
}
}"""
routes = ""
for name, addr in services.items():
# Mengubah "user-service" menjadi path "/api/v1/users/*"
path_key = name.replace('-service', '')
routes += f"""
# Rilis Dinamis untuk {name}
handle /api/v1/{path_key}* {{
uri strip_prefix /api/v1
reverse_proxy {addr} {{
header_up X-Gateway-Autodiscovery "true"
}}
}}
"""
# Ganti placeholder dengan rute dinamis yang baru dibuat
new_config = template.replace("# DYNAMIC_ROUTES_PLACEHOLDER", routes)
with open('/etc/caddy/Caddyfile', 'w') as out:
out.write(new_config)
print("[+] Konfigurasi Caddyfile berhasil diperbarui di memori file.")
EOF
# Validasi konfigurasi sebelum diaplikasikan untuk mencegah crash di produksi
echo "[+] Melakukan validasi konfigurasi Caddy..."
caddy validate --config "$OUTPUT_FILE"
# Mengirim sinyal reload aman (zero-downtime atomic swap)
echo "[+] Memuat ulang konfigurasi Caddy..."
caddy reload --config "$OUTPUT_FILE"
echo "[✓] API Gateway berhasil disinkronkan dengan registry layanan!"
Pendekatan otomatisasi berbasis konfigurasi lokal ini sangat aman, mudah didebug, dan menghilangkan ketergantungan pada pustaka pihak ketiga yang rumit saat kita baru membangun ekosistem microservices pertama kita.
Monitoring dan Observability #
Menjalankan API Gateway di tingkat produksi mewajibkan kita memiliki visibilitas penuh terhadap lalu lintas yang melewatinya. Kita harus tahu berapa banyak permintaan yang sukses (HTTP 2xx), permintaan yang gagal (HTTP 5xx), serta waktu respons rata-rata (latency).
Sumber Data Observabilitas Caddy #
- JSON Access Logs: Sumber informasi terlengkap. Setiap field dicatat secara terstruktur dan dapat diekspor langsung ke agregator log seperti Grafana Loki, Elasticsearch (ELK), atau Datadog.
- Prometheus Metrics: Caddy memiliki modul metrik bawaan yang dapat diaktifkan melalui opsi global. Metrik ini memaparkan data performa CPU, memori, jumlah koneksi aktif, serta status HTTP secara real-time yang dapat divisualisasikan menggunakan Grafana Dashboard.
Berikut adalah contoh untuk memantau metrik Prometheus Caddy melalui opsi global Caddyfile serta skrip analisis log berbasis CLI menggunakan utilitas jq dan awk untuk mendeteksi slow endpoints secara real-time:
# ── Mengaktifkan Metrik Prometheus di Opsi Global ──────────────────
{
# Mengaktifkan endpoint admin pada port localhost:2019
admin localhost:2019
# Mengaktifkan pengumpulan metrik internal
prometheus
}
api.example.com {
log {
output file /var/log/caddy/api-access.log {
roll_size 100mb
}
format json
}
encode gzip zstd
handle /api/v1/* {
uri strip_prefix /api/v1
reverse_proxy backend:3000
}
}
Untuk menganalisis log akses secara real-time langsung dari terminal produksi, kita dapat menggunakan skrip bash diagnostik berikut:
#!/bin/bash
# analyze-gateway-logs.sh — Analisis cepat kinerja API Gateway dari log JSON
LOG_FILE="/var/log/caddy/api-access.log"
if [ ! -f "$LOG_FILE" ]; then
echo "[-] File log tidak ditemukan di $LOG_FILE"
exit 1
fi
echo "=== DIAGNOSTIK KINERJA API GATEWAY ==="
echo "Menganalisis 1000 baris permintaan terakhir..."
echo ""
# 1. Total Request & Deteksi Kode Status
tail -n 1000 "$LOG_FILE" | jq -s '
{
total_requests: length,
success_2xx: [ .[] | select(.status >= 200 and .status < 300) ] | length,
redirect_3xx: [ .[] | select(.status >= 300 and .status < 400) ] | length,
client_error_4xx: [ .[] | select(.status >= 400 and .status < 500) ] | length,
server_error_5xx: [ .[] | select(.status >= 500) ] | length
}
'
echo ""
echo "--- 5 ENDPOINT TERLAMBAT (Latency > 1.5 Detik) ---"
# Mengurutkan request berdasarkan durasi eksekusi terlama
tail -n 1000 "$LOG_FILE" | \
jq -r 'select(.duration > 1.5) | "\(.duration)s \t \(.request.method) \t \(.request.uri) \t (Status: \(.status))"' | \
sort -rn | head -5
echo ""
echo "--- 5 IP KLIEN DENGAN TRAFIK TERTINGGI ---"
# Mengidentifikasi IP klien yang paling agresif melakukan request
tail -n 1000 "$LOG_FILE" | \
jq -r '.request.remote_ip' | \
sort | uniq -c | sort -rn | head -5
Dengan mengaktifkan visualisasi metrik dan melakukan audit log secara rutin, kita dapat mendeteksi degradasi performa database backend sebelum hal itu berdampak luas pada pengguna akhir aplikasi kita.
Ringkasan #
- Titik Masuk Tunggal — Caddy API Gateway mereduksi kompleksitas arsitektur hilir dengan menyatukan perutean (routing), validasi CORS, dan kebijakan keamanan di tepi (edge) jaringan.
- Pembersihan Jalur (Strip Prefix) — Selalu gunakan
uri strip_prefix /api/vXagar layanan backend dapat berjalan secara mandiri tanpa perlu mengetahui struktur versi atau gateway yang menaunginya.- Circuit Breaker Pasif — Amankan backend dari fenomena kegagalan beruntun (cascading failure) dengan menyetel parameter
max_failsdanfail_durationpada direktifreverse_proxy.- Autentikasi Terpusat — Manfaatkan validasi token API Key (via
map) atau integrasikan token JWT di gateway untuk memotong CPU overhead verifikasi signature di backend.- Penyuntikan Korelasi (Trace ID) — Selalu sematkan UUID unik
{http.request.uuid}sebagaiX-Request-IDke upstream dan downstream demi mempermudah pelacakan rantai log di produksi.- Zero-Downtime Reload — Gunakan perintah
caddy validatedilanjutkancaddy reloaduntuk menerapkan perubahan registry layanan secara dinamis tanpa merusak koneksi aktif.