Bir sabah sunucuya bağlanıp df -h yazdığında kök bölümün %98 dolu olduğunu görürsen ve suçluyu ararken /var/lib/docker/containers altında 40 GB'lık tek bir dosya bulursan, karşılaştığın şey Docker log driver yapılandırmasının hiç yapılmamış olmasıdır. Docker varsayılan olarak json-file sürücüsünü kullanır ve bu sürücünün fabrika ayarında hiçbir boyut sınırı yoktur. Konteynerin stdout'a bastığı her satır, konteyner silinene kadar diskte büyümeye devam eder.
Bu rehberde Docker'ın logları tam olarak nereye ve hangi biçimde yazdığını, hangi log driver'ın hangi senaryoya uygun olduğunu, daemon.json üzerinden sunucu genelinde rotasyonu nasıl açacağını, tek bir konteyner için ayarı nasıl ezeceğini ve syslog / uzak log sunucusu kullanırken hangi yeteneği kaybettiğini anlatacağım. Sonunda da üretimde en çok karşılaştığım tuzakları toplu hâlde göreceksin. Konteyner loglarını okuma komutlarına henüz hâkim değilsen önce Docker konteyner loglarını okuma yazısına göz atman iyi olur; buradaki her şey onun üzerine kuruluyor.
Docker Logları Aslında Nereye Yazılıyor#
Docker'ın loglama modeli tek bir kurala dayanır: konteyner içindeki ana süreç (PID 1) stdout ve stderr akışlarına ne yazarsa, Docker daemon onu yakalar ve seçili log driver'a teslim eder. Uygulaman logunu bir dosyaya (/var/log/app.log gibi) yazıyorsa Docker bundan haberdar olmaz; o dosya konteynerin yazılabilir katmanında büyür ve docker logs çıktısında hiç görünmez. Konteyner tabanlı bir uygulamanın loglarını dosyaya değil stdout'a basması gerektiğini söyleyen "12 faktör" kuralının pratik karşılığı budur.
Varsayılan json-file sürücüsünde her satır ayrı bir JSON nesnesi olarak diske yazılır. Dosyanın yerini kendi gözünle görmek istersen:
# Konteynerin log dosyasının tam yolunu al
docker inspect --format '{{.LogPath}}' web
# Örnek çıktı:
# /var/lib/docker/containers/9f2c.../9f2c...-json.log
# Dosyanın gerçek boyutu
sudo du -h "$(docker inspect --format '{{.LogPath}}' web)"
# 3.7G /var/lib/docker/containers/9f2c.../9f2c...-json.log
# İçerik biçimi: her satır bir JSON nesnesi
sudo head -n 1 "$(docker inspect --format '{{.LogPath}}' web)"
# {"log":"listening on :8080\n","stream":"stdout","time":"2026-08-25T09:12:44.118Z"}
Buradaki stream alanı satırın stdout'tan mı stderr'den mi geldiğini, time alanı ise Docker'ın satırı yakaladığı anı tutar. docker logs -t komutunun gösterdiği zaman damgası işte bu alandır — uygulamanın kendi bastığı zaman damgası değil. İki saat farkı görürsen konteynerin saat dilimi ayarına bakman gerekir; bu konuyu Docker konteynerlerde saat dilimi ve locale yazısında ayrıca ele aldım.
JSON biçiminin bir maliyeti daha var: her satır için {"log":"...","stream":"...","time":"..."} sarmalayıcısı yazıldığından, diskteki dosya ham log metninden kabaca %25-40 daha büyük olur. Yoğun log basan bir uygulamada bu fark tek başına gigabaytlarla ifade edilir.
Mevcut Log Driver'ı Görmek ve Sunucu Genelinde Değiştirmek#
Sunucunun hangi sürücüyü kullandığını öğrenmek tek komutluk iştir:
# Daemon'ın varsayılan sürücüsü
docker info --format '{{.LoggingDriver}}'
# json-file
# Tek bir konteynerin gerçekte kullandığı sürücü ve seçenekleri
docker inspect --format '{{json .HostConfig.LogConfig}}' web
# {"Type":"json-file","Config":{"max-size":"10m","max-file":"3"}}
Sunucu genelinde varsayılanı değiştirmek için /etc/docker/daemon.json dosyasını düzenlersin. Dosya yoksa oluşturursun; içeriği geçerli bir JSON olmalıdır, aksi hâlde Docker servisi hiç açılmaz.
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null << 'JSON'
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"compress": "true"
}
}
JSON
# JSON söz dizimini yeniden başlatmadan önce doğrula
sudo dockerd --validate --config-file /etc/docker/daemon.json
# Ayarları uygula
sudo systemctl restart docker
docker info --format '{{.LoggingDriver}}'
Burada üç kritik ayrıntı var. Birincisi, log-opts altındaki bütün değerler dize (string) olmalıdır: "max-file": 3 değil "max-file": "3". Sayı yazarsan daemon açılışta hata verir. İkincisi, systemctl restart docker komutu çalışan konteynerleri de yeniden başlatır; üretimde bunu bakım penceresinde yap. Üçüncüsü ve en çok atlananı: bu ayar yalnızca yeni oluşturulan konteynerleri etkiler. Mevcut konteynerler yaratıldıkları andaki log yapılandırmasını taşımaya devam eder; onlar için docker compose up -d --force-recreate ya da docker rm + yeniden run gerekir.
json-file Rotasyon Seçenekleri#
json-file sürücüsünün sunduğu seçenek kümesi küçüktür ama disk kazasını önlemeye yeter:
| Seçenek | Anlamı | Varsayılan | Pratik değer |
|---|---|---|---|
max-size | Tek log dosyasının üst sınırı | sınırsız | 10m – 50m |
max-file | Saklanacak dosya sayısı (rotasyon) | 1 | 3 – 5 |
compress | Rotasyona uğrayan dosyaları gzip'le | false | true |
labels / env | Log satırlarına etiket/ortam değişkeni ekle | boş | tanı için |
Toplam disk kullanımını max-size × max-file ile hesaplarsın. Yani 10m ve 3 ile bir konteyner en fazla 30 MB tutar. On konteynerli bir sunucuda üst sınır 300 MB olur ki bu, sınırsız büyümeye kıyasla çok daha öngörülebilir bir tablodur. compress: "true" açıkken rotasyona uğramış dosyalar .gz uzantısıyla saklanır ve metin logları genelde 8-10 kat küçülür; yalnızca en güncel dosya sıkıştırılmamış kalır.
Tek bir konteyner için ayarı komut satırından ezmek istersen:
docker run -d --name api \
--log-driver json-file \
--log-opt max-size=20m \
--log-opt max-file=5 \
--log-opt tag="{{.Name}}/{{.ID}}" \
firmaniz/api:1.4
Çok gürültülü tek bir konteyneri tamamen susturmak istersen --log-driver none kullanabilirsin — ama bunu yaptığın anda docker logs bomboş döner ve bir sorun anında elinde hiçbir kanıt kalmaz. Genelde none yerine küçük bir max-size değeri çok daha sağlıklı bir tercihtir.
local Driver: json-file'ın Daha Ucuz Kardeşi#
Docker'ın local adında ikinci bir yerel sürücüsü var ve çoğu kişi varlığından habersiz. local özel bir ikili biçim kullanır, JSON sarmalayıcısı yazmaz ve varsayılan olarak rotasyon açıktır. Yani hiçbir seçenek vermeden kullansan bile diski doldurmaz.
| Özellik | json-file | local |
|---|---|---|
| Disk biçimi | Satır başına JSON | Sıkıştırılmış ikili |
| Varsayılan boyut sınırı | Yok (sınırsız büyür) | 20m × 5 dosya |
| Varsayılan sıkıştırma | Kapalı | Açık |
docker logs desteği | Var | Var |
Dosyayı grep/tail ile okumak | Mümkün | Mümkün değil |
| Üçüncü parti araç uyumu | Yüksek | Düşük |
Seçim kuralı basittir: log dosyasını doğrudan tail -f ya da grep ile okumaya alışkınsan veya Filebeat/Promtail gibi bir ajan diskteki dosyayı okuyorsa json-file kal. Logları yalnızca docker logs üzerinden görüntülüyorsan local sürücüsü daha az disk, daha az CPU ve kutudan çıkan rotasyon demektir.
sudo tee /etc/docker/daemon.json > /dev/null << 'JSON'
{
"log-driver": "local",
"log-opts": { "max-size": "20m", "max-file": "5" }
}
JSON
sudo systemctl restart docker
Diskin şu anda log yüzünden dolduysa ve önce yer açman gerekiyorsa Docker diski doldurdu, nasıl temizlenir yazısındaki temizlik adımlarını uygula; rotasyonu açmak geçmişte birikmiş dev dosyayı geriye dönük küçültmez.
syslog, journald ve Uzak Log Sunucusuna Gönderme#
Tek sunucu ötesine geçtiğinde logları merkezî bir yerde toplamak istersin. Docker bunun için birkaç sürücü sunar; en çok kullanılanlar journald, syslog, fluentd ve gelf'tir.
# systemd journal'a yaz (RHEL/AlmaLinux ailesinde çok pratik)
docker run -d --name web --log-driver journald nginx:alpine
journalctl CONTAINER_NAME=web -f
# Uzak bir syslog toplayıcısına TCP ile gönder
docker run -d --name web \
--log-driver syslog \
--log-opt syslog-address=tcp://185.12.34.56:514 \
--log-opt syslog-facility=local0 \
--log-opt tag="{{.Name}}" \
nginx:alpine
Burada bilmen gereken en önemli kısıt şu: docker logs komutu yalnızca yerel bir okuma arayüzü olan sürücülerle çalışır. json-file, local ve journald bunu destekler; syslog, gelf, fluentd ve awslogs desteklemez. Bu sürücülerden birine geçtikten sonra docker logs web yazarsan şuna benzer bir hata alırsın:
docker logs web
# Error response from daemon: configured logging driver does not support reading
Bu bir arıza değil, tasarım gereğidir; loglar artık uzak sistemdedir. Ekipteki herkes docker logs'a alışkınsa bu geçişi haber vermeden yapmak, gece yarısı bir arıza sırasında çok kötü bir sürprize dönüşür. Ara çözüm olarak fluentd sürücüsünün --log-opt fluentd-async=true seçeneğiyle çalışmasını tercih edebilirsin: toplayıcı erişilemez olduğunda konteynerin bloke olmasını engeller. Varsayılan senkron modda toplayıcı çökerse konteynerin stdout yazma işlemi bekler ve uygulaman fiilen durur — üretimde gördüğüm en sinsi kesinti sebeplerinden biridir.
Compose Dosyasında ve Çok Konteynerli Kurulumlarda Loglama#
Compose kullanıyorsan log ayarı servis düzeyinde logging anahtarıyla tanımlanır ve daemon.json içindeki varsayılanı ezer:
services:
web:
image: nginx:alpine
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
worker:
image: firmaniz/worker:2.1
logging:
driver: local
options:
max-size: "50m" # kuyruk işçisi çok log basıyor
max-file: "5"
# Gürültülü bir yan servis: yalnızca son 5 MB yeter
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
logging:
driver: json-file
options:
max-size: "5m"
max-file: "1"
Compose dosyasında options altındaki değerlerin tırnak içinde yazılması gerekir; max-size: 10m (tırnaksız) YAML tarafında beklenmedik biçimde yorumlanabilir. Servis bazında farklı sınır vermek, tek bir gürültülü bileşenin diğerlerinin log alanını yemesini önler. Compose yapısına yabancıysan Docker Compose kullanımı yazısı temel dosya anatomisini anlatıyor.
Aynı yaklaşımı tek tek konteyner çalıştırırken de uygularsın; her docker run satırına --log-opt eklemeyi unutmamak yerine daemon.json içinde makul bir varsayılan tanımlamak ve yalnızca istisnaları elle ezmek çok daha sürdürülebilirdir.
Sık Yapılan Hatalar ve Tuzaklar#
Yıllar içinde aynı hataları defalarca gördüm; listeyi kısa ve uygulanabilir tutuyorum:
- Rotasyonu açıp mevcut konteynerleri yeniden oluşturmamak.
daemon.jsondüzenlendi, servis yeniden başlatıldı, ama üç aydır çalışan konteyner hâlâ sınırsız yazıyor.docker inspect --format '{{json .HostConfig.LogConfig}}'ile her konteyneri tek tek doğrula. logrotateile Docker log dosyalarını döndürmeye çalışmak.logrotatedosyayı taşır ya da keser; Docker ise açık dosya tanıtıcısına yazmaya devam eder. Sonuç: disk boşalmaz,docker logsbozulur. Rotasyonu Docker'ın kendisine bıraktır.- Log dosyasını
rmile silmek. Aynı sebeple çalışmaz; dosya silinse bile inode açık kaldığı için alan geri gelmez. Doğru yol konteyneri yeniden başlatmak ya datruncate -s 0uygulamaktır. max-fileveripmax-sizevermemek.max-filetek başına hiçbir işe yaramaz; rotasyonu tetikleyen şey boyut sınırıdır.- Uygulamayı
stdoutyerine dosyaya yazdırmak. Bu durumda log driver ne yaparsan yap devreye girmez, dosya konteynerin katmanında büyür vedocker system dfbile bunu net göstermez. - Hassas veriyi loglamak. Token, parola ve oturum çerezleri log satırına düştüğü anda merkezî log sistemine ve yedeklere de kopyalanır. Kod tarafında maskeleme yapmıyorsan, bunu bir güvenlik açığı olarak ele al; .env dosyası ifşası yazısındaki mantık burada da geçerlidir.
Diski ne kadar hızlı doldurduğunu ölçmek istersen kaba ama işe yarar bir yöntem var:
# Tüm konteyner log dosyalarını büyükten küçüğe sırala
sudo du -h /var/lib/docker/containers/*/*-json.log | sort -rh | head -n 10
# Bir konteynerin 60 saniyede kaç satır bastığını ölç
timeout 60 docker logs -f --since 0s web 2>&1 | wc -l
Dakikada binlerce satır basan bir uygulama varsa çözüm daha büyük disk değil, log seviyesini debug'dan info'ya çekmektir.
Sıkça Sorulan Sorular#
Docker log rotasyonu varsayılan olarak açık mı#
Hayır. Docker'ın fabrika ayarı olan json-file sürücüsünde max-size tanımsızdır, yani log dosyası konteyner silinene kadar sınırsız büyür. Rotasyonu ya daemon.json içinde max-size ve max-file vererek ya da local sürücüsüne geçerek açman gerekir. local sürücüsü varsayılan olarak 20 MB × 5 dosya sınırıyla gelir.
Log ayarını değiştirdim ama konteyner hâlâ eski davranıyor, neden#
Çünkü log yapılandırması konteyner oluşturulurken sabitlenir. daemon.json değişikliği ve systemctl restart docker yalnızca bundan sonra oluşturulacak konteynerleri etkiler. Mevcut olanları docker compose up -d --force-recreate ile veya docker rm sonrası yeniden çalıştırarak yeniden oluşturman gerekir. Doğrulamak için docker inspect --format '{{json .HostConfig.LogConfig}}' komutunu kullan.
Docker log dosyasını silmek güvenli mi#
Doğrudan rm ile silmek önerilmez. Docker daemon dosyayı açık tuttuğu için silinen dosyanın kapladığı alan geri kazanılmaz ve docker logs çıktısı bozulur. Alanı hemen boşaltman gerekiyorsa sudo truncate -s 0 "$(docker inspect --format '{{.LogPath}}' konteyner)" komutu dosyayı yerinde sıfırlar; kalıcı çözüm ise rotasyonu açmaktır.
syslog sürücüsüne geçince docker logs neden çalışmıyor#
docker logs komutu, log satırlarını yerelde okuyabilen sürücülerle çalışır: json-file, local ve journald. syslog, gelf, fluentd ve bulut sağlayıcı sürücülerinde loglar doğrudan uzak sisteme gönderildiği için yerel bir kopya kalmaz ve komut "configured logging driver does not support reading" hatası döner. Logları artık merkezî sistemin arayüzünden okursun.
Hangi log driver'ı seçmeliyim#
Tek sunucuda çalışıyor ve logları docker logs ile okuyorsan local en dengeli seçimdir: rotasyon hazır gelir, daha az disk harcar. Diskteki dosyayı bir log ajanına okutuyorsan json-file kal. Birden fazla sunucun varsa ve logları tek yerde toplamak istiyorsan journald üzerinden yerel toplama ya da fluentd ile merkezî bir toplayıcı mantıklıdır.
Log dosyaları ne kadar yer kaplayacak, nasıl hesaplarım#
Formül basittir: her konteyner için üst sınır max-size × max-file kadardır. 10m ve 3 ile bir konteyner en fazla 30 MB tutar; 20 konteynerli bir sunucuda toplam üst sınır 600 MB olur. compress açıksa rotasyona uğramış dosyalar gzip'lendiği için gerçek kullanım genelde bunun çok altında kalır. Mevcut durumu ölçmek için du -h /var/lib/docker/containers/*/*-json.log | sort -rh komutunu kullan.
Kapanış#
Docker loglaması, kurulduğu gün beş dakika ayırmazsan aylar sonra gecenin ikisinde disk dolu alarmı olarak geri dönen konulardan biridir. Aklında kalması gereken dört alışkanlık şunlar: daemon.json içinde mutlaka bir max-size ve max-file tanımla, ayarı değiştirdikten sonra konteynerleri yeniden oluşturmayı unutma, uygulamanı dosyaya değil stdout'a log basacak şekilde yapılandır ve logrotate ile Docker log dosyalarına asla dokunma. Merkezî loglamaya geçerken de docker logs'un artık çalışmayacağını ekibe önceden duyur.
Bu tür alt yapı ayarlarıyla tek başına uğraşmak istemiyorsan Clou.TR tarafında işin büyük kısmını devralabiliriz. Tam root erişimli VDS ve sanal sunucu paketlerimizde kendi Docker daemon ayarlarını özgürce yapabilir, kurulum ve izleme yükünü bize bırakmak istersen sunucu yönetimi hizmetimizden yararlanabilir, log ve veri kaybına karşı düzenli kopyalar için yedekleme çözümümüze göz atabilirsin.