İmaj ve Konteyner Kavramları: Docker’ın Temel Yapı Taşları

Docker ile çalışmaya başladığınızda sizi karşılayan iki temel kavram var: imaj ve konteyner. Bu ikisini tam anlamıyla kavramadan Docker ile verimli çalışmak neredeyse imkansız. Yıllarca “konteyner çalıştırdım, oldu” diyerek geçiştirebilirsiniz ama bir gün production ortamında bir şeyler ters gittiğinde, ya da disk dolmaya başladığında, ya da “bu imaj nereden geldi, içinde ne var?” sorusunu sormak zorunda kaldığınızda, temel kavramları bilmemenin acısını yaşarsınız. Bu yazıda Docker’ın iki temel yapı taşını gerçek dünya senaryolarıyla birlikte ele alacağız.

İmaj Nedir? Dondurulmuş Bir An

Docker imajını bir şablon olarak düşünebilirsiniz. Daha teknik bir benzetme yapmak gerekirse, imaj bir read-only dosya sistemi katmanlar topluluğudur. Çalışmaz, değiştirilemez, sadece vardır. İşletim sistemi dosyaları, uygulama kodları, kütüphaneler, ortam değişkenleri ve başlangıç komutları bu katmanlar içinde bulunur.

Bir Ubuntu imajı düşünün. Bu imaj içinde Ubuntu’nun temel dosya sistemi var, paket yöneticisi var, sistem kütüphaneleri var. Ama çalışmıyor, CPU kullanmıyor, RAM tüketmiyor. Sadece diskte duruyor.

# Mevcut imajları listele
docker images

# Ya da daha uzun formatta
docker image ls

# Belirli bir imajı ara
docker images | grep ubuntu

Bu komutu çalıştırdığınızda şunları görürsünüz: imaj adı, tag, imaj ID’si, oluşturulma tarihi ve boyutu. Burada tag kavramı önemli. ubuntu:22.04 dediğinizde ubuntu imajının 22.04 versiyonunu çekiyorsunuz. Tag belirtmezseniz Docker otomatik olarak latest tagini kullanır, ki bu production ortamlarında ciddi bir sorun haline gelebilir.

Katmanlı Yapı: Neden Önemli?

Docker imajlarının en güçlü özelliklerinden biri katmanlı mimarisidir. Her Dockerfile komutu yeni bir katman oluşturur ve bu katmanlar önbelleğe alınır. Aynı temel katmanı kullanan birden fazla imajınız varsa, o temel katman diskte sadece bir kez saklanır.

# Bir imajın katmanlarını incele
docker history nginx:latest

# Daha detaylı çıktı için
docker image inspect nginx:latest

# Sadece katman ID'lerini görmek için
docker image inspect nginx:latest --format='{{range .RootFS.Layers}}{{println .}}{{end}}'

Gerçek bir senaryo: Şirketinizde 10 farklı Python uygulaması çalıştırıyorsunuz. Hepsi python:3.11-slim temel imajını kullanıyor. Bu temel imaj diskte sadece bir kez tutuluyor. Sadece her uygulamanın kendine özgü katmanları ek yer kaplıyor. Bu sayede hem disk tasarrufu sağlıyorsunuz hem de imaj çekme süreleri dramatik şekilde kısalıyor.

Konteyner Nedir? İmajın Hayat Bulması

Konteyner, bir imajın çalışan örneğidir. İmajı alır, üzerine yazılabilir bir katman ekler ve çalıştırırsınız. Bu yazılabilir katman sayesinde konteyner içinde dosya oluşturabilir, değiştirebilir, silebilirsiniz. Ama dikkat: konteyner silindiğinde bu yazılabilir katman da gider. Kalıcılık istiyorsanız volume kullanmanız gerekiyor, buna ileride değineceğiz.

# Bir konteyner başlat
docker run ubuntu:22.04 echo "Merhaba Dünya"

# İnteraktif mod - terminal ile
docker run -it ubuntu:22.04 bash

# Arka planda çalıştır
docker run -d nginx:latest

# İsim vererek çalıştır
docker run -d --name web-sunucu nginx:latest

docker run komutunu çalıştırdığınızda arka planda şunlar oluyor: Docker önce imajın yerel olarak mevcut olup olmadığını kontrol ediyor, yoksa Docker Hub’dan çekiyor, ardından imajdan yeni bir konteyner oluşturuyor ve başlatıyor.

Konteyner Yaşam Döngüsü

Bir konteyner birkaç farklı durumda bulunabilir:

  • created: Oluşturuldu ama henüz başlatılmadı
  • running: Çalışıyor
  • paused: Duraklatıldı, process’ler donduruldu
  • stopped/exited: Durduruldu
  • dead: Bir hata nedeniyle ölü durumda
# Çalışan konteynerleri listele
docker ps

# Tüm konteynerleri listele (durdurulmuşlar dahil)
docker ps -a

# Konteyner başlat, durdur, yeniden başlat
docker start web-sunucu
docker stop web-sunucu
docker restart web-sunucu

# Konteyneri sil
docker rm web-sunucu

# Çalışan bir konteyneri zorla sil
docker rm -f web-sunucu

Sysadmin olarak en sık karşılaştığım durumlardan biri: sunucuda onlarca exited durumda konteyner birikmiş, disk dolmaya başlamış. Yeni başlayanlar docker ps çalıştırıyor, hiçbir şey görmüyor, “temiz” sanıyor. Oysa docker ps -a ile bakınca onlarca ölü konteyner çıkıyor ortaya.

# Tüm durdurulmuş konteynerleri tek seferde temizle
docker container prune

# Onay sormadan temizle
docker container prune -f

İmaj ve Konteyner Arasındaki Farkı Somutlaştırmak

Şöyle bir analoji yapayım: İmaj bir ISO dosyası gibidir. Ubuntu’nun ISO’sunu indirdiniz, diskte duruyor. Konteyner ise bu ISO’dan boot edilmiş, çalışan bir sanal makine gibi. ISO’yu silmediğiniz sürece istediğiniz kadar yeni sanal makine açabilirsiniz. Bir sanal makineyi silseniz de ISO yerinde durmaya devam eder.

# Aynı imajdan birden fazla konteyner oluştur
docker run -d --name web1 -p 8080:80 nginx:latest
docker run -d --name web2 -p 8081:80 nginx:latest
docker run -d --name web3 -p 8082:80 nginx:latest

# Üç konteyner de çalışıyor, ama imaj hala bir tane
docker images | grep nginx
docker ps | grep nginx

Bu üç konteyner de aynı nginx imajını kullanıyor. Disk üzerinde nginx dosyaları üç kez değil, bir kez bulunuyor. Her konteyner sadece kendi değişikliklerini (log dosyaları, geçici dosyalar vs.) ayrı ayrı saklıyor.

Docker Hub ve İmaj Yönetimi

İmajlar nereden geliyor? Büyük çoğunlukla Docker Hub‘dan. Docker’ın resmi imaj deposu. Ama kurumsal ortamlarda genellikle özel bir registry kullanılır: Harbor, AWS ECR, GitLab Container Registry bunların başında geliyor.

# Docker Hub'dan imaj çek
docker pull nginx:1.25-alpine

# Belirli bir platformu hedefleyerek çek
docker pull --platform linux/amd64 nginx:latest

# İmaj hakkında detaylı bilgi
docker image inspect nginx:1.25-alpine

# İmajı bir dosyaya kaydet (offline transfer için kullanışlı)
docker save nginx:1.25-alpine > nginx-1.25-alpine.tar

# Kaydedilen imajı yükle
docker load < nginx-1.25-alpine.tar

docker save ve docker load kombinasyonu gerçek hayatta çok işe yarıyor. İnternete erişimi kısıtlı, air-gapped bir ortamda çalışıyorsanız veya imajları bir ortamdan diğerine taşımanız gerekiyorsa bu yöntem hayat kurtarıcı oluyor.

İmaj Etiketleme

# Mevcut bir imaja yeni tag ekle
docker tag nginx:latest my-registry.company.com/nginx:v1.0

# Özel registry'ye gönder
docker push my-registry.company.com/nginx:v1.0

# İmajı sil
docker rmi nginx:latest

# Kullanılmayan tüm imajları temizle
docker image prune

# Tüm kullanılmayan imajları temizle (tagged olanlar dahil)
docker image prune -a

Dockerfile: Kendi İmajınızı Oluşturmak

Var olan imajları kullanmak yetmez, kendi uygulamanız için özel imaj oluşturmanız gerekir. Bunun için Dockerfile yazarsınız.

# Temel imaj
FROM python:3.11-slim

# Çalışma dizini oluştur
WORKDIR /app

# Bağımlılık dosyalarını kopyala
COPY requirements.txt .

# Bağımlılıkları yükle
RUN pip install --no-cache-dir -r requirements.txt

# Uygulama kodunu kopyala
COPY . .

# Port tanımla
EXPOSE 8000

# Başlangıç komutu
CMD ["python", "app.py"]
# Dockerfile'dan imaj oluştur
docker build -t my-python-app:1.0 .

# Farklı bir Dockerfile kullan
docker build -f Dockerfile.prod -t my-python-app:prod .

# Build argümanı geçir
docker build --build-arg APP_VERSION=1.0 -t my-python-app:1.0 .

# Build cache'i kullanmadan oluştur
docker build --no-cache -t my-python-app:fresh .

Burada dikkat edilmesi gereken bir nokta var: Dockerfile’daki komut sırası önemli. En sık değişen katmanları en sona koyun. Yukarıdaki örnekte önce requirements.txt kopyalanıp bağımlılıklar yükleniyor, sonra uygulama kodu kopyalanıyor. Böylece sadece uygulama kodu değiştiğinde, bağımlılık katmanı önbellekten kullanılıyor ve build süresi kısalıyor.

Konteyner İçini İncelemek

Bir konteyner çalışırken içine girmeniz, log bakmanız veya kaynak kullanımını izlemeniz gerekebilir.

# Çalışan konteynere bağlan
docker exec -it web-sunucu bash

# Tek komut çalıştır
docker exec web-sunucu cat /etc/nginx/nginx.conf

# Konteyner loglarını gör
docker logs web-sunucu

# Logları canlı takip et
docker logs -f web-sunucu

# Son 100 satır log
docker logs --tail 100 web-sunucu

# Zaman damgasıyla logları gör
docker logs -t web-sunucu

# Konteyner kaynak kullanımını izle
docker stats web-sunucu

# Tüm konteynerlerin kaynak kullanımı
docker stats

docker exec komutu production ortamında çok kullanışlı ama aynı zamanda dikkatli olmayı gerektiriyor. Konteyner içinde yaptığınız değişiklikler geçici. Konteyner yeniden başladığında tüm değişiklikler gidiyor. Bu yüzden “hızlı düzeltme” için konteyner içinde bir şeyler değiştirmek yerine, imajı güncelleyip yeni konteyner ayağa kaldırmak daha doğru yaklaşım.

Gerçek Dünya Senaryosu: Bir Web Uygulaması Deploy Etmek

Şimdi tüm öğrendiklerimizi bir araya getirelim. Diyelim ki bir Node.js uygulamasını Docker ile deploy edeceksiniz.

# 1. Node.js imajını çek
docker pull node:20-alpine

# 2. Uygulama için Dockerfile oluştur (yukarıdaki gibi)
# 3. İmajı build et
docker build -t my-nodeapp:1.0 .

# 4. Test için çalıştır
docker run -d 
  --name nodeapp-test 
  -p 3000:3000 
  -e NODE_ENV=production 
  -e DB_HOST=192.168.1.10 
  my-nodeapp:1.0

# 5. Logları kontrol et
docker logs nodeapp-test

# 6. Uygulamanın çalıştığını doğrula
curl http://localhost:3000/health

# 7. Sorun yoksa production'a geç
docker stop nodeapp-test
docker rm nodeapp-test
docker run -d 
  --name nodeapp-prod 
  -p 80:3000 
  --restart unless-stopped 
  -e NODE_ENV=production 
  my-nodeapp:1.0

--restart unless-stopped parametresi önemli. Sunucu yeniden başladığında veya konteyner bir hata nedeniyle çöktüğünde Docker otomatik olarak konteyneri başlatıyor.

İmaj Temizliği: Disk Yönetimi

Uzun süre Docker kullandıktan sonra diskinizdeki imaj ve konteyner birikintisi ciddi bir sorun haline gelebilir. Özellikle CI/CD pipeline’larında her build yeni imajlar oluşturuyorsa, disk dolma hızı şaşırtıcı olabiliyor.

# Docker'ın ne kadar disk kullandığını gör
docker system df

# Daha detaylı bilgi
docker system df -v

# Kullanılmayan her şeyi temizle
# (durdurulmuş konteynerler, kullanılmayan imajlar, network'ler, build cache)
docker system prune

# Hacimler dahil her şeyi temizle - DİKKATLİ KULLANIN
docker system prune --volumes

# Belirli bir tarihten eski imajları sil
docker image prune -a --filter "until=72h"

docker system prune komutunu production ortamında kullanmadan önce iki kez düşünün. Çalışan konteynerleri etkilemez ama durdurulmuş konteynerleri ve kullanılmayan imajları siler. Bir konteyner geçici olarak durdurulmuşsa ve tekrar başlatılması planlanıyorsa, bu komut beklenmedik sonuçlar doğurabilir.

Güvenlik Perspektifinden İmaj Seçimi

Sysadmin gözüyle imaj seçimi yaparken güvenliği de göz önünde bulundurmanız gerekiyor.

  • Resmi imajları tercih edin: nginx, postgres, redis gibi Docker tarafından onaylanmış imajlar daha güvenilir
  • Alpine tabanlı imajlar daha küçük ve daha az saldırı yüzeyi sunuyor: nginx:alpine vs nginx:latest
  • Tag olarak latest kullanmaktan kaçının: latest her zaman en son versiyonu getirmez ve tekrarlanabilirlik sağlamaz
  • Distroless imajlar üretim için değerlendirilebilir: Google’ın distroless imajları, kabuk ve paket yöneticisi içermiyor, saldırı yüzeyini minimize ediyor
  • İmaj tarama araçları kullanın: Trivy, Snyk gibi araçlar imajlardaki güvenlik açıklarını tespit ediyor
# Trivy ile imaj güvenlik taraması
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock 
  aquasec/trivy:latest image nginx:latest

# İmajı çekmeden, sadece manifest üzerinden tara
docker run --rm aquasec/trivy:latest image --input nginx-1.25-alpine.tar

Konteyner ve Sanal Makine Farkı

Bu konuya değinmeden geçmek olmaz. Konteynerler sanal makine değil. Sanal makineler tam bir işletim sistemi çalıştırır, her birinin kendi kernel’i vardır. Konteynerler ise host sistem kernel’ini paylaşır. Bu şu anlama geliyor:

  • Konteynerler çok daha hızlı başlar (milisaniyeler vs. dakikalar)
  • Çok daha az kaynak tüketir
  • Ama tam anlamıyla izolasyon sağlamaz

Sysadmin olarak şunu net söyleyeyim: Güçlü izolasyon gerektiren senaryolarda (farklı müşterilerin iş yüklerini ayırmak gibi), konteynerlere ek olarak VM veya Kata Containers gibi güvenli konteyner çözümleri düşünülmeli.

Sonuç

Docker imaj ve konteyner kavramları kulağa basit geliyor ama üzerine inşa edilen her şeyin temeli bunlar. İmaj değişmez bir şablondur, konteyner ise o şablonun çalışan, geçici bir örneğidir. Bu ayrımı içselleştirdiğinizde “neden konteyner içinde yaptığım değişiklikler kayboldu?” veya “disk neden doldu?” gibi sorular kendiliğinden cevap buluyor.

Pratik tavsiyem şu: Sadece okuyarak değil, terminal açıp bu komutları çalıştırarak öğrenin. Birkaç imaj çekin, inceleyin, konteyner başlatın, içine girin, silin. Docker’ın güzelliği burada: Bir şeyleri bozarsanız, silip yeniden başlayabilirsiniz. Production dışında bol bol deney yapın. Bu temel kavramları sağlamlaştırdıktan sonra Docker Compose, networking, volume yönetimi ve Kubernetes gibi konular çok daha kolay oturacak.

Bir sonraki yazıda Docker ağ yapılandırmasını ve konteynerler arası iletişimi ele alacağız. O zamana kadar terminal şimdilik boş durmasın.

Yorum yapın