← Kursa Dön
📄 Text · 30 min

Image Yönetimi — pull, push, tag, inspect, history

Bir önceki derste image'ın ne olduğunu, katmanlı yapısını ve varyantlarını öğrendik. Şimdi sıra geldi bu image'larla günlük çalışmaya — indirme, etiketleme, inceleme, paylaşma ve temizleme. Bu derste öğreneceğin komutlar, Docker ile çalışırken her gün kullanacağın temel operasyonlar.

Bir fotoğrafçı düşün. Fotoğraflarını çeker (build), etiketler (tag), albümde saklar (local storage), müşteriye gönderir (push), gerektiğinde geri alır (pull). Bazılarını siler, bazılarını inceler. Docker image yönetimi de tam bu iş akışını takip ediyor.


docker pull — Image İndirme

docker pull, Docker Hub'dan (veya başka bir registry'den) image'ı bilgisayarına indiren komut. Hadi detaylıca bakalım.

Temel Kullanım

En basit hali:

docker pull nginx

Bu komut nginx:latest tag'ini indirir. Ama bir önceki derste öğrendiğimiz gibi, latest güvenilir değil! Her zaman belirli versiyon kullanmalıyız:

docker pull nginx:1.25.4       # Belirli versiyon
docker pull node:20-alpine     # Alpine varyantı
docker pull postgres:16        # PostgreSQL 16

Pull sırasında konsoldaki çıktıya dikkat edelim:

1.25.4: Pulling from library/nginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:abc123def456...
Status: Downloaded newer image for nginx:1.25.4
docker.io/library/nginx:1.25.4

Her "Pull complete" satırı bir katman. Altı katman indirildi ve birleştirildi. En altta Digest var — bu image'ın benzersiz SHA256 hash'i. Değişmez, güvenilir bir referans.

Katman Paylaşımı — Bant Genişliği Tasarrufu

Docker'ın en güzel özelliklerinden biri katman paylaşımı. Bunu görmek için iki farklı Node.js image'ını indirelim:

# İlk image — tüm katmanlar indirilir
docker pull node:20-alpine
# a2abf6c4d29d: Pull complete
# f19e5a72315b: Pull complete
# 7c2d5c8e3a05: Pull complete
# Tamamı indirildi

# İkinci image — paylaşılan katmanlar atlanır!
docker pull node:20-slim
# a2abf6c4d29d: Already exists  ← Bu katman zaten var!
# b8c3a12e5f67: Pull complete   ← Sadece farklılar indirildi

"Already exists" mesajını gördün mü? Docker, daha önce indirdiği katmanları tekrar indirmiyor. Bu, özellikle CI/CD pipeline'larında çok önemli — her build'de aynı base image'ı tekrar tekrar indirmek yerine, sadece değişen katmanları çekiyorsun.

Platform Seçimi

Apple Silicon (M1/M2/M3) Mac kullanıyorsan bazen AMD64 image'a ihtiyacın olabilir — mesela bazı image'lar henüz ARM64 desteği eklememiştir:

# Belirli platformu zorla
docker pull --platform linux/amd64 nginx:1.25.4

# Raspberry Pi için ARM
docker pull --platform linux/arm64 nginx:1.25.4

Normalde Docker platformunu otomatik algılar ve doğru olanı çeker. Ama bazen "no matching manifest" hatası alırsan, bu flag işe yarar.

Digest ile Pull — En Güvenli Yöntem

Tag'ler değişebilir ama digest asla değişmez:

# Image'ın digest'ini bul
docker inspect nginx:1.25 --format '{{index .RepoDigests 0}}'
# nginx@sha256:6db391d1c0cfb30588ba0bf72ea999404f2764f...

# Digest ile pull — %100 aynı image garanti
docker pull nginx@sha256:6db391d1c0cfb30588ba0bf72ea999404f2764f...

Production'da maximum güvenlik istiyorsan digest kullan. Ama pratikte semantic versioning (nginx:1.25.4) çoğu senaryo için yeterli.


docker images — Image Listeleme

Bilgisayarındaki image'ları listelemek için:

docker images
# REPOSITORY   TAG          IMAGE ID       CREATED        SIZE
# nginx        1.25         a8758716bb6a   2 weeks ago    187MB
# node         20-alpine    a8c5be9b1d03   3 weeks ago    130MB
# postgres     16           f1d524c76f35   1 week ago     432MB

Filtreleme ve Formatlama

Çok sayıda image'ın varsa filtreleme çok işe yarar:

# Belirli bir repository
docker images node
# Sadece node image'ları

# Dangling image'lar (tag'siz)
docker images -f "dangling=true"

# Sadece ID'ler (scripting için)
docker images -q

# Boyuta göre sıralama — en büyükler en üstte
docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -rh
# 1.1GB   node:20
# 432MB   postgres:16
# 187MB   nginx:1.25
# 130MB   node:20-alpine

Bu son komut çok kullanışlı. "Disk neden doldu?" diye merak ettiğinde, en büyük image'ları hemen görebilirsin.

Gerçek Disk Kullanımı

docker images komutu her image'ın boyutunu gösterir ama paylaşılan katmanları sayar. Gerçek disk kullanımı farklı olabilir:

docker system df
# TYPE          TOTAL   ACTIVE  SIZE      RECLAIMABLE
# Images        15      3       4.2GB     3.1GB (73%)
# Containers    5       2       120MB     80MB (66%)
# Build Cache   -       -       2.1GB     2.1GB
# Local Volumes 8       3       1.5GB     800MB (53%)

Bu komut sana Docker'ın toplamda ne kadar disk alanı kullandığını gösterir. "Reclaimable" sütunu temizlenebilecek alanı gösterir — prune komutlarıyla bu alanı geri kazanabilirsin.


docker tag — Image Etiketleme

Tag, bir image'a yeni bir isim vermek. Çok önemli bir nokta: tag bir kopya oluşturmaz. Aynı image'a sadece yeni bir referans ekler.

docker tag nginx:1.25 my-nginx:latest
docker tag nginx:1.25 my-nginx:v1.0
docker tag nginx:1.25 my-nginx:production

Hepsini listeleyelim:

docker images | grep -E "nginx|my-nginx"
# nginx      1.25        a8758716bb6a   187MB
# my-nginx   latest      a8758716bb6a   187MB  ← Aynı ID!
# my-nginx   v1.0        a8758716bb6a   187MB  ← Aynı ID!
# my-nginx   production  a8758716bb6a   187MB  ← Aynı ID!

Dört farklı isim, hepsi aynı IMAGE ID'ye sahip. Disk'te tek bir kopya. Bu çok verimli.

Registry İçin Tag'leme

Image'ı bir registry'ye push etmeden önce, doğru formatta tag'lemen gerekir. Her registry'nin kendi formatı var:

# Docker Hub: username/repository:tag
docker tag myapp:latest tolgahan/myapp:v1.0.0

# GitHub Container Registry: ghcr.io/username/repository:tag
docker tag myapp:latest ghcr.io/tolgahan/myapp:v1.0.0

# AWS ECR: account.dkr.ecr.region.amazonaws.com/repository:tag
docker tag myapp:latest 123456789.dkr.ecr.eu-west-1.amazonaws.com/myapp:v1.0.0

# Özel registry: registry.domain.com/repository:tag
docker tag myapp:latest registry.mycompany.com/myapp:v1.0.0

Production Tag Stratejisi

Production'da her image'a birden fazla tag vermek iyi bir pratik:

VERSION="1.2.3"
COMMIT=$(git rev-parse --short HEAD)
DATE=$(date +%Y%m%d)

docker build \
    -t myapp:${VERSION} \
    -t myapp:${VERSION}-${COMMIT} \
    -t myapp:latest \
    -t myapp:${DATE} \
    .

Bu sayede:

  • myapp:1.2.3 → Versiyon numarasıyla bulabilirsin

  • myapp:1.2.3-abc123f → Hangi git commit'ten build edildiğini bilirsin

  • myapp:latest → Geliştirmede hızlıca erişebilirsin

  • myapp:20240115 → Tarih bazlı sorgulama yapabilirsin


docker inspect — Image'ı Derinlemesine İnceleme

docker inspect bir image'ın (veya container'ın) tüm metadata'sını JSON olarak verir. Bu komut bir dedektif aracı gibi — image hakkında bilmek isteyebileceğin her şey burada.

Tüm Bilgiyi Gör

docker inspect nginx:1.25 | jq .

Çıktı çok uzun olacak — yüzlerce satır JSON. Her şeyi bir arada görmek için güzel ama pratikte belirli bilgilere ihtiyacın olacak.

Spesifik Bilgi Çekme — Format Kullanımı

--format flag'i ile istediğin bilgiyi çıkarabilirsin:

# Container başladığında çalışacak varsayılan komut
docker inspect nginx:1.25 --format '{{json .Config.Cmd}}' | jq .
# ["nginx", "-g", "daemon off;"]

# Entrypoint
docker inspect nginx:1.25 --format '{{json .Config.Entrypoint}}' | jq .
# ["/docker-entrypoint.sh"]

# Açık portlar
docker inspect nginx:1.25 --format '{{.Config.ExposedPorts}}'
# map[80/tcp:{}]

# Environment variable'lar
docker inspect nginx:1.25 --format '{{json .Config.Env}}' | jq .
# ["PATH=...", "NGINX_VERSION=1.25.4", ...]

# İşlemci mimarisi
docker inspect nginx:1.25 --format '{{.Architecture}}'
# amd64

# Oluşturma tarihi
docker inspect nginx:1.25 --format '{{.Created}}'
# 2024-01-15T10:30:00.000000000Z

# Katman sayısı
docker inspect nginx:1.25 --format '{{len .RootFS.Layers}}'
# 7

# Çalışma dizini
docker inspect nginx:1.25 --format '{{.Config.WorkingDir}}'
# (boş ise /)

# Container'ın hangi kullanıcıyla çalışacağı
docker inspect nginx:1.25 --format '{{.Config.User}}'
# (boş ise root)

Bu bilgiler ne işe yarar? Mesela birinin oluşturduğu bir image'ı kullanacaksın ama dokümantasyonu yok. docker inspect ile hangi portu açıyor, hangi komutla başlıyor, hangi environment variable'ları kullanıyor — hepsini öğrenirsin.

İki Image'ı Karşılaştır

Pratik bir kullanım senaryosu: iki image'ı karşılaştırmak.

echo "=== node:20 vs node:20-alpine ==="
echo "Boyut:"
echo "  node:20:        $(docker inspect node:20 --format '{{.Size}}' | numfmt --to=iec)"
echo "  node:20-alpine: $(docker inspect node:20-alpine --format '{{.Size}}' | numfmt --to=iec)"
echo "Katman sayısı:"
echo "  node:20:        $(docker inspect node:20 --format '{{len .RootFS.Layers}}')"
echo "  node:20-alpine: $(docker inspect node:20-alpine --format '{{len .RootFS.Layers}}')"

docker history — Build Geçmişi

docker history image'ın nasıl build edildiğini, hangi komutların hangi katmanları oluşturduğunu ve her katmanın boyutunu gösterir:

docker history node:20-alpine
IMAGE          CREATED       CREATED BY                                      SIZE
a8c5be9b1d03   3 weeks ago   CMD ["node"]                                    0B
<missing>      3 weeks ago   ENTRYPOINT ["docker-entrypoint.sh"]             0B
<missing>      3 weeks ago   COPY docker-entrypoint.sh /usr/local/bin/       388B
<missing>      3 weeks ago   RUN /bin/sh -c apk add --no-cache libstdc++    1.59MB
<missing>      3 weeks ago   ENV NODE_VERSION=20.11.0                        0B
<missing>      4 weeks ago   /bin/sh -c #(nop) CMD ["/bin/sh"]               0B
<missing>      4 weeks ago   ADD alpine-minirootfs-3.19... /                 7.38MB

SIZE sütununa bak. ADD alpine-minirootfs 7.38MB (Alpine Linux'un kendisi), RUN apk add 1.59MB. Gerçek yer kaplayan komutlar bunlar.

En Büyük Katmanları Bulmak

Image neden bu kadar büyük? Hemen bulalım:

docker history myapp:v1 --format "{{.Size}}\t{{.CreatedBy}}" | sort -rh | head -5
# 500MB  COPY . /app        ← node_modules dahil mi?!
# 200MB  RUN apt-get...     ← Cache temizlenmiş mi?

Eğer COPY . /app çok büyükse, muhtemelen .dockerignore dosyası eksik ve node_modules gibi gereksiz dosyalar image'a girmiş.

Tam Komutları Görmek

# --no-trunc ile komutlar kısaltılmaz
docker history --no-trunc node:20-alpine

docker push — Image Paylaşma

Image'ını bir registry'ye göndermek için docker push kullanırsın. Bu, başkalarının senin image'ını pull edebilmesini veya CI/CD pipeline'ının production'a deploy edebilmesini sağlar.

Docker Hub'a Push

# 1. Giriş yap
docker login
# Username: tolgahan
# Password: ********
# Login Succeeded

# 2. Image'ı Docker Hub formatında tag'le
docker tag myapp:latest tolgahan/myapp:v1.0.0

# 3. Push et
docker push tolgahan/myapp:v1.0.0
# The push refers to repository [docker.io/tolgahan/myapp]
# abc123: Pushed
# def456: Pushed
# ghi789: Mounted from library/node  ← Zaten Hub'da var!
# v1.0.0: digest: sha256:... size: 1234

"Mounted from library/node" mesajına dikkat! Bu katman Docker Hub'da zaten var (çünkü node:20-alpine official image'ının bir parçası). Docker, var olan katmanları tekrar yüklemez — sadece yeni katmanlarını push eder.

Birden Fazla Tag Push

docker push tolgahan/myapp:v1.0.0
docker push tolgahan/myapp:v1.0
docker push tolgahan/myapp:latest

İlk push yavaş olabilir ama sonrakiler çok hızlı — çünkü katmanlar zaten yüklenmiş, sadece tag ekleniyor.

Farklı Registry'lere Push

# GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
docker push ghcr.io/tolgahan/myapp:v1

# AWS ECR
aws ecr get-login-password --region eu-west-1 | \
    docker login --username AWS --password-stdin 123456789.dkr.ecr.eu-west-1.amazonaws.com
docker push 123456789.dkr.ecr.eu-west-1.amazonaws.com/myapp:v1

Image Temizleme — Disk Dolmasını Önle

Docker zamanla disk'i doldurur. Eski image'lar, durmuş container'lar, build cache — hepsi birikir. Düzenli temizlik çok önemli.

Manuel Temizlik Komutları

# Belirli image'ı sil
docker rmi nginx:1.25

# Dangling image'ları sil (tag'siz, havada kalan)
docker image prune -f

# Kullanılmayan TÜM image'ları sil
docker image prune -a
# ⚠️ Dikkat: çalışan container'lar hariç her image silinir

# 24 saatten eski olanları sil
docker image prune -a --filter "until=24h"

# Toplu temizlik — her şey (image + container + network + cache)
docker system prune -a --volumes
# ⚠️ Nuclear option! Kullanılmayan her şeyi siler

Otomatik Temizlik Script'i

Production sunucularında haftalık çalışacak bir temizlik script'i hazırlamak iyi bir pratik:

#!/bin/bash
# docker-cleanup.sh — Haftalık çalıştır
echo "=== Docker Temizlik Başlıyor === $(date)"
echo "Önceki disk kullanımı:"
docker system df

docker container prune -f
docker image prune -f
docker network prune -f
docker builder prune --filter "until=168h" -f

echo "Sonraki disk kullanımı:"
docker system df
echo "=== Temizlik Tamamlandı ==="
# Cron'a ekle (her Pazar gece 2'de)
crontab -e
# 0 2 * * 0 /path/to/docker-cleanup.sh >> /var/log/docker-cleanup.log 2>&1

Image Save ve Load — Offline Transfer

İnterneti olmayan ortamlara (güvenlik nedeniyle izole ağlar, askeri sistemler, uçak, vb.) image taşımak gerekebilir:

# Image'ı sıkıştırılmış dosyaya kaydet
docker save nginx:1.25 | gzip > nginx-1.25.tar.gz

# Birden fazla image tek dosyada
docker save nginx:1.25 postgres:16 redis:7 | gzip > all-images.tar.gz

# Dosyadan image yükle
gunzip -c nginx-1.25.tar.gz | docker load
# Loaded image: nginx:1.25

USB'ye kopyala, internet olmayan sunucuya götür, docker load ile yükle. Basit ve etkili.

⚠️ save/load ile export/import'u karıştırma! save image'ı kaydet (katmanlar + metadata dahil). export container'ın dosya sistemini kaydet (metadata kayıp). Image transferi için her zaman save/load kullan.


Yaygın Hatalar ve Çözümleri

"image is referenced in multiple repositories"

docker rmi a8758716bb6a
# Error: unable to delete — image is referenced in multiple repositories

Aynı image'a birden fazla tag verilmiş. Docker, referans kaldırılmadan image'ı silmez. Çözüm:

# Tag'lerle sil
docker rmi nginx:1.25 my-nginx:latest
# veya zorla sil
docker rmi -f a8758716bb6a

"denied: requested access to the resource is denied"

docker push myapp:v1
# denied: requested access to the resource is denied

İki olası neden var. Birincisi, Docker Hub'a giriş yapmamışsındır — docker login çalıştır. İkincisi, image tag'i kullanıcı adınla eşleşmiyor. myapp:v1 yerine tolgahan/myapp:v1 olmalı — Docker Hub'a push ederken namespace (kullanıcı adı) gerekli.

"manifest unknown" — Tag Bulunamadı

docker pull myapp:v2.0
# manifest for myapp:v2.0 not found

Bu tag registry'de mevcut değil. Ya yanlış yazdın, ya da o versiyon henüz push edilmemiş. Docker Hub web sayfasından mevcut tag'leri kontrol et.


Bu Derste Ne Öğrendik?

  • docker pull image indirir. Katmanlar paylaşımlı — zaten var olanlar tekrar indirilmez. Bu ciddi bant genişliği ve disk tasarrufu sağlar.

  • docker tag image'a yeni referans ekler. Kopya oluşturmaz — aynı image'a birden fazla isim takabilirsin.

  • docker inspect ile image'ın tüm metadata'sına erişirsin — CMD, ENV, PORT, katmanlar, mimari. Dokümantasyonu olmayan image'ları anlamak için vazgeçilmez.

  • docker history build geçmişini ve katman boyutlarını gösterir. Image neden büyük? Bu komutla hemen anlarsın.

  • docker push image'ı registry'ye gönderir. Tag formatı registry'ye göre ayarlanmalı.

  • docker save/load offline image transferi sağlar. Internet olmayan ortamlar için kullan.

  • Düzenli temizlik (docker image prune, docker system prune) yapmazsan disk dolar. Haftalık temizlik alışkanlığı edin.

Bir sonraki derste Docker Hub ve alternatif registry'leri detaylıca inceleyeceğiz — nasıl giriş yapılır, official image nedir, kendi registry'ni nasıl kurarsın.