Geçen ay, 28 Nisan sabahı, kendi VPS’imden gelen bir “Disk Space Critical” mailiyle uyandım. df -h çektiğimde, / dizininin %100 dolu olduğunu gördüm. Tahmin ettiğim gibi, suçlu yine Docker’dı. 13’ten fazla container’ı aynı sunucuda yönetince, bir tanesi bile kontrolden çıksa, tüm sistemin swap’e inip OOM-killed olması an meselesi oluyor.
Bu durum, sürekli disk depolama sorunlarıyla boğuşan, kendi sunucusunda birden fazla uygulamayı barındıran herkesin tanıdık olduğu bir senaryo. Ben de bu yazıda, kendi VPS’imde bu “Docker Disk Depolama Savaşları”nı nasıl yönettiğimi, veri bütünlüğünü nasıl sağladığımı ve disk alanını nasıl optimize ettiğimi anlatacağım. Amacım, pratik çözümler ve benim de bizzat yaşadığım tecrübelerle size yol göstermek.
Docker Disk Depolama Savaşları Neden Çıkar?
VPS’teki disk alanımın aniden dolması, benim için bir rutine dönüştü neredeyse. Çoğu zaman, sorunun kökeninde beklenmedik bir container’ın log büyümesi, gereksiz image’lar veya build cache’leri yatıyor. Örneğin, bir dönem Next.js uygulamalarımın build cache’leri 33 GB’ı bulmuştu, bir de üzerine unused image’lar eklenince 23 GB daha disk yemişti. İşte o zaman disk %100 oldu.
Bu tür durumlar, özellikle sınırlı kaynaklara sahip bir VPS kullanıyorsanız, ciddi performans düşüşlerine ve hatta servis kesintilerine yol açabiliyor. Bir container’ın disk I/O’sunun tavan yapması, kcompactd’nin %92 CPU kullanmasına ve sshd’nin yeni bağlantıları kabul edememesine kadar gidebiliyor. Bu yüzden sorunun kökenini iyi anlamak gerekiyor.
Yaygın Disk Alanı Tüketicileri
Docker ekosisteminde disk alanını en çok tüketen unsurları iyi bilmek, sorunu çözmenin ilk adımı. Benim tecrübelerime göre, bu konuda başı çekenler şunlar:
- Dangling Images ve Volumes: Kullanılmayan veya bağlantısı kesilmiş image’lar ve volume’lar, farkında olmadan gigabaytlarca yer kaplayabilir. Özellikle sık sık image rebuild ediyorsanız, bu durum kaçınılmaz hale geliyor.
- Build Cache: Multi-stage build kullanmasanız bile, Docker her build adımında ara katmanlar oluşturur. Bu cache’ler birikerek devasa boyutlara ulaşabilir. Benim 33 GB’lık build cache sorunum buna güzel bir örnekti.
- Container Logları: Özellikle çok konuşkan uygulamalar veya hata ayıklama modunda çalışan servisler, log dosyalarını kontrolsüzce büyütebilir. Birkaç gün içinde gigabaytlarca log birikebilir, hatta bir bankanın iç platformunda bu yüzden kritik servislerinin durduğuna şahit oldum.
- Anlık Veriler ve Geçici Dosyalar: Uygulamaların çalışma esnasında oluşturduğu geçici dosyalar, eğer düzgün temizlenmezse, diskte kalıcı yer edinebilir.
Bu sorunları tespit etmek için docker system df komutu çok işime yarıyor. Bu komut, Docker’ın disk kullanımına dair detaylı bir özet sunarak, hangi bileşenin ne kadar yer kapladığını anlamamı sağlıyor.
docker system df
Bu komutun çıktısı, disk alanını hangi Docker bileşeninin ne kadar kullandığını gösterir. Örneğin, “Images”, “Containers”, “Local Volumes” ve “Build Cache” gibi kategoriler altında detaylı bilgiye ulaşabilirsiniz. Özellikle “Reclaimable” alanı, manuel müdahale ile geri kazanabileceğiniz disk miktarını gösterir.
Veri Bütünlüğü ve Kalıcı Depolama Stratejileri
Docker’da uygulamaları çalıştırırken en önemli konulardan biri, container’lar ölse bile verinin kalıcı olmasını sağlamak. Kendi sitelerimi ve yan ürünlerimi (hesapciyiz.com, spamkalkani.com gibi) barındırdığım için, verilerin kaybolmaması kritik. Yanlış bir konfigürasyon veya otomatik temizlik, verilerinizin anında uçmasına neden olabilir.
Bu yüzden, Docker’ın depolama mekanizmalarını doğru anlamak ve proaktif stratejiler geliştirmek şart. Ben genelde volumes kullanmayı tercih ediyorum çünkü yönetimi daha kolay ve Docker’ın kendi içinde tasarlanmış bir kalıcı depolama çözümü.
Docker Volumes ve Bind Mounts
Docker, verileri kalıcı hale getirmek için iki temel yöntem sunar: volumes ve bind mounts. İkisinin de kendine göre avantajları ve dezavantajları var, seçim biraz kullanım senaryosuna bağlı.
- Volumes: Docker tarafından yönetilen dosya sistemleridir. Genellikle
/var/lib/docker/volumesaltında bulunur. Docker, volume’ları oluşturur, yönetir ve temizler. Container’lar arasında veri paylaşımı için idealdirler ve host işletim sisteminin detaylarından soyutlanmışlardır. Benim tercih ettiğim yöntem bu. Özelliklepostgres,redisgibi stateful uygulamalar için olmazsa olmaz. - Bind Mounts: Host sistemdeki belirli bir dizini doğrudan container içine bağlar. Host sistemdeki dosya ve dizinlere doğrudan erişim sağlar. Geliştirme ortamlarında, kaynak kodları container’a bağlamak gibi senaryolarda çok kullanışlıdır. Ancak, host sistemdeki path’e bağımlılık yaratır ve güvenlik riskleri taşıyabilir (container’ın host sistemdeki hassas dosyalara erişimi gibi).
Kalıcı Veri Yedekleme Stratejileri
Veri bütünlüğü sadece diskte yer açmakla bitmiyor, aynı zamanda olası bir felaket durumunda verileri geri getirebilmek de önemli. Benim setup’ımda otomatik yedekleme kritik bir rol oynuyor.
- Volume Yedekleme:
docker cpkomutunu kullanarak veya container içinden yedekleme araçları çalıştırarak volume’ları yedekleyebilirsiniz. Benimpostgresveritabanlarım için düzenlipg_dumpalıp uzak bir depolama alanına gönderen bircronjob’ım var. - Snapshot’lar: VPS sağlayıcınızın sunduğu snapshot özelliklerini kullanmak, tüm disk imajının bir yedeğini almak için hızlı ve kolay bir yoldur. Ancak bu, veritabanı tutarlılığı için yeterli olmayabilir, bu yüzden uygulamaya özel yedeklemeler hala gerekli.
Disk Alanını Akıllıca Yönetmek
Şimdi gelelim asıl konuya: bu disk alanı sorunlarıyla nasıl başa çıkıyorum? Benim için proaktif yönetim ve otomasyon anahtar kelimeler. VPS’te 13’ten fazla container’ı yönettiğimi düşünürsek, her şeyi manuel yapmak imkansız.
Düzenli Temizlik Rutinleri
Manuel olarak yapılması gereken ilk şey, Docker’ın kendi temizlik komutlarını kullanmak. docker system prune komutu, dangling image’ları, container’ları, volume’ları ve build cache’i temizlemek için tek durak bir çözüm.
# Tüm dangling container'ları, image'ları ve ağları temizler
docker system prune
# Tüm dangling ve kullanılmayan image'ları temizler (dangling olmayanlar dahil)
docker image prune -a
# Tüm dangling ve kullanılmayan volume'ları temizler
docker volume prune
Otomatik Temizlik Mekanizmaları
Manuel temizlik iyi hoş ama unutkanlık insanlık hali. Benim gibi sistem yöneticileri için otomasyon şart. Ben bu işi cron veya systemd timer’ları ile hallediyorum.
İşte benim kullandığım systemd timer ve service dosyalarına benzer bir örnek:
/etc/systemd/system/docker-prune.service:
[Unit]
Description=Clean up old Docker images, containers and volumes
Wants=network-online.target
After=network-online.target docker.service
[Service]
Type=oneshot
ExecStart=/usr/bin/docker system prune -af --volumes
/etc/systemd/system/docker-prune.timer:
[Unit]
Description=Run Docker prune weekly
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target
Bu iki dosyayı oluşturduktan sonra sudo systemctl daemon-reload, sudo systemctl enable docker-prune.timer ve sudo systemctl start docker-prune.timer komutlarıyla servisi etkinleştiriyorum. Bu sayede her hafta otomatik olarak gereksiz Docker kaynakları temizleniyor. Benim GitHub Actions self-hosted runner’ımın _work/_temp dizinleri de bu tür otomatik temizliklerden nasibini alıyor, bazen can sıkıcı olabiliyor ama disk doluluğuna tercih ederim.
Log Yönetimi
Container logları, disk doluluğunun sinsi düşmanlarından. Bir servisin hata vermeye başlamasıyla loglar birikmeye başlar ve kısa sürede diski doldurur. Bunu engellemek için docker-compose.yml dosyamda logging opsiyonlarını kullanıyorum.
version: '3.8'
services:
my_app:
image: my_app_image
ports:
- "80:80"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Bu yapılandırma, her log dosyasının boyutunu 10MB ile sınırlar ve en fazla 3 dosya tutar. Yani, bir container için maksimum 30MB log dosyası tutulur. Bu, benim VPS’imdeki log şişmelerini büyük ölçüde engelledi.
Disk Alanı İzleme ve Uyarılar
Otomatik temizlik ne kadar iyi olursa olsun, her zaman beklenmedik durumlar ortaya çıkabilir. Bu yüzden disk alanını sürekli izlemek ve kritik seviyelere ulaştığında uyarı almak çok önemli.
Ben kendi sistemlerimde Prometheus ve Grafana kullanıyorum. Node Exporter ile disk kullanımını izleyip, belirli eşik değerler aşıldığında (örneğin %80 veya %90 doluluk) Discord veya email üzerinden bildirim gönderiyorum. Bu “preflight resource guard” mekanizması, Pipeline-health monitor’ümün “DEGRADED” maili atmasından çok daha önce müdahale etmeme olanak tanıyor.
# Disk kullanım oranı %80'in üzerindeyse uyarı ver
(node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 20
Bu basit PromQL sorgusu ile disk doluluğunu izleyebilir ve Alertmanager üzerinden uyarıları yönetebilirsiniz.
Kapasite Planlaması ve Performans
Disk alanını boşaltmak kadar, gelecekteki ihtiyaçları öngörmek ve performansı optimize etmek de önemli. Özellikle Astro build’imin 2.5 GB RAM yediği ve sistemde 7.6 GB RAM olduğu bir durumda, kaynakları verimli kullanmak kritik hale geliyor.
Image Optimizasyonu
Daha küçük Docker image’ları, daha az disk alanı kaplar ve daha hızlı build edilir. Bu da hem diskten tasarruf sağlar hem de deploy süreçlerini hızlandırır.
- Multi-stage Builds: Bu teknik, build aşamasında kullanılan gereksinimleri (derleyiciler, SDK’lar vb.) nihai image’dan ayırır. Örneğin, bir Go uygulamasını build ederken, Go derleyicisini içeren bir base image kullanıp, nihai image’ı sadece çalıştırılabilir dosyayı içeren çok daha küçük bir
scratchveyaalpineimage üzerine kurabilirsiniz. - .dockerignore Dosyası:
.dockerignoredosyası,.gitignoregibi çalışır. Build context’ine dahil edilmemesi gereken dosyaları (node_modules, .git, .env vb.) belirterek image boyutunu önemli ölçüde azaltır. - Minimal Base Image’lar:
alpineveyadistrolessgibi minimal base image’ları tercih etmek, gereksiz kütüphanelerin ve araçların image’a dahil olmasını engeller.
Storage Drivers ve Performans
Docker, container’ları ve image’ları yönetmek için çeşitli depolama sürücüleri (storage drivers) kullanır. overlay2 günümüzde varsayılan ve en çok önerilen sürücüdür. Performansı ve disk alanı kullanım verimliliği açısından oldukça başarılıdır.
Eğer eski bir Docker kurulumunuz varsa veya farklı bir sürücü kullanıyorsanız, docker info komutuyla mevcut sürücünüzü kontrol edebilirsiniz. Genellikle overlay2 ile bir sorun yaşamazsınız, ancak performans sorunları yaşıyorsanız sürücüyü kontrol etmek iyi bir başlangıç noktası olabilir.
Swap Kullanımı ve Disk I/O
VPS’imde OOM senaryolarıyla çok karşılaştım. Özellikle bir container’ın bellek tüketiminin artmasıyla sistemin swap’e düşmesi ve ardından disk I/O’sunun tavan yapması, tüm sistemi kilitliyor. Geçen ay sleep 360 yazıp OOM-killed olduğumu ve polling-wait’e geçmek zorunda kaldığımı hatırlıyorum. Bu durum, disk performansının genel sistem sağlığı için ne kadar kritik olduğunu gösteriyor.
Disk I/O’sunun yüksek olması, swap kullanımını artırır ve bu da sistemi daha da yavaşlatır. Bu kısır döngüden kurtulmak için hem disk alanını yönetmek hem de bellek tüketimini optimize etmek gerekiyor. cve-2026-31431 gibi kernel zafiyetlerinde algif_aead modülünü blacklist’e almak gibi güvenlik önlemleri de, sistemin stabil çalışması için dolaylı yoldan önemlidir.
Sonuç
Docker ile kendi sunucunda operasyon yapmak, “olur o kadar” dediğimiz birçok zorluğu beraberinde getiriyor. Disk depolama savaşları da bunlardan sadece biri. Ancak doğru stratejiler, proaktif izleme ve otomasyon ile bu sorunları yönetmek mümkün. Benim kendi VPS’imde edindiğim tecrübeler gösteriyor ki, düzenli temizlik, akıllı log yönetimi ve kapasite planlaması, sistemin stabil ve performanslı çalışması için vazgeçilmez.
Bu rehberin, sizin de kendi VPS’inizde veya sunucularınızda Docker disk sorunlarıyla başa çıkmanıza yardımcı olmasını umuyorum. Unutmayın, önemli olan sorunu görüp paniklemek değil, kök nedenini anlayıp kalıcı çözümler üretmek. Sizin de bu konuda yaşadığınız benzer olaylar veya farklı çözümleriniz varsa, duymak isterim. Bir sonraki yazıda, AI generation pipeline’ımda yaşadığım bir bug’ı ve nasıl çözdüğümü anlatacağım.