Bir Pod'u sildiğinde içindeki her şey gider. Bu, durumsuz bir web uygulaması için sorun değil — hatta istenen davranış. Ama bir PostgreSQL, bir dosya yükleme dizini ya da bir Prometheus veri deposu söz konusuysa aynı davranış felakettir. Kubernetes Persistent Volume mekanizması tam olarak bu boşluğu doldurur: Pod'un yaşam döngüsünden bağımsız, yeniden başlatmalara ve düğüm değişikliklerine dayanan bir depolama katmanı sunar.
Bu rehberde PV, PVC ve StorageClass üçlüsünün nasıl birbirine bağlandığını, dinamik sağlamanın neden elle PV oluşturmaktan iyi olduğunu, erişim modlarının pratikte ne anlama geldiğini, StatefulSet ile Pod başına ayrı disk vermeyi, hacim genişletmeyi ve en sık karşılaşılan üç sorunun (Pending PVC, Multi-Attach hatası, Terminating takılması) çözümünü anlatacağım. Docker tarafındaki hacim mantığına aşina değilsen Docker volume veri yönetimi yazısı iyi bir zemin sağlar.
Konteyner Diski Neden Yetmiyor#
Bir konteynerin yazılabilir katmanı geçicidir. Konteyner silindiğinde o katman da silinir; Pod yeniden zamanlandığında başka bir düğümde sıfırdan bir katmanla açılır. Kubernetes'in emptyDir hacmi bir adım ileridir ama yeterli değildir: Pod yaşadığı sürece verinin kalmasını sağlar, Pod silindiğinde veri yine gider. Yani emptyDir geçici çalışma alanı ve konteynerler arası paylaşım içindir, kalıcılık için değil.
Üç seçeneği karşılaştıralım:
| Hacim türü | Ömrü | Kullanım |
|---|---|---|
| Konteyner katmanı | Konteynerle birlikte | Hiçbir zaman veri için kullanma |
emptyDir | Pod ile birlikte | Geçici dosya, önbellek, konteynerler arası paylaşım |
hostPath | Düğümde kalır | Yalnızca tek düğümlü kurulum ve düğüm ajanları |
| PersistentVolume | Pod'dan bağımsız | Veritabanı, yükleme dizini, kalıcı her şey |
hostPath cazip görünür çünkü kurulumu kolaydır, ama Pod başka bir düğüme zamanlandığında veriyi bulamaz ve düğümün dosya sistemine doğrudan erişim verdiği için ciddi bir güvenlik riski taşır. Çok düğümlü hiçbir üretim kurulumunda kullanılmamalıdır.
PV, PVC ve StorageClass Üçlüsü#
Kubernetes depolamayı iki tarafa ayırır ve bu ayrım bir kez anlaşıldığında her şey yerine oturur:
- PersistentVolume (PV) — kümedeki gerçek depolama parçasıdır. Bir bulut diski, bir NFS paylaşımı, bir iSCSI birimi olabilir. Yöneticinin dünyasına aittir.
- PersistentVolumeClaim (PVC) — bir uygulamanın "bana 20 GiB, tek düğümden yazılabilir bir disk lazım" talebidir. Geliştiricinin dünyasına aittir.
- StorageClass (SC) — talep geldiğinde diski otomatik oluşturan şablondur. Hangi sürücü, hangi disk tipi, hangi politika.
Zincir şöyle işler: PVC oluşturursun, StorageClass o talebi görür, arka plandaki sürücü gerçek diski oluşturur, karşılığında bir PV üretilir ve PVC ona bağlanır. Buna dinamik sağlama denir ve elle PV yazmaktan çok daha sağlıklıdır; elle yönetimde disk boyutları uyuşmaz, artık PV'ler birikir ve kimse hangi diskin kime ait olduğunu bilmez.
Kümende hangi sınıfların olduğunu görmek için:
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
# standard (default) csi.ornek-saglayici Delete WaitForFirstConsumer true
# hizli-ssd csi.ornek-saglayici Retain WaitForFirstConsumer true
(default) etiketi önemlidir: PVC'de storageClassName yazmazsan varsayılan sınıf kullanılır. Varsayılan sınıf tanımlı değilse ve sen de belirtmediysen PVC sonsuza kadar Pending kalır — bu, en sık karşılaşılan tuzaklardan biridir.
İlk PVC'yi Oluşturmak ve Pod'a Bağlamak#
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: veritabani-disk
spec:
accessModes:
- ReadWriteOnce
storageClassName: hizli-ssd
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels: { app: postgres }
strategy:
type: Recreate # RWO diskte RollingUpdate kilitlenmeye yol açar
template:
metadata:
labels: { app: postgres }
spec:
securityContext:
fsGroup: 999 # bağlanan diskin grup sahipliğini düzeltir
containers:
- name: postgres
image: postgres:16-alpine
envFrom:
- secretRef:
name: db-secret
volumeMounts:
- name: veri
mountPath: /var/lib/postgresql/data
subPath: pgdata # kayıp+bulundu klasörü sorununu önler
volumes:
- name: veri
persistentVolumeClaim:
claimName: veritabani-disk
Üç ayrıntıya dikkat et. strategy: Recreate kritiktir: varsayılan RollingUpdate ile yeni Pod eski Pod kapanmadan açılmaya çalışır, ikisi aynı ReadWriteOnce diski isteyince güncelleme kilitlenir. fsGroup alanı, bağlanan diskin sahipliğini konteynerin çalıştığı gruba verir; bunu yazmazsan çoğu veritabanı imajı "permission denied" ile açılmaz. subPath: pgdata ise diskin kökündeki lost+found klasörünün veritabanı imajını "dizin boş değil" hatasına düşürmesini engeller.
Durumu doğrulamak için:
kubectl get pvc
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
# veritabani-disk Bound pvc-9f2c... 20Gi RWO hizli-ssd
kubectl describe pvc veritabani-disk | tail -n 12
Yapılandırma değerlerini Secret üzerinden verdiğimize dikkat et; bu yaklaşımın ayrıntıları ConfigMap ve Secret kullanımı yazısında.
Erişim Modları ve Yeniden Kazanım Politikası#
Erişim modu, diskin aynı anda kaç yerden bağlanabileceğini belirler ve yanlış anlaşılması en kolay konudur:
| Mod | Kısaltma | Gerçek anlamı |
|---|---|---|
ReadWriteOnce | RWO | Tek düğümden okuma-yazma (aynı düğümdeki birden çok Pod bağlanabilir) |
ReadOnlyMany | ROX | Birden çok düğümden yalnızca okuma |
ReadWriteMany | RWX | Birden çok düğümden okuma-yazma |
ReadWriteOncePod | RWOP | Tek Pod, kesin kilit |
Buradaki en yaygın yanılgı ReadWriteOnce'ın "tek Pod" anlamına geldiğini sanmaktır. Aslında sınır düğüm düzeyindedir: aynı düğüme zamanlanmış iki Pod aynı RWO diski paylaşabilir. Gerçekten tek Pod garantisi istiyorsan ReadWriteOncePod kullanmalısın.
Uygulamada ReadWriteMany çoğu blok depolama sürücüsünde desteklenmez. Birden fazla Pod'un aynı dizine yazması gerekiyorsa (klasik örnek: birden çok kopyalı WordPress'in wp-content/uploads dizini) bir dosya sistemi tabanlı çözüme, örneğin NFS'e ihtiyaç duyarsın. Bunu bilmeden replicas: 3 yapıp RWO disk vermek, üç Pod'un ikisinin sonsuza dek ContainerCreating durumunda kalmasına yol açar.
Yeniden kazanım politikası ise PVC silindiğinde asıl diske ne olacağını belirler:
| Politika | Davranış | Ne zaman |
|---|---|---|
Delete | PVC silinince disk de silinir | Test ortamları, kolay yeniden oluşturulabilen veri |
Retain | PVC silinse de disk kalır, elle temizlenir | Üretim veritabanları, kritik veri |
Üretim veritabanı için mutlaka Retain politikalı bir StorageClass kullan. Yanlışlıkla silinen bir PVC, Delete politikasıyla birlikte veriyi de götürür ve bunu geri almanın tek yolu yedektir. Kalıcı hacimlerin varlığı yedek ihtiyacını ortadan kaldırmaz; disk anlık görüntüleri ve düzenli mantıksal yedekler için yedekleme çözümümüz bu boşluğu doldurur.
StatefulSet: Pod Başına Ayrı Disk#
Üç kopyalı bir veritabanı kümesi kurmak istiyorsan Deployment doğru araç değildir; her kopyanın kendi diskine ve sabit bir kimliğe ihtiyacı vardır. StatefulSet tam olarak bunu sağlar ve volumeClaimTemplates alanı sayesinde her Pod için ayrı PVC üretir.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: veri
spec:
serviceName: veri # headless Service adı
replicas: 3
selector:
matchLabels: { app: veri }
template:
metadata:
labels: { app: veri }
spec:
containers:
- name: veri
image: firmaniz/veri:2.0
volumeMounts:
- name: depo
mountPath: /veri
volumeClaimTemplates:
- metadata:
name: depo
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: hizli-ssd
resources:
requests:
storage: 50Gi
Bu tanım üç PVC üretir: depo-veri-0, depo-veri-1, depo-veri-2. Pod isimleri de sabittir (veri-0, veri-1, veri-2) ve bir Pod yeniden oluşturulduğunda kendi eski diskine geri bağlanır.
Bilinmesi gereken önemli bir davranış: StatefulSet'i sildiğinde PVC'ler silinmez. Bu kasıtlı bir güvenlik önlemidir; veriyi kazara kaybetmeni engeller. Ancak kümeyi temizlerken yalnızca StatefulSet'i silip işi bitirdiğini sanırsan diskler (ve faturaları) durmaya devam eder:
kubectl delete statefulset veri
kubectl get pvc -l app=veri # PVC'ler hâlâ burada
kubectl delete pvc depo-veri-0 depo-veri-1 depo-veri-2
Hacim Genişletme ve Sorun Giderme#
Disk dolmaya başladığında, StorageClass allowVolumeExpansion: true ise PVC'yi düzenleyerek büyütebilirsin. Küçültme hiçbir sürücüde desteklenmez.
# Talebi 20Gi'den 50Gi'ye çıkar
kubectl patch pvc veritabani-disk -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'
# İlerlemeyi izle
kubectl describe pvc veritabani-disk | grep -A5 Conditions
Bazı sürücüler dosya sistemini genişletmek için Pod'un yeniden başlatılmasını ister; FileSystemResizePending durumu görüyorsan bir kubectl rollout restart yeterlidir.
En sık karşılaşılan üç sorun ve gerçek sebepleri:
1. PVC sonsuza kadar Pending. kubectl describe pvc çıktısındaki olay satırı sebebi açıkça yazar:
kubectl describe pvc veritabani-disk | tail -n 6
# Events:
# Warning ProvisioningFailed no persistent volumes available for this claim
# Normal WaitForFirstConsumer waiting for first consumer to be created
İkinci mesaj bir hata değildir: volumeBindingMode: WaitForFirstConsumer ayarlı bir sınıfta disk, Pod zamanlanana kadar oluşturulmaz. Bu davranış kasıtlıdır ve diskin yanlış bölgeye açılmasını önler. İlk mesaj ise gerçek bir sorundur: varsayılan StorageClass yok, istenen sınıf adı yanlış yazılmış ya da sürücü çalışmıyordur.
2. Multi-Attach error for volume. RWO bir disk hâlâ eski düğüme bağlıyken Pod başka bir düğüme zamanlanmıştır. Genelde düğüm çökmesi ya da RollingUpdate sırasında olur. Çözüm, eski Pod'un gerçekten kapandığından emin olmak ve Deployment stratejisini Recreate yapmaktır.
3. PVC Terminating durumunda takılıyor. Kubernetes, PVC'yi kullanan bir Pod varken silinmesini engellemek için bir koruma işareti kullanır. Önce Pod'u sil, PVC kendiliğinden gider. Bu işareti elle kaldırmak veriyi kullanan bir Pod varken bozulmaya yol açabilir; son çare olarak bile dikkatli kullanılmalıdır.
Sık Yapılan Hatalar ve Tuzaklar#
hostPathile üretime çıkmak. Pod başka düğüme geçtiğinde veri yok olur; ayrıca düğümün dosya sistemine erişim ciddi bir güvenlik açığıdır.- Deployment ile RWO diski birden çok kopyada kullanmak. İkinci ve üçüncü Pod bağlanamaz. Ya kopya sayısını 1'de tut ya da StatefulSet'e geç.
Deletepolitikalı sınıfta üretim veritabanı çalıştırmak. Yanlışlıkla silinen bir PVC veriyi de götürür. Üretim içinRetainşart.fsGrouptanımlamamak. Bağlanan diskin sahibirootkalır ve root olmayan kullanıcıyla çalışan konteyner yazamaz. Hata mesajı genelde uygulamanın kendi diliyle geldiği için sebebi bulmak zaman alır.- Diskin kökünü doğrudan veritabanı dizini yapmak. Çoğu blok disk kökünde
lost+foundklasörü bulundurur ve birçok veritabanı imajı bunu "dizin boş değil" diye reddeder.subPathile bir alt klasör kullan. - Yedeği hacme güvenerek atlamak. Kalıcı hacim dayanıklıdır ama silinen bir tabloyu geri getirmez. Anlık görüntü ve mantıksal yedek birbirinin yerine geçmez, birlikte kullanılır.
- Artık PVC'leri temizlememek. Özellikle StatefulSet silindikten sonra kalan diskler aylarca ücretlendirilir.
kubectl get pvc -Açıktısını düzenli aralıklarla gözden geçir.
Sıkça Sorulan Sorular#
PV ile PVC arasındaki fark nedir#
PersistentVolume, kümede var olan gerçek depolama kaynağıdır ve genelde yönetici tarafından ya da bir StorageClass tarafından otomatik oluşturulur. PersistentVolumeClaim ise bir uygulamanın belirli boyutta ve erişim modunda disk talebidir. Kubernetes talebi uygun bir PV ile eşleştirir; dinamik sağlamada PV, talep geldiği anda otomatik yaratılır.
PVC neden Pending durumunda kalıyor#
En sık üç sebep var. Birincisi, kümede varsayılan StorageClass tanımlı değildir ve PVC'de de sınıf adı belirtilmemiştir. İkincisi, belirtilen sınıf adı yanlış yazılmıştır. Üçüncüsü ve aslında normal olan durum, sınıfın WaitForFirstConsumer modunda olması ve diskin Pod zamanlanana kadar oluşturulmamasıdır. kubectl describe pvc çıktısının olay bölümü hangisi olduğunu net söyler.
ReadWriteOnce diski birden fazla Pod kullanabilir mi#
Kısıt Pod düzeyinde değil düğüm düzeyindedir: aynı düğüme zamanlanmış birden fazla Pod aynı RWO diski bağlayabilir, farklı düğümlerdekiler bağlayamaz. Gerçekten tek Pod garantisi istiyorsan ReadWriteOncePod modunu kullanmalısın. Birden çok düğümden yazma gerekiyorsa ReadWriteMany destekleyen bir dosya sistemi çözümü (örneğin NFS) gerekir.
Kalıcı hacmi büyütebilir miyim, küçültebilir miyim#
StorageClass tanımında allowVolumeExpansion: true yazıyorsa PVC'nin depolama talebini artırarak diski büyütebilirsin; işlem çoğunlukla kesintisiz tamamlanır, bazı sürücülerde Pod'un yeniden başlatılması gerekir. Küçültme hiçbir sürücüde desteklenmez. Daha küçük bir diske geçmen gerekiyorsa yeni bir PVC oluşturup veriyi kopyalaman gerekir.
StatefulSet'i silersem verilerim gider mi#
Hayır. volumeClaimTemplates ile oluşturulan PVC'ler StatefulSet silindiğinde kasıtlı olarak korunur; yeni bir StatefulSet aynı adla oluşturulduğunda Pod'lar eski disklerine geri bağlanır. Bunun tersi de doğrudur: kümeyi gerçekten temizliyorsan PVC'leri elle silmen gerekir, yoksa diskler ve maliyetleri durmaya devam eder.
Veritabanını Kubernetes'te mi çalıştırmalıyım#
Teknik olarak mümkün ve yaygın, ancak bedava değil. Yedekleme, sürüm yükseltme, yük devretme ve performans ayarı gibi işler senin sorumluluğunda kalır ve bunları doğru kurmak ciddi emek ister. Ekibin bu konuda deneyimli değilse veritabanını küme dışında, ayrı ve yönetilen bir sunucuda çalıştırmak çoğu zaman daha az risk taşır; uygulamalar kümede, veri dışarıda kalabilir.
Kapanış#
Kalıcı depolama, Kubernetes'in en çok hata yapılan alanı; çünkü yanlış bir seçim genelde kurulum günü değil, aylar sonra bir düğüm çöktüğünde ortaya çıkar. Aklında kalması gereken dört şey: hostPath ile üretime çıkma, üretim verisi için Retain politikalı bir StorageClass kullan, RWO diski birden çok kopyalı Deployment ile eşleştirme ve StatefulSet sildiğinde arkada kalan PVC'leri unutma. Bir PVC Pending kaldığında cevap neredeyse her zaman kubectl describe pvc çıktısının son satırlarındadır.
Depolama ve küme altyapısını kurarken destek istersen Clou.TR tarafında birkaç seçenek var. Küme düğümleri ve disk kapasitesi için bulut sunucu ve VDS paketlerimizi inceleyebilir, kurulum ve işletme yükünü devretmek istersen sunucu yönetimi hizmetimize göz atabilir, veri kaybına karşı düzenli ve test edilmiş kopyalar için yedekleme çözümümüzü değerlendirebilirsin.