Docker'a alışkın biri için Kubernetes'in ilk günü kafa karıştırıcıdır: docker run ile tek satırda başlattığın şey için burada üç ayrı nesne tanımlaman gerekir. Kubernetes Pod, Deployment ve Service kavramları bir kez oturduğunda ise bu üçlemenin neden gerekli olduğu netleşir — çünkü her biri farklı bir soruyu cevaplar: konteyner nerede çalışıyor, kaç kopya ayakta duracak ve bu kopyalara nasıl ulaşılacak.
Bu rehberde bu üç nesneyi tek tek ele alacağım; aralarındaki sahiplik zincirini (Deployment → ReplicaSet → Pod), etiket seçicilerin neden her şeyi birbirine bağlayan tutkal olduğunu, Service tiplerinin farkını ve baştan sona çalışan bir örneği adım adım kuracağız. Sonunda da yeni başlayanların en sık takıldığı hataları toplu hâlde göreceksin. Konteyner kavramına yabancıysan önce Docker nedir başlangıç rehberi yazısına göz atman iyi olur; Kubernetes'in yönettiği şey nihayetinde konteynerlerdir.
Pod: En Küçük Dağıtım Birimi#
Kubernetes konteynerleri doğrudan çalıştırmaz; onları Pod adı verilen bir sarmalayıcının içine koyar. Bir Pod, bir ya da birden fazla konteyneri barındırır ve bu konteynerler üç şeyi paylaşır: aynı ağ ad alanı (yani aynı IP adresi ve port aralığı), aynı hacimler ve aynı yaşam döngüsü. Aynı Pod içindeki iki konteyner birbirine localhost üzerinden ulaşır.
Çoğu durumda bir Pod'da tek konteyner olur. Birden fazla konteyner koymanın anlamlı olduğu senaryolar sınırlıdır: ana uygulamanın yanına log toplayan bir yardımcı (sidecar), bir yapılandırma dosyasını hazırlayan bir başlangıç konteyneri (initContainer) ya da yerel bir proxy. "İki farklı servisi tek Pod'a koyayım" fikri neredeyse her zaman yanlıştır, çünkü Pod bir bütün olarak ölçeklenir; veritabanını uygulamayla aynı Pod'a koyarsan uygulamayı 5 kopyaya çıkardığında 5 veritabanı elde edersin.
En yalın Pod tanımı şöyle görünür:
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
resources:
requests: # zamanlayıcının yer ayırırken kullandığı taban
cpu: "50m"
memory: "64Mi"
limits: # bu sınırı aşarsa kısıtlanır ya da öldürülür
cpu: "500m"
memory: "256Mi"
Ama üretimde Pod'u tek başına oluşturmazsın. Sebebi basit: Pod kalıcı değildir. Bulunduğu düğüm çökerse, Pod da gider ve kimse yerine yenisini koymaz. IP adresi her yeniden oluşturmada değişir. Pod'ları ayakta tutma işi bir üst nesnenin, yani Deployment'ın görevidir.
Deployment: Pod'ları Kim Ayakta Tutuyor#
Deployment, "şu şablona uyan Pod'lardan her zaman N tane çalışsın" beyanıdır. Bunu doğrudan yapmaz; arada bir ReplicaSet oluşturur ve asıl sayım işini o yürütür. Zincir şöyledir:
Deployment → ReplicaSet → Pod, Pod, Pod...
(güncelleme (kopya sayısını (asıl iş yükü)
stratejisi) korur)
Deployment'ın kendi katkısı güncelleme yönetimidir: yeni bir imaj sürümü verdiğinde yeni bir ReplicaSet oluşturur, yavaş yavaş yeni Pod'ları ayağa kaldırır, eskileri kapatır ve bir sorun çıkarsa geri alabilmen için eski ReplicaSet'i sıfır kopyayla saklar.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web # BU alan, aşağıdaki template etiketleriyle eşleşmeli
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # geçici olarak 1 fazla Pod açılabilir
maxUnavailable: 0 # hiçbir an kapasitenin altına düşme
template:
metadata:
labels:
app: web # selector burayı arıyor
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
maxUnavailable: 0 ile maxSurge: 1 birleşimi, kesintisiz güncelleme isteyen servisler için sağlam bir varsayılandır: önce yeni Pod hazır olur, sonra eski kapanır. readinessProbe olmadan bu garanti çalışmaz — Kubernetes, hazır olmayan bir Pod'u hazır sayar ve trafiği henüz açılmamış bir uygulamaya yönlendirir.
Günlük kullandığın komutlar:
kubectl apply -f web-deployment.yaml
kubectl get deploy web
# NAME READY UP-TO-DATE AVAILABLE AGE
# web 3/3 3 3 2m
# İmajı güncelle ve dağıtımı izle
kubectl set image deployment/web nginx=nginx:1.27.1-alpine
kubectl rollout status deployment/web
# Sorun çıktıysa bir önceki sürüme dön
kubectl rollout undo deployment/web
kubectl rollout history deployment/web
# Kopya sayısını değiştir
kubectl scale deployment/web --replicas=5
Burada bir uyarı: spec.selector alanı oluşturduktan sonra değiştirilemez. Sonradan etiket şemasını değiştirmen gerekirse Deployment'ı silip yeniden oluşturman gerekir. Bu yüzden etiketleri baştan düşün: app, component, environment gibi tutarlı bir şema, altı ay sonra hayatını kolaylaştırır.
Service: Değişen IP'lere Sabit Bir Adres#
Üç Pod'un var ve her biri kendi IP'sine sahip. Pod'lardan biri öldüğünde yerine gelen yeni Pod farklı bir IP alıyor. O hâlde uygulamanın ön yüzü arka uca nasıl bağlanacak? Cevap Service: etiket seçicisine uyan Pod'ları bulan, sabit bir sanal IP ve sabit bir DNS adı sunan nesne.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web # bu etikete sahip Pod'lara trafik dağıtır
ports:
- name: http
port: 80 # Service'in dinlediği port
targetPort: 80 # Pod içindeki konteyner portu
Bu Service oluştuğunda küme içinde web adıyla erişilebilir hâle gelir. Tam DNS adı web.<namespace>.svc.cluster.local biçimindedir; aynı ad alanındaki bir Pod'dan yalnızca http://web yazman yeterlidir.
Üç portu birbirine karıştırmamak önemli:
| Alan | Kim dinler | Örnek |
|---|---|---|
port | Service'in sanal IP'si | 80 |
targetPort | Pod içindeki konteyner | 8080 |
nodePort | Her düğümün kendi IP'si | 30080 |
Service'in gerçekten Pod bulup bulmadığını anlamanın en hızlı yolu uç noktalara bakmaktır:
kubectl get endpoints web
# NAME ENDPOINTS AGE
# web 10.244.1.7:80,10.244.2.4:80,10.244.2.5:80 3m
Bu liste boşsa Service hiçbir Pod'a bağlanmıyor demektir ve neredeyse her zaman iki sebebi vardır: seçici etiket yanlış yazılmıştır ya da Pod'lar readinessProbe yüzünden hazır sayılmamıştır. Hazır olmayan Pod uç nokta listesine hiç girmez — bu bir arıza değil, tasarımdır.
Service Tipleri ve Hangisini Ne Zaman Kullanacağın#
| Tip | Nereden erişilir | Tipik kullanım |
|---|---|---|
ClusterIP (varsayılan) | Yalnızca küme içinden | Servisler arası iletişim |
NodePort | Düğüm IP'si + 30000-32767 arası port | Geliştirme, hızlı test |
LoadBalancer | Sağlayıcının verdiği genel IP | Bulut ortamında dışa açık servis |
ExternalName | DNS diğer adı olarak | Küme dışındaki bir servise takma ad |
Headless (clusterIP: None) | Doğrudan Pod IP'leri | StatefulSet, kendi keşif mantığı olan uygulamalar |
Pratikte kural şu: küme içi trafik için ClusterIP kullan, dışa açılacak HTTP/HTTPS servisleri için ise her servise ayrı bir LoadBalancer almak yerine tek bir Ingress katmanı kur. Onlarca servis için onlarca genel IP hem pahalıdır hem de sertifika yönetimini dağıtır. Ingress kurulumunu Kubernetes Ingress Controller kurulumu yazısında adım adım anlattım.
NodePort cazip görünür ama üretim için uygun değildir: port aralığı kısıtlıdır, akılda kalmayan port numaraları üretir ve düğüm IP'sini istemciye açar. Hızlı bir doğrulama için ise gayet kullanışlıdır. Bir servisi kalıcı olarak açmadan test etmek istersen en temiz yöntem port yönlendirmedir:
kubectl port-forward svc/web 8080:80
# Tarayıcıdan http://localhost:8080
Uçtan Uca Örnek ve Doğrulama#
Üç nesneyi tek dosyada birleştirip çalışan bir örnek kuralım. Yapılandırma değerlerini imaja gömmek yerine ConfigMap'ten okumak iyi bir alışkanlıktır; ayrıntısı ConfigMap ve Secret kullanımı yazısında.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: firmaniz/api:1.4
ports:
- containerPort: 8080
env:
- name: TZ
value: Europe/Istanbul
readinessProbe:
httpGet: { path: /saglik, port: 8080 }
initialDelaySeconds: 5
livenessProbe:
httpGet: { path: /saglik, port: 8080 }
initialDelaySeconds: 20
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
Uygulayıp doğrulama sırası:
kubectl apply -f api.yaml
# 1. Pod'lar ayakta mı, kaç kez yeniden başladı
kubectl get pods -l app=api -o wide
# 2. Bir Pod sorunluysa olayları oku (asıl sebep genelde burada)
kubectl describe pod api-7c9d5b8f4-xk2mn | tail -n 20
# 3. Uygulama logu
kubectl logs -l app=api --tail=50
# 4. Service gerçekten Pod buluyor mu
kubectl get endpoints api
# 5. Küme içinden erişimi test et
kubectl run test --rm -it --image=busybox --restart=Never -- \
wget -qO- http://api/saglik
Bu beş adım, Kubernetes'te karşılaştığın sorunların büyük çoğunluğunu teşhis eder. describe çıktısının en altındaki olay listesi özellikle değerlidir; ImagePullBackOff, Insufficient cpu ve readiness probe failed gibi mesajlar orada açık açık yazar.
Sık Yapılan Hatalar ve Tuzaklar#
- Seçici ile şablon etiketlerinin uyuşmaması.
selector.matchLabelsiletemplate.metadata.labelsbirebir aynı olmalıdır. Uyuşmazsa Deployment Pod oluşturur ama sahiplenemez, ya da hiç oluşturmaz. readinessProbeyazmamak. Yoksa Kubernetes konteyner başlar başlamaz trafiği yönlendirir; henüz veritabanına bağlanmamış bir uygulama 502 döndürür ve güncelleme sırasında kısa kesintiler yaşarsın.livenessProbeeşiğini çok agresif ayarlamak. Yavaş açılan bir uygulamadainitialDelaySecondsdüşükse Kubernetes uygulamayı sürekli öldürür veCrashLoopBackOffgörürsün. Sorun uygulamada değil, sondadır.- Kaynak istek ve sınırlarını hiç tanımlamamak.
requestsyoksa zamanlayıcı düğümü doğru dolduramaz;limitsyoksa tek bir Pod düğümün tüm belleğini yiyip komşularını düşürebilir. latestetiketiyle imaj kullanmak.kubectl set imageile aynı etiketi verdiğinde Kubernetes hiçbir değişiklik görmez ve güncelleme başlamaz. Her sürüme değişmez bir etiket ver.- Pod'u doğrudan
kubectl runile oluşturup üretimde bırakmak. Böyle bir Pod'u kimse denetlemez; düğüm çökünce uygulama tamamen kaybolur. Üretimde her iş yükü bir Deployment (veya StatefulSet / DaemonSet) altında olmalıdır. - Durum tutan uygulamayı Deployment ile çalıştırmak. Veritabanı gibi kalıcı diske ve sabit kimliğe ihtiyaç duyan uygulamalarda StatefulSet ve kalıcı hacim gerekir; ayrıntısı Kubernetes Persistent Volume yönetimi yazısında.
Sıkça Sorulan Sorular#
Pod ile konteyner arasındaki fark nedir#
Konteyner çalışan bir uygulama örneğidir; Pod ise bir veya birden fazla konteyneri saran, Kubernetes'in zamanlayabildiği en küçük birimdir. Aynı Pod'daki konteynerler aynı IP adresini ve hacimleri paylaşır, birbirlerine localhost üzerinden ulaşır ve birlikte yaşayıp birlikte ölürler. Kubernetes kaynak ayırmayı, zamanlamayı ve ölçeklemeyi konteyner düzeyinde değil Pod düzeyinde yapar.
Deployment yerine doğrudan Pod oluşturabilir miyim#
Teknik olarak oluşturabilirsin ama üretimde yapmamalısın. Tek başına oluşturulan bir Pod'u hiçbir denetleyici izlemez: düğüm çökerse, Pod tahliye edilirse veya konteyner ölürse yerine yenisi gelmez. Deployment ise istediğin kopya sayısını sürekli korur, güncelleme sırasında kademeli geçiş yapar ve geri alma imkânı sunar.
Service olmadan Pod'lara erişebilir miyim#
Pod'ların kendi IP'leri vardır ve küme içinden doğrudan erişilebilir, ancak bu IP'ler her yeniden oluşturmada değişir. Bu yüzden bir Pod IP'sini uygulama yapılandırmasına yazmak sürdürülebilir değildir. Service, seçiciye uyan Pod'ları takip ederek sabit bir isim ve sanal IP sunar; yük dağıtımını da kendisi yapar.
ClusterIP ile NodePort arasındaki fark nedir#
ClusterIP yalnızca küme içinden erişilebilen bir sanal IP verir ve servisler arası iletişim için varsayılan tiptir. NodePort ise her düğümün IP adresi üzerinde 30000-32767 aralığından bir port açar ve servisi küme dışına da çıkarır. NodePort hızlı testler için pratiktir; üretimde dışa açılan HTTP servisleri için Ingress ya da bulut yük dengeleyici tercih edilir.
Pod'um CrashLoopBackOff durumunda, nereden başlamalıyım#
Önce kubectl logs <pod> --previous ile ölen konteynerin son loglarını oku; sebep çoğunlukla oradadır. Sonra kubectl describe pod <pod> çıktısının olaylar bölümüne bak. Yaygın sebepler şunlardır: eksik ortam değişkeni veya yapılandırma, ulaşılamayan veritabanı, çok agresif ayarlanmış livenessProbe ve bellek sınırına takılıp öldürülme (OOMKilled).
Kaç kopya (replica) çalıştırmalıyım#
Kesintisiz güncelleme yapabilmek için en az iki kopya gerekir; tek kopyada güncelleme sırasında kaçınılmaz olarak kısa bir boşluk oluşur. Bunun ötesinde kopya sayısı trafiğe ve tek bir kopyanın kapasitesine bağlıdır. Yük değişkense sabit bir sayı yerine yatay otomatik ölçekleyici kullanıp alt ve üst sınır belirlemek daha verimlidir.
Kapanış#
Kubernetes'in üç temel nesnesi aslında üç ayrı soruyu cevaplıyor: Pod "bu konteyner nerede ve neyle birlikte çalışıyor", Deployment "kaç kopya olacak ve nasıl güncellenecek", Service "bu kopyalara hangi sabit adresten ulaşılacak". Aklında tutman gereken dört alışkanlık şunlar: etiket şemasını baştan tutarlı kur, her iş yüküne readinessProbe ve kaynak isteği tanımla, imaj etiketlerini değişmez tut ve bir sorunda önce describe çıktısının olay listesine bak.
Kubernetes'i çalıştıracak altyapıyı ararken Clou.TR tarafında birkaç seçenek bulabilirsin. Küme düğümleri için tam root erişimli VDS ve esnek ölçeklenebilen bulut sunucu paketlerimize bakabilir, kurulum ve işletme yükünü devretmek istersen sunucu yönetimi hizmetimizi değerlendirebilir, dışa açtığın servislerin önüne koruma katmanı koymak için DDoS koruma çözümümüzü inceleyebilirsin.