Kubernetes ortamlarında ingress yönetimi genellikle NGINX ya da Traefik ile özdeşleşmiş durumda. Ama son yıllarda Caddy’nin bu alana girişi ve özellikle otomatik TLS yönetimi konusundaki üstünlükleri, pek çok ekibin dikkatini çekmeye başladı. Eğer Caddy’nin sadeliğini ve otomatik HTTPS özelliğini Kubernetes cluster’ınıza taşımak istiyorsanız, doğru yerdesiniz.
Caddy’nin Kubernetes’te Kullanım Mantığı
Caddy, geleneksel ingress controller’lardan farklı bir yaklaşım sergiliyor. NGINX’in karmaşık annotation yığınlarına ya da Traefik’in middleware zincirlerine alışkınsanız, Caddy’nin Caddyfile tabanlı konfigürasyon anlayışı başta biraz farklı gelecek. Ama bir kez oturttuğunuzda, özellikle küçük ve orta ölçekli cluster’larda son derece verimli çalıştığını göreceksiniz.
Kubernetes’te Caddy’yi ingress controller olarak kullanmanın iki temel yolu var:
- caddy-ingress-controller: Resmi Caddy ekibi tarafından geliştirilen ve aktif olarak devam eden proje
- Manuel Caddy deployment: Caddy’yi bir DaemonSet ya da Deployment olarak ayağa kaldırıp ConfigMap üzerinden yönetme
Bu yazıda her iki yaklaşıma da bakacağız, ama ağırlıklı olarak caddy-ingress-controller üzerinde duracağız çünkü production ortamlar için bu yol çok daha mantıklı.
Ön Gereksinimler
Başlamadan önce şunlara ihtiyacınız var:
- Çalışan bir Kubernetes cluster’ı (v1.19+)
kubectlvehelmkurulu- Bir domain adı (otomatik TLS için)
- Cluster’ınıza dışarıdan erişim sağlayan bir LoadBalancer ya da NodePort
Ortamınızı test etmek için:
kubectl version --client
helm version
kubectl get nodes
Cluster node’larının Ready durumunda olduğunu ve en az bir worker node bulunduğunu doğrulayın.
Caddy Ingress Controller Kurulumu
Helm ile Kurulum
En hızlı yol Helm chart kullanmak. Önce repo’yu ekleyelim:
helm repo add caddy https://caddyserver.github.io/ingress/
helm repo update
Basit bir kurulum için:
kubectl create namespace caddy-system
helm install caddy-ingress-controller caddy/caddy-ingress-controller
--namespace caddy-system
--set [email protected]
--set ingressController.config.acmeCA=https://acme-v02.api.letsencrypt.org/directory
Kurulum tamamlandıktan sonra pod’ların ayağa kalktığını kontrol edin:
kubectl get pods -n caddy-system
kubectl get svc -n caddy-system
LoadBalancer tipi servis oluşturulmuş olmalı ve external IP atanmış olmalı. Cloud ortamında bu IP cloud provider’ınızdan gelir. Bare-metal kurulumda MetalLB ya da benzeri bir çözüm gerekir.
Manuel YAML ile Kurulum
Helm kullanmak istemeyenler ya da konfigürasyonu daha ince ayarlamak isteyenler için YAML dosyaları üzerinden gidebiliriz. Önce namespace ve ServiceAccount oluşturalım:
kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: caddy-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: caddy-ingress-controller
namespace: caddy-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: caddy-ingress-controller
rules:
- apiGroups: [""]
resources: ["endpoints", "services", "secrets", "configmaps", "namespaces"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses", "ingressclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses/status"]
verbs: ["update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: caddy-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: caddy-ingress-controller
subjects:
- kind: ServiceAccount
name: caddy-ingress-controller
namespace: caddy-system
EOF
Bu RBAC tanımları Caddy’nin cluster içindeki kaynakları okuyabilmesi için gerekli. Özellikle ingresses kaynağını izleyebilmesi kritik.
IngressClass Tanımı
Kubernetes 1.18’den itibaren IngressClass kavramı zorunlu hale geldi. Caddy için bunu tanımlıyoruz:
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: caddy
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: caddy.ingress.kubernetes.io/ingress-controller
EOF
is-default-class: "true" annotation’ını dikkatli kullanın. Bu, ingressClassName belirtilmemiş tüm Ingress kaynaklarının otomatik olarak Caddy’ye yönlendirilmesi anlamına gelir. Eğer cluster’ınızda birden fazla ingress controller varsa bu annotation’ı kaldırın.
İlk Uygulama Deployment’ı
Teoriyi bir kenara bırakıp gerçek bir senaryoya geçelim. Diyelim ki bir web uygulamanızı ve bir API servisinizi aynı cluster’da çalıştırıyorsunuz. Her ikisini de farklı subdomain’ler üzerinden dışarıya açmak istiyorsunuz.
Önce örnek uygulamaları ayağa kaldıralım:
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-app-svc
namespace: default
spec:
selector:
app: web-app
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: api-service
template:
metadata:
labels:
app: api-service
spec:
containers:
- name: api-service
image: hashicorp/http-echo:alpine
args:
- "-text=Merhaba API!"
ports:
- containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
name: api-service-svc
namespace: default
spec:
selector:
app: api-service
ports:
- port: 80
targetPort: 5678
EOF
Şimdi bu servisleri dışarıya açan Ingress kaynağını oluşturalım:
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: production-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: caddy
spec:
ingressClassName: caddy
rules:
- host: app.sirketiniz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app-svc
port:
number: 80
- host: api.sirketiniz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service-svc
port:
number: 80
EOF
Ingress oluştuktan birkaç dakika içinde Caddy Let’s Encrypt’ten otomatik olarak sertifika alır. Bu süreci takip etmek için:
kubectl logs -n caddy-system -l app.kubernetes.io/name=caddy-ingress-controller -f
Logları izlerken certificate obtained successfully mesajını görmelisiniz.
Otomatik TLS ve Sertifika Yönetimi
Caddy’nin en büyük avantajı olan otomatik TLS konusuna biraz daha derinlemesine bakalım. Caddy, Let’s Encrypt ile iletişim kurarken iki farklı challenge mekanizması kullanabilir:
- HTTP-01 Challenge: 80 numaralı port üzerinden. Cluster’ınızın 80 portuna dışarıdan erişilebilmesi gerekiyor.
- DNS-01 Challenge: DNS sağlayıcınız üzerinden. Port açmaya gerek yok, wildcard sertifikalar için ideal.
DNS-01 challenge için Caddy’yi özelleştirmek biraz daha fazla çaba gerektiriyor. Cloudflare kullananlar için değerleri Helm values dosyasına ekleyelim:
cat > caddy-values.yaml <<EOF
ingressController:
config:
email: [email protected]
acmeCA: https://acme-v02.api.letsencrypt.org/directory
debug: false
image:
repository: ghcr.io/caddyserver/ingress
tag: latest
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
replicaCount: 2
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
EOF
helm upgrade caddy-ingress-controller caddy/caddy-ingress-controller
--namespace caddy-system
-f caddy-values.yaml
Path-Based Routing ve Yük Dağılımı
Tek bir domain altında birden fazla servis sunan bir senaryo oldukça yaygın. Örneğin bir monorepo’dan çıkan microservice’leri tek domain altında topluyorsunuz:
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: microservices-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: caddy
spec:
ingressClassName: caddy
rules:
- host: platform.sirketiniz.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: users-service
port:
number: 80
- path: /orders
pathType: Prefix
backend:
service:
name: orders-service
port:
number: 80
- path: /notifications
pathType: Prefix
backend:
service:
name: notifications-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
EOF
Burada dikkat edilmesi gereken nokta path sıralaması. Caddy, daha spesifik path’lere öncelik veriyor, dolayısıyla / en sona gelmelidir, aksi halde diğer path’ler bu genel kural tarafından yakalanır.
TLS Sertifikaları İçin Secret Kullanımı
Kendi sertifikanızı kullanmak istediğiniz durumlar da olabilir. Kurumsal ortamlarda internal CA’dan alınmış sertifikalar veya wildcard sertifikalar bu kategoriye giriyor. Secret oluşturma ve Ingress’e bağlama:
# Sertifika ve key'i Secret olarak kaydet
kubectl create secret tls sirket-tls-secret
--cert=sirket.crt
--key=sirket.key
--namespace=default
# Ingress'te bu secret'ı kullan
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
namespace: default
spec:
ingressClassName: caddy
tls:
- hosts:
- app.sirketiniz.com
- www.sirketiniz.com
secretName: sirket-tls-secret
rules:
- host: app.sirketiniz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app-svc
port:
number: 80
EOF
Caddy bu durumda Let’s Encrypt’e gitmez, doğrudan Secret içindeki sertifikayı kullanır.
Monitoring ve Gözlemlenebilirlik
Production ortamda Caddy ingress controller’ınızın ne yaptığını bilmeniz gerekiyor. Caddy, Prometheus formatında metrik sunabiliyor. Bunun için pod’un annotation’larına bakalım:
# Mevcut pod annotation'larını kontrol et
kubectl get pods -n caddy-system -o jsonpath='{.items[0].metadata.annotations}'
# Caddy metrics endpoint'ini kontrol et
kubectl port-forward -n caddy-system svc/caddy-ingress-controller 2019:2019
curl http://localhost:2019/metrics
Eğer Prometheus Operator kullanıyorsanız, ServiceMonitor kaynağı oluşturabilirsiniz:
kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: caddy-ingress-monitor
namespace: caddy-system
labels:
release: prometheus
spec:
selector:
matchLabels:
app.kubernetes.io/name: caddy-ingress-controller
endpoints:
- port: metrics
interval: 30s
path: /metrics
EOF
Log seviyesini production’da warn ya da error olarak tutmanızı öneririm. Debug modunda log hacmi ciddi boyutlara ulaşabiliyor, özellikle trafik yoğunsa.
Gerçek Dünya Senaryosu: Blue-Green Deployment
Caddy ingress controller’ı blue-green deployment senaryolarında oldukça işe yarıyor. Diyelim ki uygulamanızın yeni versiyonunu sıfır downtime ile yayına almak istiyorsunuz:
# Green ortam (yeni versiyon) deploy et
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-green
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: web-app
version: green
template:
metadata:
labels:
app: web-app
version: green
spec:
containers:
- name: web-app
image: web-app:v2.0.0
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-app-green-svc
namespace: default
spec:
selector:
app: web-app
version: green
ports:
- port: 80
targetPort: 80
EOF
# Green ortam test edildikten sonra Ingress'i güncelle
kubectl patch ingress production-ingress -n default --type='json'
-p='[{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/service/name", "value":"web-app-green-svc"}]'
Bu yaklaşımla eski versiyon hala çalışmaya devam ediyor, bir sorun çıkarsa anında geri dönebiliyorsunuz.
Yaygın Sorunlar ve Çözümleri
Caddy ingress controller kullanırken sık karşılaşılan sorunları ve çözümlerini ele alalım.
Sertifika alamıyor: Let’s Encrypt rate limit’e takıldıysanız staging ortamını kullanın.
helm upgrade caddy-ingress-controller caddy/caddy-ingress-controller
--namespace caddy-system
--set ingressController.config.acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory
503 Service Unavailable hatası: Backend servisin endpoint’lerini kontrol edin.
kubectl get endpoints web-app-svc -n default
kubectl describe ingress production-ingress -n default
Ingress kuralı uygulanmıyor: IngressClass ayarlarını ve controller annotation’larını doğrulayın.
kubectl get ingressclass caddy -o yaml
kubectl describe ingress production-ingress
Pod restart döngüsü: Resource limit’leri gözden geçirin. Caddy görece az kaynak tüketse de cluster’ınızdaki trafik yoğunluğuna göre limit’leri ayarlamanız gerekebilir.
kubectl top pods -n caddy-system
kubectl describe pod -n caddy-system -l app.kubernetes.io/name=caddy-ingress-controller
Çoklu Namespace Desteği
Büyük cluster’larda farklı ekiplerin farklı namespace’lerde çalıştığı durumlar yaygın. Caddy ingress controller varsayılan olarak tüm namespace’lerdeki Ingress kaynaklarını izliyor. Bunu belirli namespace’lerle kısıtlamak isteyebilirsiniz:
helm upgrade caddy-ingress-controller caddy/caddy-ingress-controller
--namespace caddy-system
--set ingressController.watchNamespaces="default,staging,production"
Her ekibin kendi Ingress annotation’larını yönetmesi için namespace başına ayrı Caddy instance’ı çalıştırmak da bir seçenek. Bu yaklaşım izolasyon açısından daha güvenli ama kaynak kullanımı açısından daha maliyetli.
NGINX’ten Caddy’ye Geçiş
Halihazırda NGINX ingress kullanıyorsanız ve Caddy’ye geçmeyi düşünüyorsanız, geçiş süreci dikkatli planlanmalı. Her iki controller’ı aynı anda çalıştırıp trafiği kademeli aktarabilirsiniz:
# Mevcut NGINX ingress'leri listele
kubectl get ingress --all-namespaces -o wide
# IngressClass'ı kontrol et
kubectl get ingressclass
# Yeni Ingress'lerde Caddy'yi belirt, eskilerde NGINX kalsın
# Önce kritik olmayan servislerle başla
Test ortamında aynı uygulamayı hem NGINX hem Caddy üzerinden yayımlayıp karşılaştırma yapmanızı şiddetle tavsiye ederim. Özellikle NGINX’e özgü annotation’lar (nginx.ingress.kubernetes.io/*) Caddy’de çalışmayacak, bunların Caddy muadillerini bulmanız gerekiyor.
Performans Optimizasyonu
Caddy’nin Go tabanlı yapısı ve async mimarisi performans açısından avantaj sağlıyor. Ama birkaç ek ayarla bunu daha da ileriye taşıyabilirsiniz:
# HPA ile otomatik ölçeklendirme
kubectl apply -f - <<EOF
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: caddy-ingress-hpa
namespace: caddy-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: caddy-ingress-controller
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
EOF
Pod’ları birden fazla node’a dağıtmak için PodAntiAffinity kuralı ekleyin. Tüm Caddy pod’larının aynı node’a düşmesi, o node’un çökmesi halinde ciddi sorun yaratır.
Sonuç
Caddy ingress controller, özellikle otomatik TLS yönetimi, sade konfigürasyon yapısı ve düşük kaynak tüketimi açısından NGINX ve Traefik’e gerçek bir alternatif sunuyor. Küçük ve orta ölçekli cluster’larda, özellikle de TLS sertifika yönetiminin baş ağrısı haline geldiği ortamlarda Caddy ciddi bir zaman kazandırıyor.
Bununla birlikte bazı sınırlamaları da göz önünde bulundurun. NGINX’in annotation ekosistemi kadar geniş bir özellik seti henüz mevcut değil. Rewrite, rate limiting gibi gelişmiş senaryolarda Caddy’nin Kubernetes entegrasyonu hala olgunlaşıyor. Proje aktif geliştirme altında, GitHub reposunu düzenli takip etmenizi öneririm.
Production’a geçmeden önce staging cluster’ında yeterince test edin, özellikle TLS yenileme döngülerini ve yük altında davranışı gözlemleyin. Ama sonuçta Caddy’yi bir kez düzgün kurduğunuzda, “sertifikam neden yenilenmedi” sorusunu sormaktan kurtuluyorsunuz ki bu başlı başına büyük bir kazanım.