Konteynerden Orkestrasyona: Kubernetes Ne Zaman Gerekir?
- Murat Akpınar
- Kubernetes öğrenme yolculuğu
- 13 Ağustos 2026
İçindekiler
Yol haritası yazısında seriyi dört evreye ayırmıştık. Birinci evrenin birinci sorusu şuydu: konteyner tam olarak nedir, ve neden bir noktadan sonra tek başına yetmiyor?
Kubernetes’i anlatan çoğu metin işi yanlış uçtan tutuyor: önce Kubernetes’in ne kadar yetenekli olduğunu sıralıyor, konteyneri bir dipnot gibi geçiyor. Oysa sıra tersidir. Kubernetes bir konteyner çalıştırma aracı değildir — konteyner çalıştırmak zaten tek satır. Kubernetes, o tek satırın cevaplamadığı soruya verilmiş bir cevaptır: o konteyneri kim ayakta tutacak?
Bu yazıda önce konteynerin sanal makineden nerede ayrıldığını komut çıktısıyla kanıtlayacağız. Sonra tek sunucuda Docker’ın tam olarak nerede duvara tosladığını, orkestrasyonun bu duvarı hangi fikirle aştığını ve — en az onun kadar belirleyici olan — Kubernetes’in size gereksiz olduğu durumları ele alacağız.
Aşağıdaki çıktılar
argo-lab01adlı kurgusal bir Debian 12 makinesinden alınmış örnek değerlerdir; PID’ler, kimlikler ve sürüm numaraları sizin sisteminizde farklı olacaktır. Komutlar ve davranışlar gerçektir.
Yazı boyunca geçen terimlerin kısa karşılıkları için sondaki terimler sözlüğüne bakabilirsiniz.
Konteyner Bir Sanal Makine Değildir: Aynı Çekirdek, Ayrı Görüş
İki teknolojiyi ayıran tek bir cümle var, gerisi bunun sonucudur:
Sanal makinenin kendi çekirdeği vardır; konteynerin yoktur.
Sanal makine, hypervisor’ın donanımı taklit etmesiyle çalışır. İçine kurduğunuz Debian kendi çekirdeğini yükler, kendi init sürecini başlatır, kendi disk ve ağ sürücülerini kullanır. Host çekirdeğiyle tek satır kod paylaşmaz. Sanallaştırma yazısında anlatılan model budur.
Konteynerde bu katman yoktur. Konteyner, host çekirdeğinin üzerinde çalışan sıradan bir Linux sürecidir — tek farkı, çekirdeğin ona sistemin geri kalanını göstermemesidir.
flowchart TB
subgraph SM["Sanal makine"]
direction TB
SMD["Donanım"] --> SMH["Host çekirdeği + hypervisor"]
SMH --> SMV1["Konuk çekirdek<br/>init + kütüphaneler"]
SMH --> SMV2["Konuk çekirdek<br/>init + kütüphaneler"]
SMV1 --> SMA1["Uygulama A"]
SMV2 --> SMA2["Uygulama B"]
end
subgraph KN["Konteyner"]
direction TB
KND["Donanım"] --> KNK["Tek host çekirdeği<br/>hepsi bunu paylaşır"]
KNK --> KNR["containerd + runc"]
KNR --> KNA1["Uygulama A<br/>+ kütüphaneleri"]
KNR --> KNA2["Uygulama B<br/>+ kütüphaneleri"]
end
SM ~~~ KN
classDef don fill:#f1f5f9,stroke:#64748b,color:#0f172a
classDef cek fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef ara fill:#fef3c7,stroke:#d97706,color:#78350f
classDef uyg fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
class SMD,KND don
class SMH,SMV1,SMV2,KNK cek
class KNR ara
class SMA1,SMA2,KNA1,KNA2 uyg
Şemada kırmızı kutuları sayın: sanal makine tarafında üç tane var — host çekirdeği ve iki konuk çekirdek. Konteyner tarafında bir tane. Bu yazının kalanı, o farkın faturasını okumaktan ibaret.
Kanıt: Konteynerin İçinden Çekirdeği Sorun
Konuşmak yerine bakalım. argo-lab01 bir Debian 12 makinesi:
root@argo-lab01:~# uname -r
6.1.0-13-amd64
Şimdi üstünde bir Alpine konteyneri çalıştırıp aynı soruyu içeriden soralım:
root@argo-lab01:~# docker run --rm alpine cat /etc/os-release | head -2
NAME="Alpine Linux"
ID=alpine
root@argo-lab01:~# docker run --rm alpine uname -r
6.1.0-13-amd64
İki çıktı birlikte okunmalı. Dağıtım Alpine: farklı paket yöneticisi, farklı libc, farklı dosya düzeni. Çekirdek Debian’ın çekirdeği: aynı sürüm numarası, çünkü zaten aynı çekirdek. Konteyner “içine Alpine kurulmuş küçük bir makine” değil, Alpine’ın kullanıcı alanı dosyalarıyla açılmış bir süreçtir.
Bunun pratikte iki keskin sonucu var:
- Linux çekirdeği yalnızca Linux konteyneri çalıştırır. Windows konteynerleri Windows çekirdeği ister. Docker Desktop’ın Windows’ta Linux konteyneri çalıştırabilmesinin sebebi bir sihir değil, arkada dönen bir Linux sanal makinesidir (WSL2).
- Çekirdeği yükseltmek host’u yeniden başlatmak demektir, bu da o host üzerindeki tüm konteynerleri durdurur. Sanal makinede bir konuk çekirdeği yükseltmek yalnızca o VM’i etkiler.
İzolasyonu Kuran İki Mekanizma: namespace ve cgroup
“Çekirdek ona sistemin geri kalanını göstermez” cümlesinin arkasında iki ayrı çekirdek özelliği var ve ikisi farklı işler yapar.
namespace = ne görüyor. Süreç için ayrı bir görüş alanı açar: kendi süreç ağacı, kendi ağ arayüzleri, kendi bağlama noktaları, kendi hostname’i.
cgroup = ne kadar tüketebiliyor. Görüşü değil kaynağı sınırlar: CPU, bellek, disk G/Ç.
Bir konteyner başlatıp ikisini de yerinde görelim:
root@argo-lab01:~# docker run -d --name web -p 8080:80 --memory=256m --cpus=0.5 nginx:1.27-alpine
3f1a9c7d2b84e5f0a1c6d39b7e24f8a05c1b6d93e7f204a8b1c5d6e9f30a7b21
Konteynerin içinden süreç listesi:
root@argo-lab01:~# docker exec web ps
PID USER TIME COMMAND
1 root 0:00 nginx: master process nginx -g daemon off;
30 nginx 0:00 nginx: worker process
31 nginx 0:00 nginx: worker process
38 root 0:00 ps
Konteyner kendini tek başına bir sistem sanıyor: nginx PID 1, yani init. Şimdi host’tan aynı sürece bakalım:
root@argo-lab01:~# ps -eo pid,ppid,user,args | grep "[n]ginx: master"
21584 21562 root nginx: master process nginx -g daemon off;
Aynı süreç, iki farklı numara. İçeride 1, dışarıda 21584. PID namespace’in yaptığı iş tam olarak budur: numaralandırmayı yeniden başlatır. Süreç saklanmış değil — host onu görüyor, kill edebilir, strace edebilir. Bir sanal makinenin içindeki süreci host’un ps çıktısında bulamazsınız; buradaysa buluyoruz.
Ebeveyn süreç 21562 de öğretici:
root@argo-lab01:~# ps -o pid,args -p 21562
PID COMMAND
21562 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 3f1a9c7d2b84e5f0a1c6d39b7e24f8a05c1b6d93e7f204a8b1c5d6e9f30a7b21 -address /run/containerd/containerd.sock
nginx’in ebeveyni dockerd değil, containerd-shim. Docker komut satırı sizin muhatabınız; konteyneri fiilen çalıştıran alt katman containerd ve runc. Bu ayrım ileride önem kazanacak: Kubernetes 1.24 ile dockershim kaldırıldığında kubelet’in Docker Engine’e olan bağı koptu, ama konteynerler aynı containerd üzerinde çalışmaya devam etti. Bu konuya yol haritasının 2. başlığında döneceğiz.
Namespace’lerin kendisi /proc altında görülebilir:
root@argo-lab01:~# ls -l /proc/21584/ns/
lrwxrwxrwx 1 root root 0 Aug 13 10:06 cgroup -> 'cgroup:[4026532633]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 ipc -> 'ipc:[4026532571]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 mnt -> 'mnt:[4026532569]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 net -> 'net:[4026532574]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 pid -> 'pid:[4026532572]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 uts -> 'uts:[4026532570]'
Köşeli parantez içindekiler namespace’lerin inode numaralarıdır; aynı numara aynı namespace demektir. Altı tanesi 4026532… ile başlıyor — bunlar konteyner için yeni açılmış. İki tanesi (time ve user) 4026531… ile başlıyor. Bunları host’un PID 1‘iyle karşılaştıralım:
root@argo-lab01:~# ls -l /proc/1/ns/user /proc/21584/ns/user
lrwxrwxrwx 1 root root 0 Aug 13 10:06 /proc/1/ns/user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Aug 13 10:06 /proc/21584/ns/user -> 'user:[4026531837]'
Numaralar birebir aynı. Yani konteyner varsayılan olarak kendi user namespace’ini almaz: konteynerin içindeki root, host’un root‘udur. Bir sonraki bölümün tamamı bu satırın sonucu.
Kaynak tarafına, yani cgroup’a bakalım. Konteyneri --memory=256m --cpus=0.5 ile başlatmıştık:
root@argo-lab01:~# docker exec web cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max
268435456
50000 100000
268435456 bayt = 256 MiB. 50000 100000 ise “her 100 ms’lik dilimde en fazla 50 ms CPU”, yani yarım çekirdek. Sınırı uygulayan Docker değil, çekirdektir; Docker yalnızca değerleri yazar.
Ön koşul: Bu yollar cgroup v2 içindir ve konteynerin kendi cgroup namespace’ini görmesini gerektirir (Docker 20.10+ ile cgroup v2 üzerinde varsayılan). Debian 12 ve güncel dağıtımlar cgroup v2 kullanır. Hâlâ cgroup v1’deyseniz karşılığı
/sys/fs/cgroup/memory/memory.limit_in_bytesolur;stat -fc %T /sys/fs/cgroupkomutucgroup2fsderse v2’desiniz.
Farkın Faturası: Neyi Kazanıp Neyi Kaybediyorsunuz
| Sanal makine | Konteyner | |
|---|---|---|
| Çekirdek | Kendi konuk çekirdeği | Host çekirdeği, paylaşımlı |
| Başlangıç süresi | Onlarca saniye: firmware, çekirdek, init, servisler | Saniyenin altında: tek süreç başlatma |
| Disk maliyeti | Minimal bir Debian imajı ~1,5 GB | nginx:1.27-alpine 52,5 MB |
| İşletim sistemi çeşitliliği | Linux host üzerinde Windows çalışır | Linux çekirdeği yalnızca Linux konteyneri çalıştırır |
| İzolasyon sınırı | Hypervisor — donanım destekli | Çekirdek — namespace ve cgroup |
| Çekirdek yükseltmesi | VM’i yeniden başlatır, host etkilenmez | Host’u yeniden başlatır, üstündeki tüm konteynerler durur |
İlk üç satır konteyneri cazip yapan şey. Dördüncü satır kısıt. Beşinci satır ise en çok hafife alınan satırdır.
Paylaşılan Çekirdek Bir Güvenlik Sınırı Değildir
Sanal makinede iki VM arasındaki duvarı hypervisor ve işlemcinin sanallaştırma desteği tutar. Konteynerde o duvarı çekirdeğin kendisi tutar — ve çekirdek her iki tarafta da aynı çekirdektir. Çekirdekte sömürülebilir bir açık varsa, konteynerden çıkış yolu vardır.
Ama pratikte insanların başını çekirdek açıkları yakmıyor. Basit bir bayrak yakıyor:
root@argo-lab01:~# docker run --rm -v /:/host alpine ls -l /host/etc/shadow
-rw-r----- 1 root shadow 1042 Aug 12 22:31 /host/etc/shadow
Bir önceki bölümde user namespace’in paylaşıldığını görmüştük; sonucu bu. Konteyner içindeki root host’un root‘u olduğu için, host kök dizinini bağlayan konteyner host’un parola dosyasını okuyabildi. Aynı yöntemle /host/etc/sudoers dosyasına yazabilir, /host/root/.ssh/authorized_keys dosyasına anahtar ekleyebilirdi.
Bu bir açık değil, tasarımın kendisidir — ve şu sonuca varır: docker grubuna üyelik, pratikte parolasız root yetkisidir. Gruplar yazısında bu grubu sudo ile aynı ağırlıkta bir karar olarak işaretlemiştik; kanıtı yukarıdaki üç satır.
Docker’ın bu duruma iki resmî cevabı var ve ikisi de varsayılan değil, açıkça seçilmesi gerekiyor. Rootless mode daemon’ı sıradan bir kullanıcıyla çalıştırır; konteynerdeki root host’ta o kullanıcıya karşılık gelir. Daha eski userns-remap seçeneği ise daemon’ı root bırakıp yalnızca konteynerlerin UID aralığını kaydırır. İkisi de bazı yeteneklerden feragat eder (rootless modda ayrıcalıklı port yayımlamak ve bazı ağ/depolama sürücüleri kısıtlıdır), bu yüzden üretimde açmadan önce uygulamanızın neye ihtiyacı olduğunu bilin.
Çekirdeği hiç paylaşmamak isteyenler için ise ara çözümler var: gVisor kullanıcı alanında bir çekirdek katmanı koyar, Kata Containers her konteyneri hafif bir sanal makineye alır. İkisinin de bedeli performans ve işletme karmaşıklığıdır; çok kiracılı ortamlar dışında karşılığı çıkmaz.
Docker Tek Sunucuda Nereye Kadar Yeter?
Şimdi hikâyenin ikinci yarısına geçelim. argo-lab01 üzerinde üç konteynerli bir uygulama çalışıyor: web, api, db. docker compose up -d diyorsunuz, çalışıyor. Buraya kadar Kubernetes’e ihtiyaç yok — ve bu cümleyi bu yazının en sonunda tekrar edeceğiz.
Sorun, “çalışıyor"un ne kadar sürdüğünde başlıyor.
restart: always Ölen Süreci Kaldırır, Asılı Kalanı Kaldırmaz
Docker’ın yeniden başlatma politikasını herkes bilir:
services:
api:
image: ornek/api:1.4.2
restart: always
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 10s
timeout: 2s
retries: 3
Bir sabah api isteklere cevap vermiyor. Veritabanı bağlantı havuzu tükenmiş, süreç kilitlenmiş. Ama ölmemiş: hâlâ 8080 portunu tutuyor, hâlâ bellekte. Docker ne diyor?
root@argo-lab01:~# docker ps --format 'table {{.Names}}\t{{.Status}}'
NAMES STATUS
api Up 22 minutes (unhealthy)
db Up 22 minutes
web Up 22 minutes
root@argo-lab01:~# docker inspect -f '{{.RestartCount}}' api
0
Bu iki çıktı birlikte kesin bir şey söylüyor. RestartCount sıfır — yani restart: always hiç devreye girmedi. Girmesi de gerekmezdi: yeniden başlatma politikası sürecin çıkışını izler, süreç çıkmadı. Uygulama kilitlendiğinde çıkış kodu üretmez.
Peki healthcheck ne oldu? Görevini yaptı: durumu unhealthy olarak işaretledi. Docker Engine bu bilgiyle hiçbir şey yapmaz. Sağlık durumuna bakıp konteyneri yeniden başlatan katman Swarm’dır; düz docker run ya da docker compose değil.
Çıktının kanıt değeri de buradan geliyor: süreç çökmüş olsaydı RestartCount sıfırdan büyük olurdu ve STATUS sütununda “Up 22 minutes” değil “Up 8 seconds” yazardı. İki senaryo aynı görünmüyor; bu yüzden yukarıdaki çıktı bir şey kanıtlıyor.
Kubernetes’in bu boşluğa verdiği cevabın adı liveness probe‘dur:
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
failureThreshold: 3
Fark tek kelimede: Kubernetes’te sağlık kontrolünün bir eylemi vardır. Üç kez üst üste cevap alınamazsa kubelet konteyneri öldürür ve yeniden başlatır; sayaç da bunu yazar:
NAME READY STATUS RESTARTS AGE
api-6d4b7c8f9d-2xk4z 1/1 Running 1 (34s ago) 22m
RESTARTS sütunundaki 1, Docker tarafındaki RestartCount: 0‘ın tam karşılığıdır. Aynı arıza, iki farklı sonuç.
Sunucu Komple Ölünce Docker’ın Elinden Bir Şey Gelmez
Yeniden başlatma politikası dockerd ayakta olduğu sürece çalışır. Anakart arızalanır, güç kesilir ya da çekirdek panik verirse dockerd de gitmiştir. Konteynerler o sunucuya çivilenmiştir; onları başka bir yere taşıyacak bir mekanizma yoktur, çünkü Docker’ın “başka bir yer” diye bir kavramı yoktur.
Elle çözmeye kalkarsanız, çözmeniz gereken beş ayrı problem çıkar:
- Yerleştirme. Yeni konteyner hangi sunucuda başlatılacak? Hangisinde yeterli boş bellek var?
- Ayakta tutma. Bir sunucu düştüğünde üstündekileri kim, ne zaman fark edip nereye taşıyacak?
- Adres. Konteyner başka sunucuda yeniden doğduğunda IP’si değişir. Ona istek gönderen servis yeni adresi nereden öğrenecek?
- Güncelleme. Yeni sürüme kesintisiz nasıl geçilecek, hata çıkarsa eskiye nasıl dönülecek?
- Yapılandırma. Veritabanı parolasını on iki sunucuya elle mi dağıtacaksınız, değiştiğinde hepsini tek tek mi güncelleyeceksiniz?
Bu beş maddeyi kendi betiklerinizle çözmeye başladığınız an, farkında olmadan bir orkestrasyon aracı yazmaya başlamış olursunuz. Kubernetes’in var oluş sebebi tam olarak bu listedir.
Orkestrasyonun Fikri: Emir Değil, İstenen Durum
Kubernetes’i “konteynerleri yöneten araç” diye tarif etmek eksik kalır, çünkü asıl yenilik yönetmek değil, yönetme biçimidir.
Docker’da emir verirsiniz: bu konteyneri başlat. Emir anlıktır; sunucu ölünce emri veren de yok olur.
Kubernetes’te durum bildirirsiniz: bu uygulamadan her zaman 3 kopya çalışsın. Bu cümle bir emir değil, kalıcı bir kayıttır. Kümenin içinde çalışan denetleyiciler (controller) sürekli olarak iki şeyi karşılaştırır: ne istendi, ne gözleniyor. Fark varsa kapatırlar.
flowchart LR
I["İstenen durum<br/>replicas: 3"] --> D{"Denetleyici<br/>farkı hesaplar"}
G["Gözlenen durum<br/>çalışan: 2"] --> D
D -->|"fark: 1"| E["Eylem<br/>1 pod oluştur"]
E --> G
D -->|"fark: 0"| B["Bir şey yapma"]
classDef ist fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef goz fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef den fill:#fef3c7,stroke:#d97706,color:#78350f
classDef eyl fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
class I ist
class G goz
class D den
class E eyl
class B goz
Bu döngü hiç durmaz. Bir sunucu öldüğünde kimse “taşı” komutu vermez; gözlenen kopya sayısı 3’ten 2’ye düştüğü için döngü kendiliğinden bir kopya daha oluşturur. Aynı döngü, bir pod’u siz elle sildiğinizde de çalışır — bu yüzden Deployment’ın pod’unu kubectl delete pod ile silmek işe yaramaz, saniyeler içinde yenisi gelir.
Önceki bölümdeki beş problemin karşılıkları da bu modelin üstünde durur:
| Tek sunucuda elle yaptığınız iş | Kubernetes’teki karşılığı | Seride nerede |
|---|---|---|
| Hangi sunucuda çalışacağına karar vermek | scheduler | 2.1 ve 9.3 |
| Ölen konteyneri kaldırmak | kubelet + ReplicaSet | 5.1 |
| Kaç kopya çalışacağını sabitlemek | Deployment replicas |
5.1 |
| Değişmeyen bir adres vermek | Service ve CoreDNS | 6.2 ve 6.3 |
| Kesintisiz güncelleme ve geri dönüş | Deployment rolling update | 5.2 |
| Parola ve ayar dağıtmak | ConfigMap ve Secret | 8.1 ve 8.2 |
| Asılı kalan süreci fark etmek | livenessProbe | 5.5 |
Bu tablodaki her satır yol haritasının bir başlığına denk geliyor; sağ sütun hangi yazıda ele alınacağını gösteriyor.
Kubernetes de Sihir Değil: Ölü Düğümün Pod’ları Ne Zaman Taşınır?
Uzlaşma döngüsünü anlattıktan sonra en sık düşülen yanılgıyı da hemen düzeltelim: Kubernetes bir düğüm kaybını saniyeler içinde değil, dakikalar içinde toparlar. Zincir şöyle işler:
| Adım | Kim yapar | Varsayılan süre |
|---|---|---|
| Düğüm durumunu bildirmek | kubelet → apiserver | her 10 saniyede bir |
Sessiz kalan düğümü NotReady işaretlemek |
kube-controller-manager | --node-monitor-grace-period |
| Pod’ları tahliye etmek | taint tabanlı tahliye | tolerationSeconds, varsayılan 300 saniye |
İkinci satırın varsayılanı uzun süredir 40 saniye civarındadır, ama bu değer sürümden sürüme değişebiliyor; sayıyı ezberlemek yerine kendi kümenizde kube-controller-manager bayraklarından okuyun. Üçüncü satırın 300 saniyesi ise apiserver’ın --default-not-ready-toleration-seconds ve --default-unreachable-toleration-seconds bayraklarından gelir. Toplam: bir düğüm elektriğini kaybettiğinde pod’ları beş dakikadan önce başka yere taşınmaz.
Bu bilinçli bir tercihtir. Kısa bir ağ kesintisiyle gerçek bir sunucu ölümünü uzaktan ayırt etmek mümkün değildir; eşik kısaltılırsa küme her ağ hıçkırığında sağlıklı pod’ları katletmeye başlar.
Pratik sonucu şudur: kesintisizlik, tek kopyanın hızlı taşınmasından değil, kopyaların baştan farklı düğümlerde durmasından gelir. Üç kopyayı üç ayrı düğüme dağıtırsanız bir düğüm öldüğünde kalan ikisi hizmeti sürdürür; taşıma işlemi arka planda, kimse fark etmeden tamamlanır. Bunu sağlayan topologySpreadConstraints ve anti-affinity kuralları serinin 9.3 başlığında.
Bir uç durum daha var: kalıcı diski olan StatefulSet pod’ları ulaşılamayan bir düğümde Terminating durumunda takılı kalır ve otomatik olarak yeniden oluşturulmaz. Sebebi mantıklı — aynı diski iki düğümün birden bağlaması veri kaybı demektir ve Kubernetes düğümün gerçekten öldüğünü uzaktan bilemez. Kubernetes 1.28’de kararlı hâle gelen node.kubernetes.io/out-of-service taint’i, “bu düğüm gerçekten öldü, kilidi aç” demenin resmî yoludur.
Kubernetes’e Gerçekten İhtiyacınız Var mı?
Buraya kadar Kubernetes’in hangi problemi çözdüğünü gördük. Şimdi asıl soru: o problem sizde var mı?
Basitten Karmaşığa Merdiven
Altyapı kararları merdiven gibi düşünülmeli. Bir üst basamağa yalnızca alt basamak somut bir sorunu çözemediğinde çıkın — merak ettiğiniz için değil.
- systemd servisi. Tek uygulama, tek sunucu.
Restart=alwaysvejournalctlçoğu iç aracın ihtiyacını karşılar. docker compose+restart: unless-stopped. Birkaç servis, hâlâ tek sunucu. Bağımlılıkları imaja gömer, kurulumu tekrarlanabilir kılar.- İki sunucu + önünde bir yük dengeleyici. İlk gerçek kesinti toleransı. Kubernetes’in en çok reklamı yapılan faydasının büyük kısmı bu basamakta, çok daha ucuza elde edilir.
- Yönetilen bir PaaS. Ekibiniz altyapı işletmek istemiyorsa dürüst cevap budur.
- k3s veya tek düğümlü Kubernetes. Öğrenmek için, ya da küçük ekiplerin Kubernetes ekosisteminden (Helm chart’ları, operator’ler) yararlanması için.
- Tam Kubernetes. Üç control plane düğümü ve n worker, ya da yönetilen bir servis.
Kubernetes’in Kendisi de Bir İş Yüküdür
Kubernetes’in maliyeti kurulum değil, işletmedir. Kümeyi kurmak bir öğleden sonra sürer; ayakta tutmak süreklidir:
- Yüksek erişilebilirlik üç control plane düğümü ister. etcd çoğunluk (quorum) ile çalışır ve çoğunluk
(n/2)+1‘dir: 3 düğüm bir kaybı tolere eder, 2 düğüm hiçbirini. Yani ikinci düğüm eklemek erişilebilirliği artırmaz, üçüncü artırır. - Yükseltme takvimi sizi bekler. Kubernetes yılda üç minor sürüm çıkarıyor ve her sürüm yaklaşık 14 ay yama alıyor. Yükseltmeyi ertelemek, bir gün desteklenmeyen bir sürümde kalmak demek.
- Sertifikaların ömrü var. Küme içi sertifikalar süreli üretilir; kimse yenilemezse bir sabah küme kendi kendisiyle konuşamaz hâle gelir.
- etcd yedeği ve geri dönüş tatbikatı gerekir. Yedeği olmayan etcd, kümenin tamamının tek arıza noktasıdır.
- Yönetilen servis işin yarısını alır. EKS, GKE ve AKS control plane’i devralır; ağ eklentisi, depolama sınıfları, RBAC, kaynak limitleri ve yükseltme kararları sizde kalır.
Kabaca bir ölçüt: bu işlerle ilgilenecek kimse yoksa, Kubernetes ekibinize güvenilirlik değil, yeni bir nöbet listesi ekler.
Karar Soruları
Dört soru, kararı büyük ölçüde belirler:
- Uygulamanız tek bir sunucunun kaybına dayanmak zorunda mı? Değilse merdivenin ilk iki basamağı yeterli.
- Kaç servis, kaç ekip? Tek ekip ve üç servis için Kubernetes’in getirdiği soyutlama katmanı, kazandırdığından fazlasını götürür.
- Yükünüz değişken mi? Sabit yükte otomatik ölçeklemenin karşılığı yoktur; sabit kapasite daha ucuzdur.
- Kümeyi kim işletecek? Bu sorunun cevabı bir isim değilse, cevap “henüz değil"dir.
Bunların hepsine “hayır” diyorsanız Kubernetes size gerekmiyor demektir — ama öğrenmek için ihtiyaç şartı yok. kind veya k3s ile dizüstü bilgisayarınızda dakikalar içinde bir küme kurulur ve serinin çoğu konusu orada denenebilir. Serinin bir sonraki evresinde tam olarak bunu yapacağız.
Sonuç
- Konteyner hafif bir sanal makine değil, çekirdeğin görüşünü ve kaynağını sınırladığı sıradan bir süreçtir. Host’un
psçıktısında görünmesi veuname -r‘ın host çekirdeğini yazması bunun kanıtı; paylaşılan çekirdek hem hızın hem de zayıf izolasyon sınırının sebebi. - Konteynerin içindeki
root, host’unroot‘udur. User namespace varsayılan olarak ayrılmaz; bu yüzdendockergrubuna üyelik vermek,sudoyetkisi vermekle aynı ağırlıkta bir karardır. - Kubernetes konteyner çalıştırmak için değil, konteyneri sizin yerinize ayakta tutmak için var. Farkı yaratan mekanizma emir değil, istenen durumla gözlenen durumu sürekli karşılaştıran uzlaşma döngüsüdür.
restart: alwaysyalnızca ölen süreci kaldırır, asılı kalanı kaldırmaz. Sağlık kontrolüne eylem bağlayan katman Kubernetes’te livenessProbe’dur; Docker Engineunhealthyyazar ve durur.- Kubernetes’in maliyeti kurmak değil, işletmektir. Tek sunucuda çalışan üç servis için merdivenin alt basamağı hâlâ doğru cevaptır.
Sıradaki yazıda kümenin kapağını açıyoruz: control plane hangi bileşenlerden oluşuyor, kubectl apply dediğinizde istek hangi duraklardan geçiyor ve uzlaşma döngüsünü fiilen kim çeviriyor? Serinin tamamı için Kubernetes Öğrenme Yolculuğu sayfasına bakabilirsiniz.
Ek: Terimler Sözlüğü
| Terim | Karşılığı |
|---|---|
| İmaj (image) | Uygulamanın ve bağımlılıklarının salt okunur katmanlardan oluşan paketi. Çalışmıyor, diskte duruyor. |
| Konteyner | Bir imajın üstüne yazılabilir katman eklenip namespace ve cgroup ile sınırlandırılarak başlatılmış hâli. |
| namespace | Çekirdek özelliği. Sürecin ne gördüğünü sınırlar: süreç ağacı, ağ, bağlama noktaları, hostname. |
| cgroup | Çekirdek özelliği. Sürecin ne kadar tükettiğini sınırlar: CPU, bellek, disk G/Ç. |
| OCI | Open Container Initiative. İmaj biçimini ve çalışma zamanı davranışını standartlaştıran belirtim; Docker ile üretilen imajın Kubernetes’te çalışmasının sebebi. |
| runc / containerd | Konteyneri fiilen başlatan alt katman. runc namespace ve cgroup’ları kurar, containerd yaşam döngüsünü yönetir. |
| Orkestrasyon | Konteynerleri birden çok sunucu üzerinde yerleştirme, ayakta tutma, adresleme ve güncelleme işi. |
| Control plane / düğüm (node) | Kümenin karar veren beyni ile iş yüklerini fiilen çalıştıran sunucular. |
| Pod | Kubernetes’in en küçük zamanlama birimi. Ağ ve depolamayı paylaşan bir veya birkaç konteyner. |
| Uzlaşma döngüsü | İstenen durum ile gözlenen durumu sürekli karşılaştırıp farkı kapatan denetleyici mantığı. |