İçeriğe geç
KubeAtlas
Kubernetes DevOps Networking Troubleshooting kubectl

Kubernetes Service Nedir? Pod'lar Birbirini Nasıl Bulur

Onur Ömer Tunç 5 dk okuma

Frontend uygulamanız backend pod’una IP adresiyle bağlanıyor ve her şey çalışıyor. Sonra backend’in yeni sürümünü yüklüyorsunuz, eski pod silinip yerine yenisi açılıyor ve frontend cevap alamıyor, çünkü yeni pod başka bir adreste. Kubernetes bu sorunu Service ile çözer.

Pod’un IP adresine neden güvenemezsiniz

Kubernetes’te her pod’un kendi IP adresi vardır, ama Kubernetes dokümanı pod’ları geçici kaynaklar olarak tanımlar ve tek bir pod’un uzun süre ayakta kalacağına güvenmemenizi söyler.

Yeni bir sürüm yüklediğinizde eski pod’lar silinir ve yerlerine yenileri açılır. Pod’un çalıştığı node arızalandığında ya da bakıma alındığında da aynısı olur. Her yeni pod kendi IP adresiyle gelir. Frontend’in yapılandırmasında eski adres yazıyorsa istek artık var olmayan bir pod’a gider.

İkinci sorun kopya sayısı. Aynı uygulamadan genelde birden fazla pod çalışır. Backend üç pod’la çalışıyorsa frontend hangisine gidecek? Yarın on pod’a çıkarsanız (ölçekleme), on adresin listesini uygulama kodunun tutması gerekmemeli.

Bir taksi durağını düşünün. Durağın telefon numarası yıllarca aynı kalır. Aradığınızda hangi şoförün geleceğini bilmezsiniz, bilmeniz de gerekmez. Şoförler değişir, sayıları artar ya da azalır, numara aynı kalır. Kubernetes’te durağın numarası Service’tir, şoförler de pod’lar.

Service sabit bir adres verir

Service, aynı işi yapan bir grup pod’un önüne konan sabit bir adrestir. Frontend doğrudan Service’e bağlanır ve arkada kaç pod olduğunu ya da hangisinin yeni açıldığını bilmesi gerekmez.

ClusterIP, Service’in varsayılan tipidir: tanımda tip yazmazsanız Kubernetes ClusterIP oluşturur. Bu Service’e bir sanal IP adresi (cluster IP) verilir ve bu adrese yalnızca cluster’ın içinden ulaşılır. Frontend’den backend’e ya da backend’den veritabanına giden iç trafik için doğru tip budur.

Sanal adrese gelen isteği bir pod’a ileten bileşen, standart kurulumda her node’da çalışan kube-proxy’dir. Varsayılan iptables modunda, aynı istemciyi hep aynı pod’a gönderen oturum bağlılığı (session affinity) ayarlanmadıysa, isteği alacak pod rastgele seçilir.

Service hangi pod’lara gideceğini nereden biliyor

Service pod’ları etiketlerle bulur. Etiket (label), pod’a eklenen bir anahtar-değer çiftidir, örneğin app: backend. Service tanımındaki seçici (selector) hangi etiketi taşıyan pod’lara trafik gideceğini söyler.

Backend Deployment’ının pod şablonunda etiket şöyle durur:

template:
  metadata:
    labels:
      app: backend

Bu pod’ları bulan Service:

apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - port: 80
      targetPort: 8080

Kubernetes, selector’a uyan pod’ları sürekli izler ve adreslerini EndpointSlice adlı bir nesnede tutar. Listeyi de kendisi günceller: app: backend etiketiyle açılan yeni pod listeye girer, silinen pod çıkar.

type: ClusterIP satırı zorunlu değil, ama yazmanızı öneririz. Dosyayı aylar sonra açan kişi varsayılanın ne olduğunu hatırlamak zorunda kalmaz.

port ile targetPort arasındaki fark

Bu iki alan sık karıştırılır:

  • port, Service’in kendi portudur. Diğer pod’lar Service’e bu porttan bağlanır.
  • targetPort, pod’daki uygulamanın gerçekten dinlediği porttur. Service gelen isteği buraya iletir.

Yukarıdaki örnekte frontend backend:80 adresine istek atar, Service de bu isteği pod’un 8080 portuna iletir. İki değer aynı olabilir. targetPort alanını hiç yazmazsanız Kubernetes onu port ile aynı değere ayarlar.

Sık yapılan bir hata, targetPort alanına uygulamanın dinlemediği bir port yazmaktır. Bu durumda Service de pod’lar da ayakta görünür, ama istek uygulamaya ulaşmaz.

Pod’lar Service’e adıyla ulaşır

Cluster IP’yi de koda yazmanız gerekmez. Kubernetes her Service için bir DNS kaydı oluşturur ve bu ad Service’in cluster IP’sine çözülür. Aynı namespace’teki (cluster içindeki mantıksal bölme) bir pod backend’e http://backend adresiyle ulaşır, uygulamanın yapılandırmasına da yalnızca bu ad yazılır.

Namespace yazılmayan ad yalnızca sorgu yapan pod’un kendi namespace’inde aranır. Service başka bir namespace’teyse adın sonuna namespace eklenir: prod namespace’indeki backend Service’ine başka bir namespace’ten backend.prod adıyla ulaşılır.

Kendi cluster’ınızda deneyin

Önce üç kopyalı bir Deployment açın ve pod’ların IP adreslerine bakın. kubectl create deployment pod’lara app=backend etiketini kendisi koyar:

kubectl create deployment backend --image=nginx --replicas=3
kubectl get pods -o wide

Sonra bir Service oluşturun. kubectl expose, Deployment’ın selector’ını alır ve aynı selector’la bir Service kurar. targetPort yazmadığımız için 80 kabul edilir:

kubectl expose deployment backend --port=80
kubectl get service backend
kubectl get endpointslices -l kubernetes.io/service-name=backend

Son komutun ENDPOINTS sütununda üç pod’un IP adresi görünür. Şimdi bir pod’u silin, sonra pod’lara ve EndpointSlice’a yeniden bakın:

kubectl delete pod <pod-adı>
kubectl get pods -o wide
kubectl get endpointslices -l kubernetes.io/service-name=backend

Deployment silinen pod’un yerine yenisini açar. Yeni pod’un adresi listeye girer, Service’in cluster IP’si ise değişmez. Son olarak cluster’ın içinden Service’e adıyla istek atın:

kubectl run test --rm -it --restart=Never --image=busybox -- wget -qO- http://backend

Ekrana nginx’in varsayılan sayfasının HTML’i gelirse Service çalışıyor.

Service cevap vermiyorsa ilk nereye bakılır

kubectl get service Service’i ve cluster IP’sini normal gösteriyor, ama istekler cevapsız kalıyorsa ilk bakılacak yer EndpointSlice’tır:

kubectl get endpointslices -l kubernetes.io/service-name=backend

ENDPOINTS sütununda <none> yazıyorsa Service hiçbir pod bulamamıştır. Kubernetes dokümanına göre yaygın bir sebep, selector ile pod etiketleri arasındaki yazım hatasıdır. Service app: backned arıyorsa app: backend etiketli pod’lar listeye girmez. İkisini yan yana görmek için:

kubectl describe service backend
kubectl get pods --show-labels

Eski kaynaklarda bunun yerine kubectl get endpoints göreceksiniz. Endpoints API’si Kubernetes v1.33’ten beri kullanımdan kaldırılmış (deprecated) durumda ve yerini EndpointSlice aldı.

Bundan sonra ne öğrenmeli

Alıştırmayı bir kez kendi cluster’ınızda yapın. Bir pod’u silip EndpointSlice listesinin değiştiğini görmek konuyu oturtur. Ardından selector’a bilerek yanlış bir etiket yazın ve listenin boşaldığını izleyin. Sonraki konu, Service’i cluster dışına açmak: NodePort ve LoadBalancer tipleri ile Ingress ve Gateway.

Kubernetes’i bu tür alıştırmalarla, gerçek cluster’lar üzerinde uygulamalı öğrettiğimiz bir DevOps eğitim programımız var. Müfredat ve başvuru: kubeatlas.com/egitimler/devops-egitimi

Kaynaklar

Etiketler Kubernetes DevOps Networking Troubleshooting kubectl