Kubernetes Service Nedir? Pod'lar Birbirini Nasıl Bulur
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
- Service, Kubernetes dokümantasyonu
- DNS for Services and Pods, Kubernetes dokümantasyonu
- Debug Services, Kubernetes dokümantasyonu
- Virtual IPs and Service Proxies, Kubernetes dokümantasyonu
- Services, Load Balancing, and Networking, Kubernetes dokümantasyonu