Docker & DevOps

    Kubernetes Pod, Deployment ve Service Kavramları

    Kubernetes'in üç temel nesnesinin birbiriyle ilişkisi ve çalışan bir örnek üzerinden anlatımı.

    10 dk okuma Güncellendi: 25 Ağustos 2026

    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:

    AlanKim dinlerÖrnek
    portService'in sanal IP'si80
    targetPortPod içindeki konteyner8080
    nodePortHer düğümün kendi IP'si30080

    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#

    TipNereden erişilirTipik kullanım
    ClusterIP (varsayılan)Yalnızca küme içindenServisler arası iletişim
    NodePortDüğüm IP'si + 30000-32767 arası portGeliştirme, hızlı test
    LoadBalancerSağlayıcının verdiği genel IPBulut ortamında dışa açık servis
    ExternalNameDNS diğer adı olarakKüme dışındaki bir servise takma ad
    Headless (clusterIP: None)Doğrudan Pod IP'leriStatefulSet, 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#

    1. Seçici ile şablon etiketlerinin uyuşmaması. selector.matchLabels ile template.metadata.labels birebir aynı olmalıdır. Uyuşmazsa Deployment Pod oluşturur ama sahiplenemez, ya da hiç oluşturmaz.
    2. readinessProbe yazmamak. 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.
    3. livenessProbe eşiğini çok agresif ayarlamak. Yavaş açılan bir uygulamada initialDelaySeconds düşükse Kubernetes uygulamayı sürekli öldürür ve CrashLoopBackOff görürsün. Sorun uygulamada değil, sondadır.
    4. Kaynak istek ve sınırlarını hiç tanımlamamak. requests yoksa zamanlayıcı düğümü doğru dolduramaz; limits yoksa tek bir Pod düğümün tüm belleğini yiyip komşularını düşürebilir.
    5. latest etiketiyle imaj kullanmak. kubectl set image ile 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.
    6. Pod'u doğrudan kubectl run ile 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.
    7. 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.

    KubernetesDeploymentService

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.