
Mon Sep 21 2026

Hoş geldin! 👋 Bu yazıda Kubernetes v1.37 ile birlikte beta olarak sunulan yeni depolama özelliklerini kolayca kavrayacak ve cluster’ında nasıl kullanacağını öğreneceksin.
Kimler faydalanacak?
fsGroup ve permission mode desteği bekleyenlerNeden önemli?
fsGroup & permission mode → pod seviyesinde grup sahipliği ve dosya izinlerini merkezi olarak tanımlamanı sağlar 📂Bu bölümden ne kazanacaksın?
Hazırsan hemen detaylara dalalım! 🚀
Bind mount’lar artık readOnly ve mountPropagation/timeout gibi yeni parametrelerle çok daha kontrollü hale geldi 🎯. Bu sayede konteynerler arası veri paylaşımı hem güvenli hem de önlenebilir hale geliyor.
Bu özellikler özellikle şu senaryolarda hayat kurtarıyor:
Aşağıdaki YAML, hostPath volume kullanarak bind mount oluşturur ve üç parametreyi bir arada gösterir:
apiVersion: v1
kind: Pod
metadata:
name: bind-mount-demo
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "while true; do cat /data/log.txt; sleep 5; done"]
volumeMounts:
- name: host-logs
mountPath: /data
readOnly: true # ✅ Sadece okuma
mountPropagation: HostToContainer # 🔁 Host → Container
volumes:
- name: host-logs
hostPath:
path: /var/log/myapp
type: DirectoryOrCreate
# timeoutSeconds: 30 # ⏱ Bağlantı için 30 sn bekleme (Kubernetes 1.27+)
Bu sayede ne oluyor?
readOnly: true → app konteyneri /data/log.txt dosyasını yazamaz, sadece tail‑leyebilir.mountPropagation: HostToContainer → Host’taki /var/log/myapp altındaki yeni log satırları anında konteynerde görünür.timeoutSeconds (yorum satırında) → Eğer hostPath bağlanırken 30 saniyeyi geçerse Kubernetes mount’u iptal eder, Pod ContainerCreating durumunda takılmaz.İpucu:
timeoutSecondsalanını kullanmak için cluster’ınızın Kubernetes 1.27+ sürümünde olması gerekiyor. Daha eski sürümlerde bu parametre yoksayılır.
| Parametre | Ne İşe Yarar? | Tipik Kullanım |
|---|---|---|
readOnly |
Yazma engeller | Log sidecar, config dosyaları |
mountPropagation: HostToContainer |
Host → Container tek yönlü senkron | Canlı log izleme, geçici dosya aktarımı |
timeoutSeconds |
Bağlantı bekleme süresini sınırlar | Ağ/Depolama gecikmelerine karşı dayanıklılık |
Bu seçenekleri kombine ederek daha az sürpriz, daha güvenli ve daha önlenebilir bind mount’lar elde edersiniz 🚀.
EmptyDir volume’ları artık fsGroupChangePolicy ve mode alanları sayesinde dosya sahipliğini ve izinlerini otomatik yönetebiliyor 🎯. Bu sayede pod security context ile birleştiğinde çok kiracılı (multi‑tenant) ortamlarda izolasyon çok daha güçleniyor ✅.
fsGroup ile uyuşmuyorsa, Kubernetes otomatik olarak sahipliği ve izinleri düzeltir 🔁.rw-rw---- izniyle gelir, yani sadece aynı grubun üyeleri erişebilir 🔐.apiVersion: apps/v1
kind: Deployment
metadata:
name: emptydir-demo
spec:
replicas: 2
selector:
matchLabels:
app: emptydir-demo
template:
metadata:
labels:
app: emptydir-demo
spec:
securityContext:
fsGroup: 1000 # 👥 Tüm container'lar bu grup ile çalışacak
fsGroupChangePolicy: OnRootMismatch # 🔄 Kök uyuşmazsa otomatik düzelt
volumes:
- name: shared-data
emptyDir:
mode: 0770 # 📁 Varsayılan dosya izni: rw-rw----
containers:
- name: writer
image: busybox
command: ["sh", "-c", "echo 'merhaba' > /data/msg.txt && sleep 3600"]
volumeMounts:
- name: shared-data
mountPath: /data
- name: reader
image: busybox
command: ["sh", "-c", "cat /data/msg.txt && sleep 3600"]
volumeMounts:
- name: shared-data
mountPath: /data
/data) fsGroup ile uyuşmuyorsa, OnRootMismatch politikası devreye girer ve sahiplik/izinler 0770 moduna göre ayarlanır.Bu küçük ayarlar, üretim ortamlarında güvenliği artırır ve manuel chmod/chown işlemlerini ortadan kaldırır 🛠. Hazırsan bir sonraki bölgede bu politikaları StatefulSet ve DaemonSet senaryolarında nasıl genişleteceğimize bakalım 🚀.
Merhaba! Çok kiracılı (multi‑tenant) Kubernetes kümelerinde en çok gördüğüm baş ağrılarından biri, bind mount ve emptyDir kullanımındaki basit ama tehlikeli yanlış yapılandırmalar. Hadi bu riskleri maddeler halinde inceleyelim ve ardından bir “kötü senaryo” hikayesiyle somutlaştıralım 🎯.
HostPath / bind mount ile yazma izni verme 📂
hostPath: tanımlarken type: DirectoryOrCreate + readOnly: false bırakılırsa, pod host dosya sistemine tam erişim kazanır./etc, /var/log veya /root gibi kritik dizinlerine yazabilir → veri sızıntısı ve ayrıcalık yükseltme riski.emptyDir’de varsayılan izinlerin çok geniş olması 📁
emptyDir: {} oluşturulduğunda, container’ın root kullanıcısı olarak çalışması durumunda dizin 777 gibi geniş izinlerle mount edilir.hostPath ile aynı emptyDir’i bağlarsa, kiracılar arası dosya paylaşımı gerçekleşir.SecurityContext eksikliği 🔐
runAsNonRoot: true, runAsUser, fsGroup tanımlanmazsa container root olarak çalışır → host dosya sistemine yazma yetkisi otomatik kazanılır.Privileged / CAP_SYS_ADMIN kapsamı ⚙️
privileged: true veya capabilities: { add: ["SYS_ADMIN"] } eklenirse, container mount namespace manipüle edebilir → host’a bind mount yapabilir.NetworkPolicy / PodSecurityPolicy yokluğu 🚫
| Risk | Etki | Örnek Senaryo |
|---|---|---|
| Host dosya sistemine yazma | Kiracı A, host’taki log dosyalarını siler / değiştirir → Kiracı B’nin logları kaybolur. | hostPath: /var/log + readOnly: false |
| emptyDir paylaşımı | Aynı node’da çalışan Kiracı B, Kiracı A’nın emptyDir içindeki geçici dosyaları okur → veri sızıntısı. |
emptyDir: {} + iki pod aynı volumeName kullanıyor. |
| Ayrıcalık yükseltme | Kiracı A, CAP_SYS_ADMIN ile yeni bir mount namespace oluşturur, host’un / dizinini kendi pod’una bind eder → root erişimi. |
privileged: true pod’u mount --bind / /mnt/host çalıştırır. |
Senaryo: Acme Corp iki müşteriyi (Tenant‑X ve Tenant‑Y) aynı kümede barındırıyor.
- Tenant‑X geliştiricisi, “geçici dosyalar için”
emptyDirkullanan bir pod dağıtır ama securityContext eklemeyi unutur. Pod root olarak çalışıyor.- Tenant‑Y’nin bir pod’u, aynı node’da
hostPath: /var/lib/kubelet/pods/<tenant‑x‑pod‑uid>/volumes/kubernetes.io~emptyDir/tempyolunu readOnly: false ile mount eder (kubelet’in oluşturduğuemptyDirhost tarafında bir dizin olarak görünür).- Tenant‑Y, bu dizine kötü amaçlı bir script yazar (
chmod +x exploit.sh).- Tenant‑X’in pod’u, bir sonraki yeniden başlatmada
emptyDiriçeriğini okuyarak scripti çalıştırır → container escape sağlar ve host’a root shell açar.- Artık Tenant‑Y, kümedeki tüm pod’ların secrets’larına, configmap’lerine ve hatta etcd verilerine erişebilir. 🚨
Ne öğrendik?
runAsNonRoot: true ve en az ayrıcalık ilkesi (least privilege) uygulayın. ✅hostPath kullanımı kesinlikle readOnly: true ile sınırlayın veya tamamen kaldırın. ⛔emptyDir için pod‑level fsGroup ve runAsUser ayarlayarak izinleri 750 gibi daraltın. 🛠Küçük bir hatırlatma: Bu kontrolleri CI/CD boru hattında statik analiz (ör. kube‑score, checkov) ve admission controller (OPA/Gatekeeper) ile otomatikleştirin. Böylece “ünleme” işareti 🚩 görmeden önce riskleri yakalarsınız.
Bir sonraki bölümde bu riskleri nasıl policy‑as‑code ile engelleyebileceğimize dair somut Rego / Kyverno örneklerine bakacağız. 🚀
Hazırsan Pod Security Standards (restricted, baseline, privileged) ile yeni beta özellikleri nasıl uyumlu hale getireceğimize bakalım 🎯. Eski PodSecurityPolicy yerine artık PodSecurity admission controller ve ValidatingAdmissionPolicy kullanıyoruz. Cluster admin olarak bu kuralları zorunlu kılmak için adım adım bir ClusterRole + RoleBinding kurulumu yapacağız.
PodSecurityPolicy terk ediyoruz?pod-security.kubernetes.io/enforce) çalışıyorapiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Ne oluyor?
Bu etiketler sayesinde production namespace’ine atılan her pod restricted kural setine tabi olur. audit ve warn loglama ve uyarı sağlar.
ValidatingAdmissionPolicy – bind mount readOnly ve emptyDir mode kontrolüSenaryo:
- Tüm
hostPathvolume’ları readOnly: true olmalıemptyDirvolume’larındamedium: Memorykullanıyorsakmode0400’den büyük olmamalı (güvenlik için)
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
name: restrict-volume-mounts
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: |
!has(object.spec.volumes) ||
object.spec.volumes.all(v,
!has(v.hostPath) || v.hostPath.readOnly == true
)
message: "hostPath volume'ları readOnly: true olmalıdır"
- expression: |
!has(object.spec.volumes) ||
object.spec.volumes.all(v,
!has(v.emptyDir) ||
!has(v.emptyDir.medium) ||
v.emptyDir.medium != "Memory" ||
(has(v.emptyDir.mode) && v.emptyDir.mode <= 0400)
)
message: "Memory‑backed emptyDir mode 0400 veya daha düşük olmalıdır"
Nasıl çalışıyor?
matchConstraints → sadece Pod create/update işlemlerini yakalarvalidations → CEL ifadeleri ile volume özelliklerini denetlerfailurePolicy: Fail → kural ihlali olursa pod reddedilir ⛔apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: restrict-volume-mounts-binding
spec:
policyName: restrict-volume-mounts
validationActions: ["Deny"]
matchResources:
namespaceSelector:
matchLabels:
pod-security.kubernetes.io/enforce: restricted
Bu sayede ne oluyor?
validationActions: ["Deny"] → ihlal durumunda istek engellenir ✅apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: enforce-pod-security-standards
rules:
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["validatingadmissionpolicies", "validatingadmissionpolicybindings"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: enforce-pod-security-standards-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: enforce-pod-security-standards
subjects:
- kind: User
name: cluster-admin-user # ← kendi admin kullanıcınızı yazın
apiGroup: rbac.authorization.k8s.io
Adım adım özet
restricted profili aktif 🎯Artık eski PodSecurityPolicy’ye gerek kalmadan, namespace‑level etiketler ve CEL tabanlı ValidatingAdmissionPolicy ile pod güvenliğini merkezi ve esnek bir şekilde zorunlu kılabilirsiniz. Bu yapı hem beta özelliklerle uyumlu hem de geleceğe dönük (GA) olacak. 🚀
Hazırsan bind mount ve EmptyDir ayarlarının I/O gecikmesini ve CPU kullanımını nasıl etkilediğini birlikte inceleyelim 🚀
mode ve fsGroupChangePolicy → dosya izinleri ve grup değişiklikleri için ek chown/chmod sistem çağrılarını engeller → I/O gecikmesi düşer 📉mountPropagation seçenekleri (None, HostToContainer, Bidirectional) → hangi yönlerde mount olaylarının yayılacağını belirler; yanlış seçim gereksiz mount/unmount tetikleyebilir 🔁mountPropagation performans karşılaştırması| Seçenek | Ne Zaman Kullanılır? | Performans Etkisi |
|---|---|---|
| None (varsayılan) | İzole pod’lar, paylaşım gerekmez | En düşük ek yük, en hızlı I/O ✅ |
| HostToContainer | Host’taki yeni mount’ların konteynere görünmesi gerekiyorsa | Her host mount evento ekstra namespace işlemi → hafif gecikme ⚠️ |
| Bidirectional | Hem host→container hem container→host yayılımı gerekiyorsa | İki yönlü propagation → en yüksek CPU ve latency maliyeti ⛔ |
Özet: Çoğu statik veri paylaşımı için None yeterlidir. Dinamik paylaşım zorunluysa HostToContainer’ı tercih edin, Bidirectional’ı sadece gerçekten gerekirse açın.
kubectl exec + fioAşağıdaki bash betiği pod içine girip sıralı (seqwrite) ve rastgele (randwrite) yazma testlerini çalıştırır, sonuçları terminale basar.
/data dizini hem bind mount (read‑only) hem EmptyDir olarak iki farklı pod’da test edilebilir.
#!/usr/bin/env bash
# 📦 Pod adı ve test dizini
POD_NAME="perf-test-pod"
TEST_DIR="/data"
# fio job dosyası (sıralı yazma)
cat <<EOF > /tmp/fio_seqwrite.fio
[seqwrite]
rw=write
bs=4k
size=512M
numjobs=1
runtime=30
time_based=1
directory=${TEST_DIR}
direct=1
ioengine=libaio
EOF
# fio job dosyası (rastgele yazma)
cat <<EOF > /tmp/fio_randwrite.fio
[randwrite]
rw=randwrite
bs=4k
size=512M
numjobs=1
runtime=30
time_based=1
directory=${TEST_DIR}
direct=1
ioengine=libaio
EOF
# Pod içine kopyala ve çalıştır
kubectl cp /tmp/fio_seqwrite.fio ${POD_NAME}:/tmp/fio_seqwrite.fio
kubectl cp /tmp/fio_randwrite.fio ${POD_NAME}:/tmp/fio_randwrite.fio
echo "▶️ Sıralı yazma testi başlıyor..."
kubectl exec ${POD_NAME} -- fio /tmp/fio_seqwrite.fio --output-format=json | jq '.jobs[0].write'
echo "▶️ Rastgele yazma testi başlıyor..."
kubectl exec ${POD_NAME} -- fio /tmp/fio_randwrite.fio --output-format=json | jq '.jobs[0].write'
Nasıl çalışıyor?
fio job dosyaları 4 KB blok, 512 MB veri, 30 sn süreyle çalışacak şekilde hazırlanır.kubectl cp ile pod içine kopyalanır.kubectl exec ile fio çalıştırılır, JSON çıktısı jq ile write metrikleri (bw, iops, lat) filtrelenir.bw (bant genişliği) ve iops değerleri EmptyDir’e göre %10‑%20 daha yüksek olabilir.top/pidstat ile izlenirse) bind mount’ta %5‑%15 daha düşük görünür çünkü yazma işlemi kernel tarafından engellenir.mountPropagation: None ile test edildiğinde latency (clat) en düşük seviyededir; Bidirectional açıldığında %30‑%50 artış gözlemlenebilir.Bu küçük benchmark seti ile kendi cluster’ınızda hangi volume yapılandırması en iyi performansı verdiğini hızlıca görebilirsiniz 🎯
Hazırsan cluster'ımızı v1.37'ye taşımaya başlayalım. Süreç aslında üç ana adımda özetlenebilir: control plane, node ve kubelet yükseltmesi. Her aşamada "bir tıkla yaparım" demek riskli, bu yüzden canary yaklaşımıyla gidelim 🎯
| Aşama | Ne Yapılır? | Önemli Not |
|---|---|---|
| Control Plane | kubeadm upgrade plan → kubeadm upgrade apply v1.37.x |
Önce bir master node'da test et, sonra diğerleri |
| Worker Node | kubeadm upgrade node + kubelet restart |
Drain → upgrade → uncordon sırası şart |
| kubelet | Systemd drop-in ile argüman güncelleme | Feature gate'ler buradan etkinleştirilir ⚙️ |
İpucu: Her adımda
kubectl get nodes -o wideile VERSION sütununu kontrol et. Bir node v1.37, diğeri v1.36 kalmışsa schedule etme ⛔
v1.37'de beta olan iki özellik işimizi kolaylaştırıyor:
BindMountReadOnly=true → HostPath volume'ları read-only mount eder (güvenlik +1 🔒)EmptyDirFSGroupChangePolicy=true → emptyDir volume'larda fsGroup değişiklik politikası kontrolüHer iki bileşende de aynı flag'leri vermelisin. Aksi takdirde API server "evet" derken kubelet "hayır" diyecek 🤷♂️
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
featureGates:
BindMountReadOnly: true
EmptyDirFSGroupChangePolicy: true
Bunu uygulamanın en temiz yolu:
kubeadm config patch --patch-file feature-gates-patch.yaml
Sonra kubeadm upgrade apply v1.37.x çalıştırdığında bu gate'ler control plane manifest'lerine (kube-apiserver, controller-manager, scheduler) otomatik enjekte edilir ✅
Control plane kubeadm hallediyor ama kubelet her node'da ayrı bir systemd servisi. Drop-in dosyasıyla argüman ekleyelim:
mkdir -p /etc/systemd/system/kubelet.service.d
cat > /etc/systemd/system/kubelet.service.d/10-feature-gates.conf <<'EOF'
[Service]
Environment="KUBELET_EXTRA_ARGS=--feature-gates=BindMountReadOnly=true,EmptyDirFSGroupChangePolicy=true"
EOF
systemctl daemon-reload
systemctl restart kubelet
Ne oluyor burada?
Environment=satırı kubelet başlatılırken--feature-gates=...argümanını enjekte eder- Drop-in yöntemi package manager güncellemelerini bozmaz (override yerine extend eder) 🛠
Tüm worker node'larda bunu tek tek mı yapacaksın? Tabii ki hayır — Ansible / Cluster API / node pool user-data ile otomatikleştir 🚀
Canary stratejisi şöyle olur:
Rollback ihtiyacı olursa (umarım olmaz ama planlı olmalı):
| Bileşen | Rollback Yöntemi |
|---|---|
| Control Plane | kubeadm upgrade apply v1.36.x (eski versiyon) |
| kubelet | Drop-in dosyasını sil → systemctl daemon-reload && systemctl restart kubelet |
| Containerd / CRI | apt/yum downgrade containerd.io + restart |
❗️ Dikkat: Etcd snapshot mutlaka alınıp yedeklenmeli upgrade öncesi.
etcdctl snapshot save /backup/etcd-pre-v137.db— bu tek satır kurtarıcı olabilir 🌊
kubeadm upgrade plan çıktısı temiz mi?kubeadm config patch ile uygulandı mı?Bu adımları sırayla uygularsan v1.37 geçişi sürprizsiz geçer. Herhangi bir adımda hata alırsan journalctl -u kubelet -f ve kubectl logs -n kube-system kube-apiserver-<node> ilk bakman gereken yerler 🔍
Hadi şimdi terminali aç ve başla — cluster'ın v1.37'yi bekliyor! 🚀
Hazırsan bu yazıyı pozitif bir notla bitirelim 🎉
Kubernetes depolama ekosistemi sürekli evriliyor — beta özelliklerin GA (General Availability) olma yol haritasını takip etmek, sürprizlere yakalanmamak için en iyi yöntem.
"Production'a atmadan önce kind veya minikube üzerinde yeni CSI sürücüsünü, volume snapshot'ı veya
VolumeAttributesClass'ı dene."
Küçük bir cluster kur,kubectl applyet, loglara bak — hata mesajları en iyi öğretmenlerdir 📚
Soruların olursa yorumlarda paylaş, birlikte çözelim 🤝
#sig-storage kanalına at, yorum yaz 🙋♂️Son söz: Depolama, Kubernetes'in en "sessiz ama güçlü" parçalarından biri. Özellikler olgunlaştıkça hayatımız kolaylaşıyor — sadece meraklı kalıp denemeye devam et 🚀
Keyifli cluster'lar! ⚓️
Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.
All rights reserved