Kubernetes Service Türleri: ClusterIP, NodePort ve LoadBalancer

Kubernetes öğrenmeye başladığında en çok kafayı karıştıran konulardan biri Service tipleri oluyor. “Neden bu kadar çok seçenek var? Hangisini ne zaman kullanacağım?” diye düşünmemek elde değil. Ama aslında mantık çok basit: Uygulamanı kimin, nereden erişebileceğini belirliyorsun. Cluster içinden mi? Dışarıdan mı? Cloud ortamında mı çalışıyorsun yoksa bare-metal üzerinde mi? Bu sorulara verdiğin cevaplara göre doğru Service tipini seçiyorsun. Gelin bu üç temel Service tipini gerçek dünya örnekleriyle birlikte inceleyelim.

Kubernetes Service Nedir, Neden Lazım?

Pod’lar geçici yapılardır. Bugün ayakta olan bir Pod, yarın crash edebilir, yeniden başlatılabilir, scale down yapıldığında silinebilir. Her yeniden başlatmada IP adresi değişir. Peki uygulamalar birbirleriyle nasıl konuşacak? Sürekli değişen IP adreslerini nasıl takip edecekler?

İşte Service nesnesi tam bu sorunu çözüyor. Service, Pod’ların önünde duran sabit bir endpoint sağlıyor. Pod’lar ölüp yeniden doğsa bile Service aynı IP ve DNS adresinden erişilebilir olmaya devam ediyor. Arkasındaki Pod’ları label selector mekanizmasıyla buluyor ve trafiği yönlendiriyor.

Kubernetes’te dört Service tipi var: ClusterIP, NodePort, LoadBalancer ve ExternalName. Bu yazıda ilk üçünü derinlemesine inceleyeceğiz çünkü gündelik operasyonlarda en çok bunlarla karşılaşıyorsun.

ClusterIP: Cluster İçi İletişimin Temeli

ClusterIP, varsayılan Service tipidir. Yani YAML dosyanda type alanını belirtmezsen otomatik olarak ClusterIP oluşturulur. Bu Service tipi sadece cluster içinden erişilebilir. Dışarıdan direkt olarak erişim sağlanamaz.

Ne zaman kullanırsın?

Microservice mimarisinde backend servisleri, veritabanları, cache katmanları, internal API’ler gibi dışarıya açılmaması gereken her şey için ClusterIP kullanırsın. Örneğin frontend Pod’larının backend API’ye ulaşması, backend’in Redis’e bağlanması, uygulama servisinin PostgreSQL’e erişmesi bunların hepsi ClusterIP üzerinden olur.

ClusterIP Örneği: Backend API Servisi

Aşağıdaki örnekte basit bir backend deployment ve buna ait ClusterIP Service tanımlıyoruz:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend-api
  template:
    metadata:
      labels:
        app: backend-api
        tier: backend
    spec:
      containers:
      - name: api
        image: myapp/backend:v2.1.0
        ports:
        - containerPort: 8080
        env:
        - name: DB_HOST
          value: postgres-service
        - name: REDIS_HOST
          value: redis-service
---
apiVersion: v1
kind: Service
metadata:
  name: backend-api-service
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: backend-api
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
EOF

Bu örnekte dikkat edilmesi gereken noktalar şunlar:

  • port: 80: Service’in cluster içinde dinlediği port
  • targetPort: 8080: Trafiğin yönlendirileceği Pod portu
  • selector: Hangi Pod’lara trafik göndereceğini belirliyor

Service oluştuktan sonra cluster içindeki herhangi bir Pod, backend-api-service.production.svc.cluster.local DNS adresiyle ya da kısaca aynı namespace içindeyse backend-api-service ile bu servise ulaşabilir.

ClusterIP ile Headless Service

Bazen Service’in yük dengeleme yapmasını değil, direkt olarak Pod IP’lerini döndürmesini isteyebilirsin. StatefulSet kullanırken ya da kendi load balancing mantığını uygulamak istediğinde headless Service kullanırsın. Bunun için clusterIP: None yazman yeterli:

apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
  namespace: production
spec:
  clusterIP: None
  selector:
    app: postgresql
  ports:
  - port: 5432
    targetPort: 5432

Headless Service ile DNS sorgusu yaptığında tek bir virtual IP yerine Pod’ların gerçek IP adreslerini alırsın. PostgreSQL cluster, Kafka, Elasticsearch gibi state’li uygulamalarda bu çok işe yarıyor.

NodePort: Basit Dışarı Açılma Yöntemi

NodePort, ClusterIP’nin üzerine inşa edilmiş bir Service tipidir. Aslında bir NodePort Service oluşturduğunda arka planda ClusterIP de otomatik olarak oluşturuluyor. Farkı şu: NodePort, cluster’daki her node üzerinde belirli bir port açarak dışarıdan erişim sağlıyor.

Varsayılan NodePort aralığı 30000-32767 arasındadır. Bu değeri istersen manuel olarak belirtebilir ya da Kubernetes’in otomatik atamasına bırakabilirsin.

Nasıl çalışır?

Dışarıdan gelen trafik önce herhangi bir node’un NodePort adresine ulaşıyor (NodeIP:NodePort), oradan ClusterIP’ye, oradan da ilgili Pod’lara yönlendiriliyor.

Ne zaman kullanırsın?

  • Development ve test ortamlarında hızlıca dışarıya açmak için
  • On-premise ya da bare-metal Kubernetes kurulumlarında
  • Cloud load balancer’a para ödemek istemediğin PoC ortamlarında
  • CI/CD pipeline’larında geçici erişim için

NodePort Örneği: Frontend Uygulaması

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: staging
  labels:
    app: frontend
    env: staging
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 3000
    nodePort: 30080

Bu konfigürasyonda:

  • port: 80: Cluster içi iletişim için ClusterIP portu
  • targetPort: 3000: React uygulamasının çalıştığı container portu
  • nodePort: 30080: Dışarıdan erişim için node üzerindeki port

Bu Service ile uygulamana http://192.168.1.100:30080 gibi node IP’si ve NodePort kombinasyonuyla erişebilirsin.

NodePort ile External Traffic Policy

NodePort kullanırken dikkat etmen gereken önemli bir konu var: externalTrafficPolicy. Varsayılan değer Cluster‘dır, yani trafik cluster’daki herhangi bir node’a gelse bile Pod’un başka bir node’da olabileceğinden ek bir network hop yapılabilir. Bu durumda kaynak IP’yi kaybedersin.

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: staging
spec:
  type: NodePort
  externalTrafficPolicy: Local
  selector:
    app: frontend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
    nodePort: 30080

externalTrafficPolicy: Local ayarladığında trafik sadece o node üzerinde çalışan Pod’lara yönlendirilir, kaynak IP korunur. Ama şunu unutma: Eğer o node’da ilgili Pod yoksa trafik drop edilir. Bu yüzden uygulamana dikkatli planlamalısın.

NodePort’un Sınırlamaları

NodePort pratikte bazı ciddi kısıtlamalar içeriyor:

  • Her servise 30000-32767 arasında ayrı bir port atamalısın, bu yönetimi zorlaştırıyor
  • Kullanıcılara http://example.com:30080 gibi bir adres veremen gerekiyor ki bu hiç profesyonel görünmüyor
  • Node IP değiştiğinde konfigürasyonu güncellemelisin
  • Production ortamda genellikle önüne bir Nginx ya da HAProxy koyman gerekiyor

Bu nedenle NodePort’u üretim ortamında direkt kullanmak yerine çoğunlukla Ingress controller’ların NodePort üzerinden dışarıya açılmasında kullanıyoruz.

LoadBalancer: Cloud Native Çözüm

LoadBalancer Service tipi, cloud provider’larla entegre çalışmak üzere tasarlanmış. AWS, GCP, Azure, DigitalOcean gibi cloud ortamlarında LoadBalancer türünde bir Service oluşturduğunda cloud provider otomatik olarak bir external load balancer provizyon ediyor ve bunu Service’e bağlıyor. Böylece dışarıdan erişilebilir bir external IP adresi elde ediyorsun.

LoadBalancer, NodePort ve ClusterIP’nin tüm özelliklerini de içeriyor. Yani bir hiyerarşi var: ClusterIP < NodePort < LoadBalancer.

Ne zaman kullanırsın?

  • Production ortamında cloud üzerinde çalışıyorsan
  • HTTP dışında protokoller için (TCP, UDP) dışarıya açılman gerekiyorsa
  • Ingress’in desteklemediği senaryolarda (database, game server, vs.)
  • Basit ve hızlı bir dış erişim noktası lazımsa

LoadBalancer Örneği: Production Web Uygulaması

apiVersion: v1
kind: Service
metadata:
  name: webapp-loadbalancer
  namespace: production
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"
spec:
  type: LoadBalancer
  selector:
    app: webapp
    tier: frontend
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
  - name: https
    protocol: TCP
    port: 443
    targetPort: 8443
  loadBalancerSourceRanges:
  - 0.0.0.0/0

AWS üzerinde service.beta.kubernetes.io/aws-load-balancer-type: "nlb" annotation’ı ile Network Load Balancer oluşturuyoruz. Classic Load Balancer yerine NLB tercih etmek genellikle daha iyi performans ve static IP desteği sunuyor.

LoadBalancer ile IP Whitelist

Güvenlik açısından LoadBalancer Service’e sadece belirli IP aralıklarından erişim izni verebilirsin:

apiVersion: v1
kind: Service
metadata:
  name: admin-panel-lb
  namespace: production
spec:
  type: LoadBalancer
  selector:
    app: admin-panel
  ports:
  - protocol: TCP
    port: 443
    targetPort: 8443
  loadBalancerSourceRanges:
  - 10.0.0.0/8
  - 192.168.1.0/24
  - 203.0.113.50/32

Bu sayede sadece belirlediğin IP aralıklarından gelen trafiği kabul ediyorsun. Örneğin ofis IP’si, VPN aralıkları veya belirli müşteri IP’leri gibi.

LoadBalancer’ın Dezavantajları

LoadBalancer tipinin en büyük sorunu maliyet. Her LoadBalancer Service için cloud provider’dan ayrı bir load balancer provizyon ediliyor ve bunların hepsi para ediyor. Diyelim ki 20 servisin var, 20 ayrı load balancer için aylık ciddi bir maliyet ödeyebilirsin.

Bu sorunu çözmek için genellikle şu yaklaşımı kullanıyoruz: Tek bir LoadBalancer Service ile Nginx Ingress Controller’ı dışarıya açıyoruz, tüm HTTP/HTTPS trafiği bu tekli load balancer üzerinden içeri giriyor, Ingress kuralları ile doğru servislere yönlendiriyoruz.

Gerçek Dünya Senaryosu: E-ticaret Uygulaması

Şimdi bir e-ticaret uygulaması için tüm bu Service tiplerini nasıl birlikte kullanacağımıza bakalım. Uygulamamızda şu bileşenler var: Frontend (React), Backend API (Node.js), Ödeme Servisi (Java), PostgreSQL ve Redis.

# PostgreSQL - Sadece cluster içinden erişilecek, ClusterIP
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
  namespace: ecommerce
spec:
  type: ClusterIP
  selector:
    app: postgresql
  ports:
  - port: 5432
    targetPort: 5432
---
# Redis Cache - Sadece cluster içinden, ClusterIP
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: ecommerce
spec:
  type: ClusterIP
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
---
# Backend API - Cluster içinden erişilecek, frontend ve diğer servisler kullanacak
apiVersion: v1
kind: Service
metadata:
  name: backend-api-service
  namespace: ecommerce
spec:
  type: ClusterIP
  selector:
    app: backend-api
  ports:
  - port: 80
    targetPort: 3000
---
# Ödeme Servisi - Özellikle izole, sadece backend erişebilmeli
apiVersion: v1
kind: Service
metadata:
  name: payment-service
  namespace: ecommerce
spec:
  type: ClusterIP
  selector:
    app: payment-service
  ports:
  - port: 8080
    targetPort: 8080
---
# Ingress Controller için dışarıya açık LoadBalancer
# Tüm HTTP/HTTPS trafiği buradan girecek
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: ingress-nginx
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https

Bu mimaride sadece Ingress Controller dışarıya açık ve tek bir cloud load balancer kullanıyoruz. Tüm iç servisler ClusterIP ile izole.

Service Durumunu Kontrol Etmek

Service’leri oluşturduktan sonra durumlarını düzenli olarak kontrol etmek önemli. İşte en sık kullandığım komutlar:

# Tüm service'leri listele
kubectl get services -n production

# Detaylı service bilgisi
kubectl describe service backend-api-service -n production

# Service endpoint'lerini görüntüle (hangi Pod'lara bağlı)
kubectl get endpoints backend-api-service -n production

# Service'e bağlı Pod'ları görüntüle
kubectl get pods -n production -l app=backend-api

# LoadBalancer external IP'sini izle
kubectl get service webapp-loadbalancer -n production -w

# Tüm namespace'lerdeki service'leri göster
kubectl get services --all-namespaces

# Service'in selector'ını hızlıca kontrol et
kubectl get service backend-api-service -n production -o jsonpath='{.spec.selector}'

Özellikle kubectl get endpoints komutu çok kritik. Eğer bir servise erişemiyorsan ilk kontrol etmen gereken şey endpoint’lerin dolup dolmadığı. Boş endpoint listesi genellikle selector eşleşmesi olmadığı anlamına gelir, yani Pod label’larını ve Service selector’ını kontrol etmen gerekiyor.

Troubleshooting: Yaygın Sorunlar ve Çözümleri

Servis’e erişilemiyor:

# Endpoint'leri kontrol et
kubectl get endpoints my-service -n production

# Pod'ların çalışıp çalışmadığını kontrol et
kubectl get pods -n production -l app=my-app

# Pod'un gerçekten o portu dinleyip dinlemediğini test et
kubectl exec -it debug-pod -n production -- curl http://pod-ip:8080/health

# DNS çözümlemesini test et
kubectl exec -it debug-pod -n production -- nslookup my-service.production.svc.cluster.local

# kube-proxy'nin çalışıp çalışmadığını kontrol et
kubectl get pods -n kube-system | grep kube-proxy

NodePort erişilemiyor:

  • Node’un firewall kurallarını kontrol et, NodePort aralığı açık mı?
  • Security group veya network policy engelliyor olabilir
  • Node IP’sinin doğru olduğundan emin ol

LoadBalancer External IP pending durumunda kalıyor:

Bu genellikle cloud controller manager’ın düzgün çalışmamasından kaynaklanır. Cloud provider’ın IAM izinlerini, cloud controller manager loglarını kontrol etmen gerekiyor.

kubectl describe service webapp-loadbalancer -n production
kubectl logs -n kube-system -l app=cloud-controller-manager

Service Tipi Seçim Kriterleri

Hangi Service tipini kullanacağına karar verirken şu soruları kendin sorabilirsin:

  • Sadece cluster içi erişim mi gerekiyor? ClusterIP kullan. Veritabanları, cache, internal API’ler için varsayılan seçim.
  • Dışarıya açmak istiyorsun ama cloud load balancer’a gerek yok mu? NodePort düşün. Development, test ortamları, bare-metal kurulumlar için ideal.
  • Production’da cloud üzerinde HTTP dışı protokol mü kullanıyorsun? LoadBalancer kullan. TCP/UDP game server, database proxy, streaming gibi senaryolar için.
  • Production’da HTTP/HTTPS trafiği mi yöneteceksin? Genellikle tek bir LoadBalancer Service + Ingress Controller kombinasyonu en mantıklısı.
  • StatefulSet kullanıyor ve Pod’lara direkt ulaşman mı lazım? Headless Service (ClusterIP: None) kullan.

Sonuç

Kubernetes Service tipleri ilk başta kafa karıştırıcı görünse de aslında çok mantıklı bir hiyerarşi sunuyor. ClusterIP ile güvenli bir şekilde cluster içi iletişimi sağlıyorsun, NodePort ile basit dışa açılma imkanı elde ediyorsun, LoadBalancer ile cloud native bir çözüme geçiyorsun.

Pratikte en yaygın gördüğüm mimari şöyle: İç servisler (veritabanları, cache, backend API’ler) ClusterIP ile izole tutulur, dışarıya açılması gereken tek nokta Ingress Controller’dır ve o da bir LoadBalancer Service üzerinden erişilebilir. Bu yaklaşım hem maliyeti minimize eder hem de güvenlik yüzeyini küçük tutar.

Bir de şunu söyleyeyim: Service tip seçimi sadece teknik bir karar değil, aynı zamanda güvenlik ve maliyet kararı. Production’a çıkmadan önce hangi servisin gerçekten dışarıya açık olması gerektiğini sorgula. Gereksiz yere dışarıya açılmış her servis potansiyel bir saldırı vektörü. Minimum exposure prensibiyle hareket et, sadece açık olması gerekeni aç, gerisini ClusterIP’de bırak.

Yorum yapın