Modern mikroservis mimarilerinde gRPC protokolü giderek daha fazla tercih edilir hale geldi. Özellikle servisler arası yüksek performanslı iletişim gerektiren sistemlerde gRPC’nin HTTP/2 tabanlı yapısı ciddi avantajlar sağlıyor. Ancak bu servisleri dışa açarken ya da load balancer arkasına almak istediğinizde karşınıza bazı zorluklar çıkıyor. İşte tam burada Caddy devreye giriyor. Caddy’nin modern HTTP/2 ve gRPC proxy desteği, bu işi hem kolaylaştırıyor hem de güvenli bir şekilde yapmanızı mümkün kılıyor.
Bu yazıda gerçek dünya senaryolarıyla Caddy’yi bir gRPC ve HTTP/2 backend proxy olarak nasıl kullanacağınızı, hangi direktifleri nasıl yapılandıracağınızı ve olası sorunları nasıl çözeceğinizi ele alacağız.
gRPC ve HTTP/2 Neden Özel Bir Proxy Konfigürasyonu Gerektirir?
Klasik HTTP/1.1 proxy senaryolarından farklı olarak gRPC, kalıcı bağlantılar üzerinden çift yönlü streaming yapabilen bir protokoldür. Bu durum, birkaç önemli teknik gereksinimi beraberinde getirir.
- HTTP/2 zorunluluğu: gRPC sadece HTTP/2 üzerinden çalışır, HTTP/1.1 ile uyumlu değildir
- Uzun süreli bağlantılar: Streaming RPC’ler için bağlantının açık kalması gerekir
- Trafikte header yönetimi: gRPC, özel header’lar kullanır ve bunların doğru iletilmesi kritiktir
- TLS gereksinimleri: Birçok gRPC client, TLS olmadan H2 (HTTP/2) kullanmayı reddeder
- Timeout yönetimi: gRPC’nin kendi deadline mekanizması vardır, proxy timeout’ları bunu engellememeli
Nginx bu konuda biraz zahmetli olabiliyor. Özellikle gRPC için ayrı grpc_pass direktifini doğru configure etmek ve TLS sonlandırmayı sağlamak bazı durumlarda sizi Nginx Plus’a yönlendirebiliyor. Caddy ise HTTP/2’yi neredeyse otomatik olarak halleden ve gRPC proxy için hazır direktiflere sahip bir sunucu olarak bu işi gerçekten sadeleştiriyor.
Caddy Kurulumu
Sisteminizde Caddy yoksa önce onu kuralım. Debian/Ubuntu tabanlı sistemler için:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
RHEL/CentOS/Rocky Linux için:
dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy
Kurulumdan sonra servisi başlatıp durumunu kontrol edelim:
sudo systemctl enable --now caddy
sudo systemctl status caddy
Caddy versiyonunu doğrulayın. gRPC proxy için Caddy 2.x gereklidir:
caddy version
Temel gRPC Proxy Konfigürasyonu
Caddy’de gRPC proxy için reverse_proxy direktifini kullanıyoruz. Ancak burada dikkat edilmesi gereken önemli nokta var: backend’e bağlanırken protokolü açıkça belirtmeniz gerekiyor.
Senaryo: Elimizde localhost:50051 portunda çalışan bir gRPC servisi var. Bu servisi TLS ile dışa açmak istiyoruz.
api.example.com {
reverse_proxy h2c://localhost:50051
}
Buradaki h2c:// prefiksi çok önemli. Bu, Caddy’ye backend ile bağlantının cleartext HTTP/2 (TLS olmadan HTTP/2) kullanacağını söylüyor. Bu senaryo genellikle şu yapıda kullanılır:
- İstemci -> Caddy (TLS ile H2) -> Backend (h2c, TLS’siz H2)
Yani TLS sonlandırması Caddy’de yapılıyor, backend’e giden trafik iç ağda TLS’siz HTTP/2 ile iletiliyor. Bu oldukça yaygın bir production pattern’idir.
Eğer backend de TLS kullanıyorsa:
api.example.com {
reverse_proxy {
to https://localhost:50051
transport http {
versions 2
}
}
}
Gelişmiş gRPC Proxy Konfigürasyonu
Gerçek production ortamında genellikle daha fazla kontrol ihtiyaç duyuyorsunuz. Aşağıdaki konfigürasyon hem gRPC hem de standart HTTP/2 trafiğini yönetmek için daha kapsamlı bir örnek:
grpc.example.com {
# gRPC servislerine yönlendirme
@grpc {
protocol grpc
}
reverse_proxy @grpc h2c://localhost:50051 {
header_up X-Forwarded-Proto https
header_up X-Real-IP {remote_host}
# gRPC için flush interval ayarı - streaming için kritik
flush_interval -1
# Health check
health_uri /grpc.health.v1.Health/Check
health_interval 30s
health_timeout 5s
lb_policy round_robin
}
# Normal HTTP/2 istekleri için fallback
reverse_proxy localhost:8080 {
flush_interval -1
}
}
Bu konfigürasyonda birkaç önemli nokta var:
@grpcmatcher: Sadece gRPC trafiğini yakalıyor, Content-Type üzerindenflush_interval -1: Bu değer streaming gRPC için kritik. -1, Caddy’nin response’u buffer’lamadan anında client’a iletmesini sağlıyor- health_uri: gRPC Health Checking Protocol standart endpoint’i
Birden Fazla gRPC Servisi için Path Bazlı Yönlendirme
Mikroservis mimarisinde sıkça karşılaşılan bir senaryo şudur: Tek bir domain altında birden fazla gRPC servisi çalıştırmanız gerekiyor. Caddy bunu path bazlı yönlendirmeyle çok temiz bir şekilde hallediyor.
api.example.com {
# UserService
@userService {
path /user.UserService/*
}
reverse_proxy @userService h2c://localhost:50051
# OrderService
@orderService {
path /order.OrderService/*
}
reverse_proxy @orderService h2c://localhost:50052
# ProductService
@productService {
path /product.ProductService/*
}
reverse_proxy @productService h2c://localhost:50053
# gRPC reflection (geliştirme ortamında)
@reflection {
path /grpc.reflection.v1alpha.ServerReflection/*
}
reverse_proxy @reflection h2c://localhost:50051
# Wildcard - kalan tüm gRPC trafiği
@grpcDefault {
protocol grpc
}
reverse_proxy @grpcDefault h2c://localhost:50054
}
Bu yapıyı kullanırken servis path’lerinin proto dosyanızdaki package ve service isimlendirmesiyle uyumlu olduğundan emin olun. gRPC path formatı genellikle /package.ServiceName/MethodName şeklindedir.
Load Balancing ile gRPC Proxy
Yüksek trafikli ortamlarda tek bir backend yetmez, birden fazla instance arasında yük dağıtmanız gerekir. Ancak gRPC’de load balancing biraz özel dikkat ister. Normal HTTP/1.1’de her istek ayrı bağlantı üzerinden gidebilirken, gRPC’de uzun süreli bağlantılar bu dengelemeyi zorlaştırır.
grpc-lb.example.com {
@grpc {
protocol grpc
}
reverse_proxy @grpc {
to h2c://10.0.1.10:50051
to h2c://10.0.1.11:50051
to h2c://10.0.1.12:50051
# least_conn, round_robin veya random kullanabilirsiniz
# gRPC streaming için least_conn genellikle daha iyi sonuç verir
lb_policy least_conn
# Streaming bağlantılar için flush interval
flush_interval -1
# Passive health check
lb_try_duration 5s
lb_try_interval 250ms
# Active health check
health_uri /grpc.health.v1.Health/Check
health_interval 15s
health_timeout 3s
health_status 200
# Bağlantı havuzu ayarları
transport http {
keepalive 30s
keepalive_idle_conns 10
versions 2
}
}
}
lb_try_duration ve lb_try_interval parametreleri önemli. Bir backend yanıt vermediğinde Caddy ne kadar süre deneme yapacağını ve bu denemeler arasında ne kadar bekleyeceğini belirliyor. gRPC için bu değerlerin gerçekçi tutulması gerekiyor, aksi halde client’ın kendi deadline’ı dolmadan önce proxy gereksiz yere retry yapabilir.
gRPC ile TLS Mutual Authentication (mTLS)
Kurumsal ortamlarda servisler arası güvenlik için mTLS sıkça kullanılır. Hem client’ın hem de server’ın birbirini doğruladığı bu yapıyı Caddy ile şöyle kurabilirsiniz:
# Önce sertifikaları oluşturalım
# CA sertifikası
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
-subj "/CN=Internal CA/O=MyOrg"
# Backend servis sertifikası
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr
-subj "/CN=grpc-backend.internal"
openssl x509 -req -days 365 -in server.csr
-CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
# Caddy client sertifikası
openssl genrsa -out caddy-client.key 2048
openssl req -new -key caddy-client.key -out caddy-client.csr
-subj "/CN=caddy-proxy"
openssl x509 -req -days 365 -in caddy-client.csr
-CA ca.crt -CAkey ca.key -CAcreateserial -out caddy-client.crt
Caddy konfigürasyonu mTLS ile:
secure-grpc.example.com {
# Client'tan gelen mTLS (opsiyonel, gerekirse zorunlu yapılabilir)
tls {
client_auth {
mode require_and_verify
trusted_ca_cert_file /etc/caddy/certs/ca.crt
}
}
@grpc {
protocol grpc
}
reverse_proxy @grpc {
to https://grpc-backend.internal:50051
transport http {
tls_trusted_ca_certs /etc/caddy/certs/ca.crt
tls_client_auth /etc/caddy/certs/caddy-client.crt /etc/caddy/certs/caddy-client.key
versions 2
}
flush_interval -1
header_up X-Forwarded-For {remote_host}
}
}
HTTP/2 Only Backend Proxy
gRPC dışında, sadece HTTP/2 kullanan modern backend servislerinizi de Caddy üzerinden proxy yapabilirsiniz. Örneğin bazı Python (hypercorn, uvicorn), Go veya Java servisleri HTTP/2 ile ayağa kalkabilir.
app.example.com {
reverse_proxy {
to h2c://localhost:8000
transport http {
versions 2
# HTTP/2 için keep-alive kritik
keepalive 60s
keepalive_idle_conns 20
# Response buffer boyutu
response_body_size 100MB
}
# Header yönetimi
header_up Host {upstream_hostport}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
flush_interval -1
}
# HTTP/2 Server Push (HTTP/2 özelliği)
header {
Link "</static/app.js>; rel=preload; as=script"
Link "</static/styles.css>; rel=preload; as=style"
}
encode gzip zstd
}
Mixed Traffic: gRPC ve REST Aynı Port Üzerinde
Gerçek dünyada sıkça karşılaşılan bir senaryo: Aynı servis hem gRPC hem de REST HTTP/2 isteklerini kabul ediyor. Bu genellikle “gRPC-Gateway” pattern’iyle uygulanır.
api.example.com {
# gRPC trafiğini algıla ve yönlendir
@isGrpc {
protocol grpc
}
# JSON/REST trafiği için matcher
@isRest {
not protocol grpc
path /api/*
}
# gRPC backend
reverse_proxy @isGrpc h2c://localhost:9090 {
flush_interval -1
header_up Content-Type {>Content-Type}
}
# REST/HTTP backend (gRPC-Gateway)
reverse_proxy @isRest localhost:8080 {
header_up X-Forwarded-For {remote_host}
header_up X-Request-ID {uuid}
}
# Swagger/OpenAPI dokümantasyonu
@docs {
path /docs*
path /swagger*
}
reverse_proxy @docs localhost:8081
# Logging
log {
output file /var/log/caddy/api.example.com.log
format json
}
}
Bu yapıda {>Content-Type} kullanımına dikkat edin. gRPC istekleri application/grpc Content-Type ile gelir ve bu header’ın backend’e doğru iletilmesi şart.
Monitoring ve Logging
Production ortamında proxy’nizin sağlıklı çalışıp çalışmadığını takip etmek kritik. Caddy’nin logging sistemi JSON formatını desteklediği için ELK Stack veya Grafana Loki ile entegrasyonu oldukça kolay:
# Caddy log dosyasını tail ile takip edin
tail -f /var/log/caddy/api.example.com.log | jq '.'
Prometheus metrics için Caddy’nin admin endpoint’ini kullanabilirsiniz:
# Admin API ile istatistikleri sorgulayın
curl http://localhost:2019/metrics
curl http://localhost:2019/debug/vars
gRPC bağlantılarını test etmek için grpcurl aracını kullanın:
# grpcurl kurulumu
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
# Servisi listele
grpcurl -plaintext localhost:50051 list
# TLS ile Caddy üzerinden test
grpcurl -d '{"name": "test"}' api.example.com:443 helloworld.Greeter/SayHello
# Verbose mod ile header'ları gör
grpcurl -v -d '{"name": "test"}' api.example.com:443 helloworld.Greeter/SayHello
Yaygın Sorunlar ve Çözümleri
“received http2 frame when not expecting it” hatası: Bu genellikle backend’in HTTP/2 desteklemediğine işaret eder. Backend’i h2c:// yerine düz http:// ile çağırdığınızdan emin olun ya da backend’in HTTP/2’yi etkinleştirmesi gerekiyor.
Streaming yanıtların gecikmeli gelmesi: flush_interval değerini kontrol edin. Varsayılan değer buffer’lamaya neden olabilir. Streaming için mutlaka -1 kullanın:
# Konfigürasyonun yüklendiğini doğrulayın
caddy validate --config /etc/caddy/Caddyfile
# Konfigürasyonu reload edin
sudo systemctl reload caddy
# veya
caddy reload --config /etc/caddy/Caddyfile
gRPC bağlantı kesilmeleri (RST_STREAM): Bu sorun genellikle proxy timeout’larından kaynaklanır. Özellikle uzun süreli streaming bağlantıları için:
grpc.example.com {
@grpc {
protocol grpc
}
reverse_proxy @grpc h2c://localhost:50051 {
flush_interval -1
transport http {
# Uzun streaming için keepalive süresini artırın
keepalive 300s
# Dial timeout
dial_timeout 10s
# Response header timeout (gRPC için 0 yapın - sınırsız)
response_header_timeout 0s
versions 2
}
}
}
CORS sorunları (web tarayıcılarından gRPC-Web): Tarayıcıdan gRPC kullanıyorsanız (grpc-web), CORS header’larını eklemeniz gerekir:
grpcweb.example.com {
@grpcWeb {
header Content-Type application/grpc-web*
}
reverse_proxy @grpcWeb h2c://localhost:50051 {
flush_interval -1
header_up Content-Type {>Content-Type}
}
header {
Access-Control-Allow-Origin *
Access-Control-Allow-Methods "POST, GET, OPTIONS"
Access-Control-Allow-Headers "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-User-Agent, X-Grpc-Web"
Access-Control-Expose-Headers "grpc-status, grpc-message"
}
}
Performans İpuçları
Production ortamına geçmeden önce sistem seviyesinde birkaç ayar yapmanız performansı önemli ölçüde iyileştirebilir:
# /etc/sysctl.d/99-caddy-grpc.conf
# Ağ buffer boyutlarını artır
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# TIME_WAIT soketlerini hızlı geri dönüştür
net.ipv4.tcp_tw_reuse = 1
# SYN backlog
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 65535
# Değişiklikleri uygula
sudo sysctl -p /etc/sysctl.d/99-caddy-grpc.conf
Caddy’nin kendi konfigürasyonunda da bazı global ayarlar yapılabilir:
{
# Admin endpoint
admin localhost:2019
# HTTP/2 için gerekli
servers {
protocols h1 h2 h3
}
# TLS cache
storage file_system {
root /var/lib/caddy
}
# Log seviyesi
log {
level INFO
}
}
grpc.example.com {
# Servis konfigürasyonu buraya
}
Sonuç
Caddy, gRPC ve HTTP/2 proxy senaryolarında hem kurulum kolaylığı hem de konfigürasyon sadeliği açısından gerçekten güçlü bir tercih. Özellikle otomatik TLS yönetimi ve HTTP/2 desteğinin kutudan çıkma özelliği, Nginx veya HAProxy ile karşılaştırıldığında ciddi bir zaman tasarrufu sağlıyor.
Bu yazıda ele aldığımız senaryoları özetlemek gerekirse: basit gRPC proxy’den load balancing’e, mTLS’den mixed traffic yönetimine kadar Caddy’nin reverse_proxy direktifi ve h2c:// protokol prefiksi bu işlerin büyük çoğunluğunu karşılıyor. flush_interval -1 streaming için neredeyse zorunlu, transport http bloğu ise backend bağlantı davranışını ince ayarlamak için en kritik yer.
Production’a geçmeden önce caddy validate ile konfigürasyonunuzu mutlaka doğrulayın, grpcurl ile gerçek gRPC çağrıları yaparak end-to-end testi tamamlayın ve streaming senaryolarınızı da ayrıca test etmeyi ihmal etmeyin. Timeout ve keepalive ayarları uygulama davranışınıza göre kalibre edilmeli, tek bir evrensel değer yoktur.
Mikro servis geçişlerinde veya varolan gRPC altyapınıza API gateway eklerken Caddy’yi ciddi bir alternatif olarak değerlendirmenizi öneririm.