---
title: 'İmaj Depoları'
source: 'https://academia.sh/tr/kurslar/konteynerler/imaj-depolari'
course: Konteynerler
language: tr
updated: '2026-08-17T18:09:04+00:00'
license: 'CC BY-SA 4.0'
---

# İmaj Depoları

Katman paylaşımlı bir imaj deposu gerçek dosyalarla kurulur ve diskten ölçülür: paylaşımın kaldırdığı bayt, saklama kuralı uygulandığında referans sayımı yüzünden silinemeyen katmanlar, yerelde bulunan katman atlandığında düşen çekme trafiği ve genel ile özel imajların paylaştığı katman sayısı sayılır.

Önceki ders etiketi bir katman listesine bağladı ve listedeki her satırın karşılığının bir yerde
durduğunu varsaydı. O yer bu dersin konusudur: **imaj deposu**. Depo, M22/K01'de ölçülen yapı
çıktısı deposunun katmanlı biçimidir — aynı iki işlevi, içerik saklamayı ve ad çözümünü görür,
farkı sakladığı nesnenin tek bir dosya değil bir katman kümesi olmasıdır. Bu fark her ölçüyü
değiştirir: büyüme, silme ve indirme artık nesne başına değil, katman başına hesaplanır.

**IM37.** Depo bu derste **gerçek dosyalarla** kurulur: her katman kendi içerik özetiyle
adlandırılmış gerçek bir dosyadır, her manifest gerçek bir dosyadır ve ölçülen baytların hepsi
diskten okunur. Ölçek 1/1000'dir — önceki derslerin 34,5 MB'lik imajı burada 34,5 KB'lik gerçek
dosyalarla kurulur. **IM38.** Derleme geçmişi önceki dersin üretecinden gelir; tohum `20260604`
görünürdür ve aynı 20 derleme, aynı 17 ayrı imaj çıkar. **IM39.** İmajların bir bölümü özel
depoya konur; bu ayrım ikinci bir üreteçten gelir, tohumu `20260812`'dir.

## Depo Neyi Saklar

İçerik adresli depoda bir katman dosyasının adı kendi özetidir. Bu tek kural depolamanın
tamamını belirler: aynı içerik ikinci kez yazıldığında aynı ada yazılır, yani yeni yer tutmaz.

```js
// olcum-agi/depo.mjs — katman paylasimli imaj deposu: gercek dosyalar, gercek baytlar
import { createHash } from "node:crypto";
import { mkdirSync, rmSync, writeFileSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";

export const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-depo");
// Olcek 1/1000: onceki derslerin 34,5 MB imaji burada 34,5 KB'lik gercek dosyalarla kurulur.
export const KATMAN = [["temel", 10500, 0.08], ["bagimliliklar", 16000, 0.25],
  ["uygulama", 7200, null], ["yapilandirma", 800, 0.30]];

export const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
export const ozetle = (b) => createHash("sha256").update(b).digest("hex").slice(0, 12);

export const gecmis = (n = 20, tohum = 20260604, gizliTohum = 20260812) => {
  const r = uretec(tohum), g = uretec(gizliTohum);
  const surum = [1, 1, 1, 1], liste = [];
  let gun = 0;
  for (let no = 1; no <= n; no++) {
    gun += 1 + Math.round(r() * 8);
    if (r() > 0.25) surum[2] += 1;
    KATMAN.forEach(([, , p], k) => { if (p !== null && r() < p) surum[k] += 1; });
    r(); r();                        // onceki dersin iki alani: cizelge ayni kalsin diye cekilir
    liste.push({ no, gun, gizli: g() < 0.35,
      katmanlar: KATMAN.map(([ad, bayt], k) => ({ ad, bayt, surum: surum[k] })) });
  }
  return liste;
};

// Depo icerik adreslidir: katman dosyasinin adi kendi ozetidir, ayni katman ikinci kez yazilmaz.
export const depoyuKur = (G) => {
  rmSync(KOK, { recursive: true, force: true });
  mkdirSync(join(KOK, "katman"), { recursive: true });
  mkdirSync(join(KOK, "manifest"), { recursive: true });
  for (const d of G) {
    d.ozetler = d.katmanlar.map(({ ad, bayt, surum }) => {
      const icerik = Buffer.alloc(bayt, `${ad}@${surum}|`);
      const o = ozetle(icerik);
      writeFileSync(join(KOK, "katman", o), icerik);
      return o;
    });
    d.imaj = ozetle(d.ozetler.join("|"));
    writeFileSync(join(KOK, "manifest", d.imaj), d.ozetler.join("\n"));
  }
  return G;
};

export const dizinBayt = (yol) =>
  readdirSync(yol).reduce((t, f) => t + statSync(join(yol, f)).size, 0);
export const kb = (b) => (b / 1000).toFixed(1) + " KB";

if (import.meta.url === `file://${process.argv[1]}`) {
  const G = depoyuKur(gecmis());
  const gercek = dizinBayt(join(KOK, "katman"));
  const paylasimsiz = G.reduce((t, d) => t + d.katmanlar.reduce((s, k) => s + k.bayt, 0), 0);
  const ayriKatman = readdirSync(join(KOK, "katman")).length;
  const ayriImaj = readdirSync(join(KOK, "manifest")).length;
  console.log(`${G.length} derleme, ${ayriImaj} ayri manifest, ${G.length * 4} katman referansi`);
  console.log(`ayri katman dosyasi: ${ayriKatman}`);
  console.log(`depo gercek (diskten olculdu): ${kb(gercek)}`);
  console.log(`paylasimsiz olsaydi          : ${kb(paylasimsiz)}`);
  console.log(`paylasimin kaldirdigi        : ${kb(paylasimsiz - gercek)} ` +
    `(%${(100 * (1 - gercek / paylasimsiz)).toFixed(1)})`);
  const sayac = new Map();
  for (const d of G) for (const o of new Set(d.ozetler)) sayac.set(o, (sayac.get(o) ?? 0) + 1);
  const sirali = [...sayac.values()].sort((a, b) => b - a);
  console.log(`katman basina referans: en cok ${sirali[0]}, ortanca ` +
    `${sirali[Math.floor(sirali.length / 2)]}, bir kez referansli ` +
    `${sirali.filter((v) => v === 1).length}/${ayriKatman}`);
}
```

```
20 derleme, 17 ayri manifest, 80 katman referansi
ayri katman dosyasi: 27
depo gercek (diskten olculdu): 176.2 KB
paylasimsiz olsaydi          : 690.0 KB
paylasimin kaldirdigi        : 513.8 KB (%74.5)
katman basina referans: en cok 16, ortanca 2, bir kez referansli 11/27
```

Seksen katman referansı diskte **yirmi yedi** dosyaya karşılık gelir. Depo 176,2 KB'dir;
her imaj kendi katmanlarını taşısaydı 690,0 KB olurdu. Paylaşım baytların **yüzde 74,5'ini**
ortadan kaldırmıştır. Kaldırılan 513,8 KB, önceki dersin taşıma ölçümündeki değişmeyen bayt
toplamıyla aynı büyüklüktür — iki ölçüm aynı olgunun iki yüzüdür.

Referans dağılımı asıl yapıyı gösterir: bir katman **on altı** imajda geçer, ortanca iki, ama
yirmi yedi katmanın **on biri** yalnız tek bir imajda geçer. Depo homojen değildir; bir ucunda
neredeyse hiç değişmeyen alt katmanlar, öbür ucunda her derlemede yenilenen üst katmanlar durur.
Bu dağılım bir sonraki ölçümün de kaynağıdır.

## Silme Referans Sayımına Çarpınca

M22/K01 saklama kuralını bayt, geri alma penceresi ve ulaşılamaz dağıtım üzerinden taramıştı;
o tarama tekrarlanmaz. Katmanlı depoda kuralın önünde yeni bir engel vardır: **bir imajın
silinmesi katmanlarının silinmesi demek değildir.** Katman ancak hiçbir manifest onu referans
etmiyorsa silinebilir.

**IM40.** Saklama kuralı sayıya bağlıdır — "son K imaj tutulur" — ve K, ayrı manifestler üzerinden
sayılır. Tarama önce hesaplanır, sonra bir kural **gerçekten uygulanır**: dosyalar silinir ve dizin
yeniden ölçülür.

```js
// olcum-agi/silme.mjs — saklama kurali katman referansina carpinca ne siliniyor (gercek dosyalar)
import { readdirSync, statSync, unlinkSync } from "node:fs";
import { join } from "node:path";
import { KOK, gecmis, depoyuKur, dizinBayt, kb } from "./depo.mjs";

const G = depoyuKur(gecmis());
const ayriImajlar = [];                       // yapim sirasina gore ayri manifestler
for (const d of G) if (!ayriImajlar.some((x) => x.imaj === d.imaj)) ayriImajlar.push(d);
const bayt = (o) => statSync(join(KOK, "katman", o)).size;
const tumKatmanlar = readdirSync(join(KOK, "katman"));
const toplamDepo = dizinBayt(join(KOK, "katman"));

console.log("kural".padEnd(14) + "tutulan".padEnd(10) + "referansli".padEnd(13) +
  "silinebilir".padEnd(13) + "bosalan".padEnd(11) + "kalan depo");
for (const K of [3, 5, 8, 12, 17]) {
  const tutulan = ayriImajlar.slice(-K);
  const referansli = new Set(tutulan.flatMap((d) => d.ozetler));
  const silinebilir = tumKatmanlar.filter((o) => !referansli.has(o));
  const bosalan = silinebilir.reduce((t, o) => t + bayt(o), 0);
  console.log(`son ${K} imaj`.padEnd(14) + `${K}/${ayriImajlar.length}`.padEnd(10) +
    `${referansli.size}/${tumKatmanlar.length}`.padEnd(13) +
    `${silinebilir.length}/${tumKatmanlar.length}`.padEnd(13) +
    kb(bosalan).padEnd(11) + kb(toplamDepo - bosalan));
}

// "son 8 imaj" kurali gercekten uygulanir: dosyalar silinir, dizin yeniden olculur.
const tutulan = ayriImajlar.slice(-8);
const referansli = new Set(tutulan.flatMap((d) => d.ozetler));
const boyut = new Map(tumKatmanlar.map((o) => [o, bayt(o)]));
const dusen = ayriImajlar.slice(0, -8);
const dusenKatman = new Set(dusen.flatMap((d) => d.ozetler));
const oncesi = dizinBayt(join(KOK, "katman"));
let silinen = 0;
for (const o of tumKatmanlar) if (!referansli.has(o)) { unlinkSync(join(KOK, "katman", o)); silinen++; }
for (const d of dusen) unlinkSync(join(KOK, "manifest", d.imaj));
const sonrasi = dizinBayt(join(KOK, "katman"));

const yasayan = [...dusenKatman].filter((o) => referansli.has(o));
const dusenBayt = [...dusenKatman].reduce((t, o) => t + boyut.get(o), 0);
console.log(`\n"son 8 imaj" uygulandi: ${dusen.length} manifest ve ${silinen} katman dosyasi silindi`);
console.log(`katman dizini ${kb(oncesi)} -> ${kb(sonrasi)} (diskten olculdu): ` +
  `${ayriImajlar.length} imajin ${dusen.length}'u dustu, bayt %${(100 * (oncesi - sonrasi) / oncesi).toFixed(0)} azaldi`);
console.log(`dusen imajlarin ${dusenKatman.size} ayri katmanindan ${yasayan.length} tanesi hala ` +
  `referanslidir: ${kb(dusenBayt)} baytin ${kb(yasayan.reduce((t, o) => t + boyut.get(o), 0))} kadari silinemedi`);
```

```
kural         tutulan   referansli   silinebilir  bosalan    kalan depo
son 3 imaj    3/17      7/27         20/27        126.5 KB   49.7 KB
son 5 imaj    5/17      10/27        17/27        111.3 KB   64.9 KB
son 8 imaj    8/17      13/27        14/27        102.5 KB   73.7 KB
son 12 imaj   12/17     20/27        7/27         56.1 KB    120.1 KB
son 17 imaj   17/17     27/27        0/27         0.0 KB     176.2 KB

"son 8 imaj" uygulandi: 9 manifest ve 14 katman dosyasi silindi
katman dizini 176.2 KB -> 73.7 KB (diskten olculdu): 17 imajin 9'u dustu, bayt %58 azaldi
dusen imajlarin 17 ayri katmanindan 3 tanesi hala referanslidir: 129.8 KB baytin 27.3 KB kadari silinemedi
```

Tablo doğrusal değildir. On yedi imajın üçü tutulduğunda depo 49,7 KB'ye iner; sekizi
tutulduğunda 73,7 KB olur. Tutulan imaj sayısı iki buçuk katına çıkarken depo yalnız yarı
yarıya büyür, çünkü yeni tutulan imajların katmanlarının çoğu zaten oradadır.

Uygulanan kural asıl noktayı verir. Dokuz manifest silinmiş, ama bu dokuz imajın referans ettiği
**on yedi ayrı katmanın üçü** hâlâ tutulan imajlarda geçtiği için silinememiştir: 129,8 KB'nin
27,3 KB'si diskte kalmıştır. Saklama kuralı manifestler üzerinde yazılır, baytlar üzerinde
çalışmaz. "Şu imajı sil" cümlesi, katmanlı bir depoda "şu manifesti sil ve referansı düşen
katmanları topla" anlamına gelir — ve bu iki iş arasında bir toplayıcı adım vardır.

Bu, silme yönünde bir yalıtım eksiğidir de. Bir imajın silinmiş olması, içindeki baytların
depodan gitmiş olması anlamına gelmez; özetini bilen için o katmanlar hâlâ okunabilir.

## Çekme: Yerelde Olan Atlanınca

Depodaki paylaşım yalnız disk kazancı değildir; çekmede de aynı hesap işler. Bir düğüm zaten
sahip olduğu bir katmanı yeniden indirmez.

**IM41.** Bir düğüm on yedi imajı sırayla çeker ve yerel katman önbelleği hiç boşaltılmaz.
Kopyalama gerçektir: katman dosyaları depodan yerel dizine kopyalanır ve inen bayt diskten
ölçülür. **IM42.** Kimlik doğrulamanın kendisi modelin dışındadır; ölçülen şey erişim denetiminin
hangi düzeyde uygulanabildiğidir.

```js
// olcum-agi/cekme.mjs — yerelde var olan katman atlaninca inen bayt (gercek kopyalama)
import { mkdirSync, rmSync, existsSync, copyFileSync, statSync } from "node:fs";
import { join } from "node:path";
import { KOK, gecmis, depoyuKur, dizinBayt, kb } from "./depo.mjs";

const G = depoyuKur(gecmis());
const YEREL = join(KOK, "yerel");
rmSync(YEREL, { recursive: true, force: true });
mkdirSync(YEREL, { recursive: true });

const ayri = [];
for (const d of G) if (!ayri.some((x) => x.imaj === d.imaj)) ayri.push(d);

// Bir dugum imajlari sirayla ceker: yerelde olan katman dosyasi kopyalanmaz.
let inen = 0, tam = 0, atlanan = 0, ilkCekme = 0;
const satirlar = [];
for (const d of ayri) {
  let cekmeBayt = 0, yeni = 0;
  for (const o of d.ozetler) {
    const hedef = join(YEREL, o);
    if (existsSync(hedef)) { atlanan += statSync(join(KOK, "katman", o)).size; continue; }
    copyFileSync(join(KOK, "katman", o), hedef);
    cekmeBayt += statSync(hedef).size;
    yeni += 1;
  }
  const imajBayt = d.katmanlar.reduce((t, k) => t + k.bayt, 0);
  inen += cekmeBayt; tam += imajBayt;
  if (satirlar.length === 0) ilkCekme = cekmeBayt;
  satirlar.push(`${String(d.no).padStart(3)}  ${yeni}/4   ${kb(cekmeBayt).padStart(8)}   ${kb(imajBayt)}`);
}
console.log("ilk cekme ve son uc cekme");
console.log(" no  yeni       inen   tam imaj");
for (const s of [satirlar[0], ...satirlar.slice(-3)]) console.log(s);
console.log(`\n${ayri.length} cekme: inen ${kb(inen)}, tam boyutlar toplami ${kb(tam)}, ` +
  `atlanan ${kb(atlanan)} (%${(100 * atlanan / tam).toFixed(1)})`);
console.log(`ilk (soguk) cekme ${kb(ilkCekme)}; sonraki ${ayri.length - 1} cekmenin ortalamasi ` +
  kb((inen - ilkCekme) / (ayri.length - 1)));
console.log(`yerel onbellek diskte ${kb(dizinBayt(YEREL))}, depo ${kb(dizinBayt(join(KOK, "katman")))}`);

// Erisim denetimi: genel ve ozel imajlar tek bir icerik adresli havuzu paylasiyor.
const genel = ayri.filter((d) => !d.gizli), ozel = ayri.filter((d) => d.gizli);
const genelK = new Set(genel.flatMap((d) => d.ozetler));
const ozelK = new Set(ozel.flatMap((d) => d.ozetler));
const ortak = [...ozelK].filter((o) => genelK.has(o));
const ortakBayt = ortak.reduce((t, o) => t + statSync(join(KOK, "katman", o)).size, 0);
const ozelTek = [...ozelK].filter((o) => !genelK.has(o));
console.log(`\ngenel imaj ${genel.length}, ozel imaj ${ozel.length}`);
console.log(`ozel imajlarin ${ozelK.size} katmanindan ${ortak.length} tanesi genel imajlarda da ` +
  `gecer (${kb(ortakBayt)}); yalniz ozel olan ${ozelTek.length} katman`);
```

```
ilk cekme ve son uc cekme
 no  yeni       inen   tam imaj
  1  4/4    34.5 KB   34.5 KB
 17  2/4     8.0 KB   34.5 KB
 18  1/4     7.2 KB   34.5 KB
 19  2/4     8.0 KB   34.5 KB

17 cekme: inen 176.2 KB, tam boyutlar toplami 586.5 KB, atlanan 410.3 KB (%70.0)
ilk (soguk) cekme 34.5 KB; sonraki 16 cekmenin ortalamasi 8.9 KB
yerel onbellek diskte 176.2 KB, depo 176.2 KB

genel imaj 10, ozel imaj 7
ozel imajlarin 16 katmanindan 9 tanesi genel imajlarda da gecer (63.4 KB); yalniz ozel olan 7 katman
```

On yedi çekmenin toplam indirdiği **176,2 KB**, imajların toplam boyutunun **586,5 KB**
olmasına karşın. Atlanan 410,3 KB, trafiğin **yüzde 70'idir**. İlk çekme tam bir imaj indirir
(34,5 KB); sonraki on altı çekmenin ortalaması **8,9 KB**, yani bir imajın dörtte biri.

İnen toplamın depo boyutuna eşit çıkması rastlantı değildir: bütün imajları çeken bir düğüm,
ayrı katman kümesini tam olarak bir kez indirir. Bu, çekme trafiğinin üst sınırının imaj sayısı
değil **ayrı katman sayısı** olduğu anlamına gelir.

Bedelin sayısı ise yerel önbellektir: düğümde 176,2 KB tutulur ve bu, o an çalışan tek bir
imajın beş katıdır. Yalıtımın çekme tarafındaki karşılığı budur — indirme azalır, saklama artar.

## Genel Depo, Özel Depo

Çıktının son iki satırı erişim denetimini ölçer. On yedi imajın **onu genel, yedisi özeldir**.
Kimlik doğrulama manifest düzeyinde uygulanır: özel bir imajın manifestini istemek için kimlik
gerekir. Katmanlar ise tek bir içerik adresli havuzda durur.

Sonuç ölçülmüştür: **özel imajların on altı katmanından dokuzu genel imajlarda da geçer** —
63,4 KB. Bu dokuz katmanın özeti genel bir manifestten okunabilir ve okunduğunda o baytlar kimlik
gerektirmeden çekilebilir. Özel imaja özgü olan yalnız **yedi katmandır**; gizli olan da odur.

Bu, bu dersin **yalıtımın delindiği yeridir** ve iki yönü vardır. Bir yanda kayıp gerçektir:
"özel depo" ifadesi baytların tamamının gizli olduğunu düşündürür, ölçüm bunun yedi katmanla
sınırlı olduğunu söyler. Öbür yanda kapatmanın bedeli sayılabilir: paylaşımı kaldırıp özel
imajlara ayrı bir havuz vermek gizliliği katmanların tamamına yayar, ama deponun paylaşımdan
kazandığı 513,8 KB'nin bir bölümünü geri verir. Yalıtım bir bütçedir ve bu kalem de bütçeden
çıkar.

## Özet

- İçerik adresli depoda 80 katman referansı 27 dosyaya karşılık gelir: depo diskte 176,2 KB,
  paylaşımsız olsaydı 690,0 KB. Paylaşım baytların yüzde 74,5'ini kaldırır.
- Referans dağılımı homojen değildir: bir katman 16 imajda geçer, ortanca 2'dir, 27 katmanın
  11'i yalnız tek imajda geçer.
- Saklama kuralı manifestler üzerinde yazılır, baytlar üzerinde çalışmaz. "Son 8 imaj"
  uygulandığında 9 manifest silinmiş, düşen imajların 17 ayrı katmanından 3'ü hâlâ referanslı
  olduğu için 129,8 KB'nin 27,3 KB'si diskte kalmıştır.
- Yerelde bulunan katman atlandığında 17 çekmenin indirdiği 586,5 KB yerine 176,2 KB olur;
  atlanan yüzde 70'tir. Çekme trafiğinin üst sınırı imaj sayısı değil ayrı katman sayısıdır.
  Bedeli düğümde tutulan 176,2 KB'lik yerel önbellektir.
- Erişim denetimi manifest düzeyinde uygulanır, katmanlar havuzu paylaşır: 7 özel imajın 16
  katmanından 9'u (63,4 KB) genel imajlarda da geçer ve özeti bilinerek kimliksiz okunabilir.
  Gizli olan yalnız 7 katmandır.

## Sonraki Adım

Depo ölçüldü, ama bir soru açık kaldı. Özel imajın dokuz katmanı genel havuzda okunabiliyorsa,
o katmanların içinde ne var? Bu dersin ölçümü katmanlara birer bayt yığını olarak baktı ve
içlerine hiç bakmadı.

Sonraki ders içeri bakar ve konuyu kapatır: üst katmanda silinmiş bir dosya alt katmanda gerçekte
duruyor mu ve kaç bayt gerçekten silinmemiş, bilinen kusurlar üzerinde koşan bir tarama kaçını
yakalıyor ve kaçını kaçırıyor, imzalama zinciri neyi doğruluyor ve imzasız bir çekme hangi yolu
açıyor?
