---
title: Konteynerler
source: 'https://academia.sh/tr/kurslar/konteynerler'
language: tr
updated: '2026-08-17T04:40:33+00:00'
license: 'CC BY-SA 4.0'
lessons: 21
outcomes:
    - 'Yalıtım düzeylerini bedeliyle birlikte karşılaştırır'
    - 'Küçük, yeniden üretilebilir ve az yetkili imaj üretir'
    - 'Çok bileşenli bir uygulamayı yerelde ayağa kaldırır'
---

# Konteynerler

Ortamdan çıkarılan farkın yapı çıktısının içine alınması: yalıtım düzeylerinin maliyetle karşılaştırılması, ad alanları, denetim grupları ve birleşim dosya sisteminin ne yalıttığı, çalışma zamanı ve imaj biçimi belirtimlerinin taşıdığı söz, katman modeli ve katman önbelleğinin derleme süresine etkisi, çok aşamalı derleme, temel imaj seçiminin boyut ve saldırı yüzeyi dengesi, etiketleme düzeni, imaj depoları ve imzalama, kaynak sınırları, geçici dosya sistemi, birimler, konteyner ağı, kullanıcı ve izinler, çok konteynerli yerel ortam ve çalışma zamanı güvenliği.

## Konteyner Kavramı

Bağımlılık ve ortam farklılığı sorununun yalıtımla karşılanması: fiziksel sunucu, sanal makine ve konteynerin yalıtım düzeyi ile maliyetinin karşılaştırılması, ad alanları, denetim grupları ve birleşim dosya sisteminin her birinin neyi ayırdığı, çalışma zamanı ve imaj biçimi belirtimlerinin taşınabilirlik sözü ve bir konteynerin oluşturma, çalıştırma, durdurma, temizleme adımlarında tuttuğu kaynak.

### Neden Konteyner

Source: https://academia.sh/tr/kurslar/konteynerler/neden-konteyner

Ortam farkının çıktının içine alınıp alınamayacağı sayılır: önceki kursun 18 farkı üç kovaya ayrılır, içeri alınanın bayt bedeli ölçülür ve ortamda kalmak zorunda olanın payı yazılır.

Önceki kurs farkın nerede saklandığını saydı ve bir yerde durdu: üç ortam ile üretim arasında
on boyut üzerinde **18 fark** vardı, bunların 12'si ortam künyesinde yazıyordu, 6'sı yalnız
süreç koşarken görünüyordu. Sayımın söylediği asıl şey farkın büyüklüğü değil, **nerede
durduğuydu**: kurulu paket sürümleri, kaynak sınırları, dosya yolları, saat dilimi. Fark
yazılımın içinde değil, yazılımın koştuğu yerdeydi.

Bu, farkı kapatmanın iki ayrı yolu olduğu anlamına gelir. Birincisi ortamları birbirine
yaklaştırmaktır: her halkanın künyesini üretiminkine çekmek, sonra çekilmiş hâlde tutmak.
İkincisi farkı ortamdan alıp **yapı çıktısının içine koymaktır**. Çıktı koştuğu yerden ne kadar
az şey isterse, koştuğu yerin ne kadar farklı olduğu o kadar az önem taşır.

İkinci yolun adı **konteynerdir**: uygulamanın yanına, uygulamanın ihtiyaç duyduğu ortam
parçalarını da koyup hepsini tek bir çıktı olarak taşımak. Bu ders o yolun ne kadarını
gerçekten kapatabildiğini sorar ve soru üç parçalıdır — 18 farkın kaçı çıktının içine alınabilir,
alınanın bedeli kaç bayttır, alınamayan ne kalır.

Kurs boyunca aynı kurgu sürer: bir belediyenin **bölgesel ölçüm ağı**. Yazılım su
sayaçlarından okuma toplar, doğrular, faturaya çevirir ve saha ekiplerine iş emri açar; gecelik
bir toplu iş bütün günün okumalarını işler. Kurgudur; aşağıdaki sayılar da bu kurgunun
modelinden gelir.

**KK1.** Önceki kursun fark listesi bu dersin **girdisidir**; on boyut ve ortam başına fark
sayısı oradan devralınır, yeniden sayılmaz. **KK2.** Ölçülen çıktı tek bir dosya değil,
dosyalardan oluşan bir kümedir; kümenin nasıl paketlendiği sonraki konunun sorusudur.

#### Farkı Üç Kovaya Ayırmak

Bir farkın çıktının içine alınabilmesinin tek koşulu vardır: o boyutun değeri bir **dosya olarak
yazılabilmelidir**. Paket sürümü yazılabilir; paketin kendisi çıktının içine konur ve ortamda
kurulu olan sürüm önemsizleşir. Ağ gecikmesi yazılamaz; gecikme bir dosyanın içeriği değil, iki
makine arasındaki yolun özelliğidir. Aradaki boyutlar da vardır ve onların ayrı bir adı olmalıdır.

**KK3.** Kova kuralı tektir: boyutun değeri bir dosya olarak çıktının içine yazılabiliyorsa
**içeri**; çekirdek, donanım ya da işlenen verinin kendisi belirliyorsa **ortam**; ikisi de
doğruysa **sınır**. **KK4.** Bir boyutun kovası bütün ortamlar için aynıdır; kova boyuta bağlıdır,
ortama değil.

```js
// olcum-agi/kova.mjs — onceki kursun 18 farkini uc kovaya ayirir (model, kurgu veri)

// Girdi: bolgesel olcum agi kurgusunda uc ortamin uretimden ayrildigi boyutlar.
// Liste onceki kurstan devralindi; burada yeniden sayilmiyor.
const FARK = {
  gelistirme: ["cozumleyiciSurumu", "bellekMB", "esZamanliIsci", "sayacSayisi",
    "faturaKapisi", "isEmriKuyrugu", "saatDilimi", "harfDuyarliDosyaSistemi", "agGecikmeMs"],
  sinama: ["cozumleyiciSurumu", "bellekMB", "esZamanliIsci", "sayacSayisi",
    "faturaKapisi", "saatDilimi", "agGecikmeMs"],
  hazirlik: ["sayacSayisi", "agGecikmeMs"],
};

// Kova kurali: boyutun degeri bir dosya olarak ciktinin icine yazilabiliyorsa ICERI;
// cekirdek, donanim ya da isleyen verinin kendisi belirliyorsa ORTAM; ikisi de
// dogruysa SINIR.
const KOVA = {
  cozumleyiciSurumu: ["iceri", "cozumleyici ikilisi ciktinin icinde tasinir"],
  kuralPaketiSurumu: ["iceri", "kural paketi ciktinin icinde tasinir"],
  esZamanliIsci: ["iceri", "isci sayisi cikti icindeki yapilandirmada sabitlenir"],
  saatDilimi: ["iceri", "saat dilimi tablosu ve ayari ciktinin icinde tasinir"],
  bellekMB: ["ortam", "siniri uygulayan denetim grubu ciktinin disindadir"],
  sayacSayisi: ["ortam", "veri hacmi ciktinin degil isleyen sistemin ozelligidir"],
  agGecikmeMs: ["ortam", "gecikme fiziksel yolun ozelligidir"],
  faturaKapisi: ["sinir", "istemci iceri girer, kapinin kendisi disarida kalir"],
  isEmriKuyrugu: ["sinir", "istemci iceri girer, kuyruk disarida kalir"],
  harfDuyarliDosyaSistemi: ["sinir", "dosyalar iceri girer, ad eslestirme kurali alttan gelir"],
};

const ORTAMLAR = Object.keys(FARK);
const BOYUTLAR = Object.keys(KOVA);
const kovasi = (b) => KOVA[b][0];

console.log("boyut".padEnd(26) + "kova".padEnd(8) + "fark".padEnd(6) + "gerekce");
for (const b of BOYUTLAR) {
  const kac = ORTAMLAR.filter((o) => FARK[o].includes(b)).length;
  console.log(b.padEnd(26) + kovasi(b).padEnd(8) + String(kac).padEnd(6) + KOVA[b][1]);
}

const tumFarklar = ORTAMLAR.flatMap((o) => FARK[o]);
const kovaSay = (k) => tumFarklar.filter((b) => kovasi(b) === k).length;
const boyutSay = (k) => BOYUTLAR.filter((b) => kovasi(b) === k).length;

console.log("\ntoplam fark: " + tumFarklar.length + " (on boyut uzerinde)");
for (const k of ["iceri", "ortam", "sinir"]) {
  console.log(`${k.padEnd(8)}boyut ${boyutSay(k)}/10   fark ${kovaSay(k)}/18   ` +
    BOYUTLAR.filter((b) => kovasi(b) === k).join(", "));
}

console.log("\nciktinin icine alindiginda ortam basina kalan fark:");
for (const o of ORTAMLAR) {
  const kalan = FARK[o].filter((b) => kovasi(b) !== "iceri");
  console.log(`${o.padEnd(12)}${FARK[o].length} -> ${kalan.length}   kalan: ${kalan.join(", ")}`);
}
const kalanToplam = ORTAMLAR.reduce((t, o) =>
  t + FARK[o].filter((b) => kovasi(b) !== "iceri").length, 0);
console.log(`toplam 18 -> ${kalanToplam}; sinir kovasi da kapatilirsa ${kalanToplam - kovaSay("sinir")}`);
```

```
boyut                     kova    fark  gerekce
cozumleyiciSurumu         iceri   2     cozumleyici ikilisi ciktinin icinde tasinir
kuralPaketiSurumu         iceri   0     kural paketi ciktinin icinde tasinir
esZamanliIsci             iceri   2     isci sayisi cikti icindeki yapilandirmada sabitlenir
saatDilimi                iceri   2     saat dilimi tablosu ve ayari ciktinin icinde tasinir
bellekMB                  ortam   2     siniri uygulayan denetim grubu ciktinin disindadir
sayacSayisi               ortam   3     veri hacmi ciktinin degil isleyen sistemin ozelligidir
agGecikmeMs               ortam   3     gecikme fiziksel yolun ozelligidir
faturaKapisi              sinir   2     istemci iceri girer, kapinin kendisi disarida kalir
isEmriKuyrugu             sinir   1     istemci iceri girer, kuyruk disarida kalir
harfDuyarliDosyaSistemi   sinir   1     dosyalar iceri girer, ad eslestirme kurali alttan gelir

toplam fark: 18 (on boyut uzerinde)
iceri   boyut 4/10   fark 6/18   cozumleyiciSurumu, kuralPaketiSurumu, esZamanliIsci, saatDilimi
ortam   boyut 3/10   fark 8/18   bellekMB, sayacSayisi, agGecikmeMs
sinir   boyut 3/10   fark 4/18   faturaKapisi, isEmriKuyrugu, harfDuyarliDosyaSistemi

ciktinin icine alindiginda ortam basina kalan fark:
gelistirme  9 -> 6   kalan: bellekMB, sayacSayisi, faturaKapisi, isEmriKuyrugu, harfDuyarliDosyaSistemi, agGecikmeMs
sinama      7 -> 4   kalan: bellekMB, sayacSayisi, faturaKapisi, agGecikmeMs
hazirlik    2 -> 2   kalan: sayacSayisi, agGecikmeMs
toplam 18 -> 12; sinir kovasi da kapatilirsa 8
```

Kovalar boyut sayısı ile fark sayısı arasında ayrışıyor. İçeri kovasında dört boyut var ama
yalnız **6 fark** duruyor; çünkü kural paketi sürümü zaten dört ortamda da aynıydı — kapatılacak
bir farkı yoktu. Ortam kovasında üç boyut var ama **8 fark** duruyor; veri hacmi ve ağ gecikmesi
üç ortamın üçünde de üretimden ayrılıyordu. Yani içeri alınabilirlik ile farkın büyüklüğü ters
yönde dizilmiş: en çok fark üreten iki boyut, çıktının içine hiç alınamayan iki boyut. Kova
sayımı bu yüzden boyut üzerinden değil fark üzerinden okunur; boyut sayısı neyin ele
alınabildiğini, fark sayısı ise ne kadarının gerçekten kapandığını söyler.

Ortam başına okuma daha keskin. Geliştirme ortamı 9 farktan 6'sına iniyor, sınama 7'den 4'e,
hazırlık **2'den 2'ye** — yani hiç değişmiyor. Üretime en yakın halkanın kazancı sıfır, çünkü
hazırlığın ayrıldığı iki boyutun ikisi de ortam kovasında. Bu sonuç, "konteyner ortam farkını
ortadan kaldırır" cümlesinin tam olarak nerede yanlış olduğunu verir: kazanç, üretimden en uzak
halkada en büyük, en yakın halkada sıfırdır. Toplamda 18 fark 12'ye iner; sınır kovasındaki
dördü de kapatılırsa 8'e.

#### İçeri Almanın Bayt Bedeli

Altı farkın kapanması bedava değil. İçeri alınan her parça çıktının içine gerçekten yazılan bir
dosya kümesidir ve çıktının boyutu, taşınan her yere taşınır: depolanan bayt, ağdan geçen bayt,
diske açılan bayt.

**KK5.** Ölçümün kaynak ağacı gerçektir — servis dosyaları belirlenimci bir üreteçle diske
yazılır (tohum 20260801) ve `node:fs` ile gerçekten ölçülür. **KK6.** İçeri alınan parçaların
boyutları ise **modeldir** — bildirilen değerlerdir, ölçülmüş bir kurulumdan gelmez — ve içeri
alınan parça ortamda kurulu olanın yerine geçer; iki kopya sayılmaz.

```js
// olcum-agi/bedel.mjs — iceri alinanin bayt bedeli
// Kaynak agaci gercekten diske yazilir ve gercekten olculur; ortamdan iceri
// alinan parcalarin boyutlari MODEL degeridir, olculmus bir kurulumdan degil.
import { mkdirSync, writeFileSync, readdirSync, statSync, rmSync } from "node:fs";
import { join } from "node:path";

const KOK = "/tmp/olcum-agi-kaynak";
let tohum = 20260801;                       // uretec tohumu; ayni tohum ayni agaci verir
const sonraki = () => (tohum = (tohum * 1103515245 + 12345) % 2147483648);

// Kurgu servislerin kaynak agaci: her servis icin belirlenimci satirlar uretilir.
const SERVIS = { toplayici: 210, dogrulayici: 340, faturalama: 480, isEmri: 160, gecelikIs: 260 };
rmSync(KOK, { recursive: true, force: true });
mkdirSync(join(KOK, "src"), { recursive: true });
for (const [ad, satir] of Object.entries(SERVIS)) {
  const govde = Array.from({ length: satir }, (_, i) =>
    `export const ${ad}${i} = ${sonraki() % 100000};`).join("\n");
  writeFileSync(join(KOK, "src", `${ad}.mjs`), govde + "\n");
}
writeFileSync(join(KOK, "yapilandirma.json"),
  JSON.stringify({ esZamanliIsci: 8, saatDilimi: "+00:00", kuralPaketi: "9.1.0" }, null, 2));

const bayt = (yol) => statSync(yol).isDirectory()
  ? readdirSync(yol).reduce((t, a) => t + bayt(join(yol, a)), 0) : statSync(yol).size;
const kaynak = bayt(KOK);

// MODEL: ortamdan cikarilip ciktinin icine alinan parcalarin bildirilen boyutlari.
const ICERI = {
  "cozumleyici calisma zamani": 44_800_000,
  "kural paketi ve bagimliliklari": 31_200_000,
  "saat dilimi tablosu": 460_000,
};
const TABAN = { "kucuk": 7_600_000, "orta": 31_500_000, "genel amacli": 118_000_000 };
const mb = (n) => (n / 1_000_000).toFixed(2).padStart(8) + " MB";

console.log("olculen kaynak agaci (gercek dosyalar, tohum " + 20260801 + ")");
for (const a of readdirSync(join(KOK, "src")).sort())
  console.log("  src/" + a.padEnd(20) + String(bayt(join(KOK, "src", a))).padStart(8) + " bayt");
console.log("  yapilandirma.json".padEnd(24) +
  String(bayt(join(KOK, "yapilandirma.json"))).padStart(8) + " bayt");
console.log("  kaynak toplami".padEnd(24) + String(kaynak).padStart(8) + " bayt = " + mb(kaynak));

console.log("\nMODEL: ciktinin icine alinan parcalar");
let iceriToplam = 0;
for (const [ad, b] of Object.entries(ICERI)) {
  iceriToplam += b;
  console.log("  " + ad.padEnd(32) + mb(b));
}
console.log("  " + "toplam (taban haric)".padEnd(32) + mb(iceriToplam));

console.log("\ntaban kok dosya sistemi turu tarandi (kapatilan fark sayisi degismez: 6/18)");
for (const [tur, t] of Object.entries(TABAN)) {
  const cikti = kaynak + iceriToplam + t;
  console.log(`  ${tur.padEnd(14)}taban ${mb(t)}   cikti ${mb(cikti)}   ` +
    `kaynagin ${Math.round(cikti / kaynak)} kati   fark basina ${mb((cikti - kaynak) / 6)}`);
}
rmSync(KOK, { recursive: true, force: true });
```

```
olculen kaynak agaci (gercek dosyalar, tohum 20260801)
  src/dogrulayici.mjs        12429 bayt
  src/faturalama.mjs         17114 bayt
  src/gecelikIs.mjs           8959 bayt
  src/isEmri.mjs              4992 bayt
  src/toplayici.mjs           7217 bayt
  yapilandirma.json           76 bayt
  kaynak toplami           50787 bayt =     0.05 MB

MODEL: ciktinin icine alinan parcalar
  cozumleyici calisma zamani         44.80 MB
  kural paketi ve bagimliliklari     31.20 MB
  saat dilimi tablosu                 0.46 MB
  toplam (taban haric)               76.46 MB

taban kok dosya sistemi turu tarandi (kapatilan fark sayisi degismez: 6/18)
  kucuk         taban     7.60 MB   cikti    84.11 MB   kaynagin 1656 kati   fark basina    14.01 MB
  orta          taban    31.50 MB   cikti   108.01 MB   kaynagin 2127 kati   fark basina    17.99 MB
  genel amacli  taban   118.00 MB   cikti   194.51 MB   kaynagin 3830 kati   fark basina    32.41 MB
```

Yazılan kod 50.787 bayttır. Ortamdan çıkarılıp içeri alınan üç parça 76,46 MB tutar; buna
altındaki kök dosya sistemi eklenir ve çıktı en küçük tabanla **84,11 MB**'a çıkar. Oran
kaynağın **1.656 katıdır**. Kapatılan altı fark başına düşen bedel 14,01 MB'dır ve bu, yalıtım
bütçesinin ilk kalemidir: bir fark, on dört megabayt karşılığında ortamdan çıkarılmıştır.

Taban türünün taranması ikinci kalemi verir. Kapatılan fark sayısı üç türde de aynıdır — 6 —
ama çıktı 84,11 MB ile 194,51 MB arasında değişir. Aynı kazanç için ödenen bedel 2,3 kat
farklıdır ve farkı yaratan tek şey, çıktının altına hangi kök dosya sisteminin konduğudur.
"Konteyner hafiftir" cümlesinin karar taşımamasının nedeni budur: hafiflik bir ölçüm sonucu
değil, bir seçimin sonucudur ve seçim çıktının içinde durur.

Bedelin yalnız bayt kalemi de yoktur. İçeri alınan çözümleyici çalışma zamanı, ortamda
yükseltildiğinde kendiliğinden yükselen bir bileşendi; içeri alındığı andan itibaren onu
yükseltmek çıktının yeniden üretilmesini gerektirir. Fark ortamdan çıkarılırken, o farkı kapatma
sorumluluğu da ortamı işletenden çıktıyı üretene geçer.

Bu devir bütçenin görünmeyen kalemidir. Ortamı işleten ekip için bir sürüm yükseltmesi tek bir
kurulum işiydi ve o makinede koşan bütün uygulamaları birden ilgilendiriyordu. Çıktının içine
alındıktan sonra aynı yükseltme, o parçayı taşıyan **her çıktı için ayrı ayrı** yapılan bir yapı
işidir. Kurgudaki ağda beş servis vardır; çözümleyici sürümünün yükseltilmesi bir kurulum yerine
beş yeniden üretim demektir. Bayt kalemi ölçülebildiği için görünürdür, bu kalem ölçülmediği
sürece görünmez — ve yalıtım bütçesinin gerçek toplamı ikisinin toplamıdır.

#### Ortamda Kalan ve Sınırda Duran

Ortam kovasındaki üç boyut, çıktının içine alınamamalarının nedenleriyle birlikte ayrılır.
Kaynak sınırını uygulayan şey çıktının dışındaki bir **denetim grubudur**; çıktı ne kadar bellek
isteyeceğini bildirebilir ama sınırı kendisi koyamaz. Veri hacmi işlenen sayaç sayısıdır ve
çalışan sistemin özelliğidir, çıktının değil. Ağ gecikmesi fiziksel yolun özelliğidir. Üçünün
ortak yanı şudur: hiçbiri bir dosyanın içeriği olarak yazılamaz.

Sınır kovasındaki üç boyut ise farkın **bir parçasını** içeri alır. Fatura kapısı ve iş emri
kuyruğu için istemci kitaplığı, sözleşme tanımı ve zaman aşımı ayarı çıktının içine girer;
karşı taraftaki gerçek kapı dışarıda kalır. Fark küçülür ama sıfırlanmaz — "sözleşme sapması"
hata sınıfı, istemci içeri alındıktan sonra da açık kalır.

Üçüncü sınır boyutu **yalıtımın delindiği yeri** en açık gösteren boyuttur. Dosyaların hepsi
çıktının içindedir; ama iki dosya adının aynı ad sayılıp sayılmayacağına karar veren kural
çıktının içinde değil, çıktının açıldığı dosya sisteminden gelir. Aynı çıktı, harf duyarlı bir
dosya sisteminde tarife dosyasını bulamaz, harf duyarsız birinde bulur. Aynı olgu saat için de
geçerlidir: saat dilimi tablosu içeri alınabilir, saatin **değeri** alınamaz — o çekirdekten
okunur. Konteyner yalıtımının tek cümlelik özeti buradan çıkar: **çıktı dosyaları taşır,
davranışı değil.** Davranışın bir bölümü hep alttaki çekirdeğe ve dosya sistemine bağlı kalır.

#### Özet

- Ortam farkını kapatmanın iki yolu vardır; bu kurs ikincisini ölçer: farkı ortamdan alıp yapı
  çıktısının içine koymak. Yolun adı konteynerdir.
- Önceki kursun 18 farkı üç kovaya ayrıldı: içeri alınabilen 6, ortamda kalmak zorunda olan 8,
  sınırda duran 4. İçeri alınabilirlik ile fark üretme sıklığı ters yönde dizilidir.
- Ortam başına kazanç eşit değildir: geliştirme 9 farktan 6'ya, sınama 7'den 4'e iner, hazırlık
  2'de kalır. Üretime en yakın halkanın kazancı sıfırdır.
- İçeri almanın bedeli ölçüldü: 50.787 baytlık kaynak, en küçük tabanla 84,11 MB'lık bir çıktıya
  dönüşür — kaynağın 1.656 katı, kapatılan fark başına 14,01 MB. Taban türü değişince kazanç
  aynı kalır, bedel 2,3 kat artar.
- Yalıtım delinir: kaynak sınırını çıktının dışındaki denetim grubu uygular, saatin değeri ve
  dosya adı eşleştirme kuralı alttaki çekirdekten gelir. Çıktı dosyaları taşır, davranışı değil.

#### Sonraki Adım

Bu ders farkın çıktının içine alınabileceğini gösterdi ama bir şeyi hiç sormadı: içeri alınan bu
76 megabaytlık ortam parçası **nerede ve neyin yanında** koşacak? Aynı makinede ikinci bir
uygulama da kendi çözümleyicisini, kendi kural paketini ve kendi saat dilimi tablosunu getirirse
ne olur — ikisi birbirinin dosyalarını görür mü, birbirinin belleğini tüketebilir mi, biri
diğerinin süreçlerini durdurabilir mi? Ortam farkını kapatmak ile ortamı **paylaşan** iki
uygulamayı birbirinden ayırmak ayrı işlerdir ve ikincisinin adı yalıtımdır. Sonraki ders
yalıtımı düzeylerine ayırır ve her düzeyin neyi ayırdığını, neyi yinelediğini ve karşılığında ne
kadar bellek ile ne kadar başlangıç gecikmesi istediğini aynı ölçütlerle karşılaştırır.

### Sunucu, Sanal Makine, Konteyner

Source: https://academia.sh/tr/kurslar/konteynerler/sunucu-sanal-makine-konteyner

Üç yalıtım düzeyi aynı ölçütlerle karşılaştırılır: hangisi neyi ayırır, neyi yineler, karşılığında kaç megabayt bellek tabanı ve hangi mertebede başlangıç gecikmesi ister, tek makineye kaç örnek sığar.

Önceki ders farkın 6'sını ortamdan çıkardı ve bunun bedelini bayt cinsinden ölçtü: en küçük
tabanla 84,11 MB'lık bir çıktı. O ölçüm tek bir çıktıyı ele alıyordu. Kurgudaki **bölgesel ölçüm
ağında** ise beş servis var — okuma toplayıcı, doğrulayıcı, faturalama, iş emri ve gecelik toplu
iş — ve hepsi aynı makinede koşuyor. Her biri kendi çözümleyicisini, kendi kural paketini ve
kendi saat dilimi tablosunu getirdiğinde soru değişir: bu beş çıktı aynı makinede yan yana
durduğunda birbirlerinden ne kadar ayrıdır?

Ayrılığın adı **yalıtımdır** ve tek bir şey değildir. Bir uygulamanın diğerinin dosyalarını
görememesi ayrı bir şeydir, süreçlerini durduramaması ayrı, belleğini tüketememesi ayrı. Bu ders
üç yalıtım düzeyini — paylaşılan sunucu, konteyner ve sanal makine — **aynı ölçütlerle**
karşılaştırır: neyi ayırıyorlar, neyi yineliyorlar, karşılığında ne istiyorlar.

**KK7.** Karşılaştırma tek makine ölçeğindedir; birden çok makineye dağıtım bu kursun konusu
değildir. **KK8.** Aşağıdaki bütün sayılar **model girdisidir**, ölçülmüş bir kurulumdan gelmez;
anlamlı olan iki düzey arasındaki **mertebe farkıdır**, tek bir hücredeki değer değil. **KK9.**
Beş ölçüt vardır ve her ölçüt ikili değer alır: ayrılır ya da paylaşılır. Kısmi yalıtım tam
sayılmaz.

#### Aynı Ölçütlerle Üç Düzey

Ölçütler, yalıtımın delinebileceği yerlerin adlarıdır. **Çekirdek**: iki uygulama aynı çekirdeği
mi çağırıyor. **Dosya sistemi**: biri diğerinin dosyalarını görebiliyor mu. **Ağ**: aynı arayüz
ve aynı kapı uzayı mı. **Süreç tablosu**: biri diğerinin süreçlerini listeleyip durdurabiliyor
mu. **Kullanıcı**: dosya sahipliği ve yetki aynı hesaba mı bağlı.

```js
// olcum-agi/duzeyler.mjs — uc yalitim duzeyinin ayni olcutlerle karsilastirilmasi
// MODEL: butun sayilar bildirilen model girdisidir, olculmus bir kurulumdan gelmez.
// Karsilastirma mertebe duzeyindedir; iki duzey arasindaki oran anlamlidir, tek
// bir hucredeki deger degil.

export const OLCUT = ["cekirdek", "dosyaSistemi", "ag", "surecTablosu", "kullanici"];

export const DUZEY = {
  "paylasilan sunucu": {
    yalitir: { cekirdek: false, dosyaSistemi: false, ag: false, surecTablosu: false, kullanici: true },
    yineler: { isletimSistemiKopyasi: 0, kutuphaneKumesi: 0 },
    baslangicMs: 30, bellekTabaniMB: 4, diskTabaniMB: 0,
  },
  "konteyner": {
    yalitir: { cekirdek: false, dosyaSistemi: true, ag: true, surecTablosu: true, kullanici: true },
    yineler: { isletimSistemiKopyasi: 0, kutuphaneKumesi: 1 },
    baslangicMs: 300, bellekTabaniMB: 12, diskTabaniMB: 84,
  },
  "sanal makine": {
    yalitir: { cekirdek: true, dosyaSistemi: true, ag: true, surecTablosu: true, kullanici: true },
    yineler: { isletimSistemiKopyasi: 1, kutuphaneKumesi: 1 },
    baslangicMs: 25_000, bellekTabaniMB: 512, diskTabaniMB: 1_240,
  },
};

// Onceki dersin 18 farkindan kacinin ortamdan cikarildigi (girdi: 01'in kova sayimi).
export const KAPATILAN = {
  "paylasilan sunucu": [0, "kutuphaneler paylasilir; iceri kovasindaki alti fark ortamda kalir"],
  "konteyner": [6, "iceri kovasindaki alti fark ciktinin icine girer"],
  "sanal makine": [7, "cekirdek de icerde oldugu icin ad eslestirme kurali da kapanir"],
};

export const ADLAR = Object.keys(DUZEY);
export const yalitilan = (d) => OLCUT.filter((o) => DUZEY[d].yalitir[o]);

// Dokum yalniz bu dosya dogrudan kosturuldugunda basilir; sonraki blok modulu
// veri icin ice aktarir ve bu bolumu yeniden basmaz.
if (import.meta.main) {
console.log("yalitilan sey (+ ayrilir, - paylasilir)");
console.log("olcut".padEnd(16) + ADLAR.map((d) => d.padEnd(20)).join(""));
for (const o of OLCUT)
  console.log(o.padEnd(16) + ADLAR.map((d) => (DUZEY[d].yalitir[o] ? "+" : "-").padEnd(20)).join(""));
console.log("yalitilan/5".padEnd(16) +
  ADLAR.map((d) => `${yalitilan(d).length}/5`.padEnd(20)).join(""));

console.log("\nyinelenen sey (ornek basina kac kopya)");
for (const y of ["isletimSistemiKopyasi", "kutuphaneKumesi"])
  console.log(y.padEnd(24) + ADLAR.map((d) => String(DUZEY[d].yineler[y]).padEnd(20)).join(""));

console.log("\nortamdan cikarilan fark (18'in kaci):");
for (const d of ADLAR)
  console.log("  " + d.padEnd(20) + `${KAPATILAN[d][0]}/18   ` + KAPATILAN[d][1]);

console.log("\npaylasilan sey (delik):");
for (const d of ADLAR) {
  const delik = OLCUT.filter((o) => !DUZEY[d].yalitir[o]);
  console.log("  " + d.padEnd(20) + (delik.length ? delik.join(", ") : "olcutlerin hicbiri"));
}
}
```

```
yalitilan sey (+ ayrilir, - paylasilir)
olcut           paylasilan sunucu   konteyner           sanal makine        
cekirdek        -                   -                   +                   
dosyaSistemi    -                   +                   +                   
ag              -                   +                   +                   
surecTablosu    -                   +                   +                   
kullanici       +                   +                   +                   
yalitilan/5     1/5                 4/5                 5/5                 

yinelenen sey (ornek basina kac kopya)
isletimSistemiKopyasi   0                   0                   1                   
kutuphaneKumesi         0                   1                   1                   

ortamdan cikarilan fark (18'in kaci):
  paylasilan sunucu   0/18   kutuphaneler paylasilir; iceri kovasindaki alti fark ortamda kalir
  konteyner           6/18   iceri kovasindaki alti fark ciktinin icine girer
  sanal makine        7/18   cekirdek de icerde oldugu icin ad eslestirme kurali da kapanir

paylasilan sey (delik):
  paylasilan sunucu   cekirdek, dosyaSistemi, ag, surecTablosu
  konteyner           cekirdek
  sanal makine        olcutlerin hicbiri
```

Matris üç düzeyi tek bir eksene dizer: 1/5, 4/5, 5/5. Paylaşılan sunucuda beş ölçütten yalnız
biri ayrılır — kullanıcı hesabı — ve bu tek ayrım da dosya izinlerinin doğru kurulmuş olmasına
bağlıdır. Konteyner dört ölçütü ayırır ve **çekirdeği paylaşır**. Sanal makine beşini de ayırır,
çünkü kendi çekirdeğini taşır.

Yinelenen şey sütunu bu dizilimin nedenini verir. Paylaşılan sunucu hiçbir şeyi yinelemez;
kütüphaneler ortaktır, dolayısıyla ortam farkının tümü ortamda kalır — önceki dersin altı farkı
kapanmaz. Konteyner kütüphane kümesini örnek başına bir kez yineler, işletim sistemi kopyasını
yinelemez. Sanal makine ikisini de yineler. Ortamdan çıkarılan fark bu sıralamayı izler: 0, 6 ve
7. Sanal makinenin yedinci farkı, çekirdeği de içine aldığı için kapattığı boyuttur — dosya adı
eşleştirme kuralı önceki derste sınır kovasındaydı; çekirdek içeri girince o da kapanır.

Ortadaki düzey bir uzlaşma değildir; belirli bir ölçütü ayırmaktan **vazgeçmenin** adıdır.
Konteynerin paylaşılan sunucudan satın aldığı üç ölçüt vardır: dosya sistemi, ağ ve süreç
tablosu. Sanal makineden vazgeçtiği ölçüt tektir: çekirdek. Üç düzey arasındaki fark bu iki
sayıda toplanır ve ikisi de tabloda yazılıdır. Ölçütlerin ikili değer alması (KK9) burada bir
sadeleştirmedir: gerçekte bir ölçüt kısmen de ayrılabilir — kullanıcı hesabı ayrıdır ama kök
yetkisi ortak kalabilir, ağ uzayı ayrıdır ama aynı fiziksel arayüzü kullanır. Bu ara durumlar
tabloda "ayrılır" görünür ve tablonun okuduğundan daha az yalıtım sağlar; sonraki derslerin
ölçtüğü şeylerden biri tam olarak budur.

#### Bedelin Mertebesi

Yalıtım satın alınan bir şeydir ve ödemesi üç kalemde yapılır: başlangıç gecikmesi, bellek
tabanı, disk tabanı.

**KK10.** Bellek tabanı, uygulamanın kendi belleği dışında **yalıtım düzeyinin getirdiği**
ektir. **KK11.** Makine 16.384 MB'dır, 1.024 MB'ı işletim sistemine ayrılmıştır ve örnek başına
uygulama belleği 180 MB'dır. **KK12.** Örnekler sırayla başlatılır; koşut başlatma modellenmez.

```js
// olcum-agi/mertebe.mjs — yalitim bedelinin mertebesi ve yogunluga cevrilmesi
import { DUZEY, ADLAR, yalitilan } from "./duzeyler.mjs";

const mertebe = (n) => (n <= 0 ? "-" : "10^" + Math.round(Math.log10(n)));
const MAKINE_MB = 16_384, AYRILAN_MB = 1_024, UYGULAMA_MB = 180, ORNEK = 25;

console.log("bedel ve mertebesi (model girdisi)");
console.log("duzey".padEnd(20) + "baslangic".padEnd(14) + "mertebe".padEnd(9) +
  "bellek tabani".padEnd(15) + "mertebe".padEnd(9) + "disk tabani");
for (const d of ADLAR) {
  const v = DUZEY[d];
  console.log(d.padEnd(20) + `${v.baslangicMs} ms`.padEnd(14) + mertebe(v.baslangicMs).padEnd(9) +
    `${v.bellekTabaniMB} MB`.padEnd(15) + mertebe(v.bellekTabaniMB).padEnd(9) + `${v.diskTabaniMB} MB`);
}

const taban = ADLAR.map((d) => DUZEY[d].bellekTabaniMB);
console.log("bellek tabani orani (en kucuge gore): " +
  ADLAR.map((d, i) => `${d} ${Math.round(taban[i] / taban[0])}x`).join(", "));

console.log("\nyalitilan boyut basina bellek tabani");
for (const d of ADLAR) {
  const k = yalitilan(d).length;
  console.log("  " + d.padEnd(20) + `${k} boyut  ` +
    `${(DUZEY[d].bellekTabaniMB / k).toFixed(1)} MB/boyut`);
}

console.log(`\n${MAKINE_MB} MB'lik tek makine, ${AYRILAN_MB} MB ayrilmis, ` +
  `ornek basina uygulama ${UYGULAMA_MB} MB`);
for (const d of ADLAR) {
  const ornekMB = UYGULAMA_MB + DUZEY[d].bellekTabaniMB;
  const sigan = Math.floor((MAKINE_MB - AYRILAN_MB) / ornekMB);
  const yuk = ((DUZEY[d].bellekTabaniMB / ornekMB) * 100).toFixed(1);
  console.log("  " + d.padEnd(20) + `ornek ${String(ornekMB).padStart(4)} MB   ` +
    `sigan ${String(sigan).padStart(3)}   yalitim yuku %${yuk}`);
}

console.log(`\n${ORNEK} ornegin sirayla baslatilmasi ve tasidigi disk`);
for (const d of ADLAR) {
  const sn = (DUZEY[d].baslangicMs * ORNEK) / 1000;
  console.log("  " + d.padEnd(20) + `${sn.toFixed(2).padStart(8)} sn   ` +
    `disk ${String(DUZEY[d].diskTabaniMB * ORNEK).padStart(5)} MB   ` +
    `yalitilan ${yalitilan(d).length}/5`);
}
```

```
bedel ve mertebesi (model girdisi)
duzey               baslangic     mertebe  bellek tabani  mertebe  disk tabani
paylasilan sunucu   30 ms         10^1     4 MB           10^1     0 MB
konteyner           300 ms        10^2     12 MB          10^1     84 MB
sanal makine        25000 ms      10^4     512 MB         10^3     1240 MB
bellek tabani orani (en kucuge gore): paylasilan sunucu 1x, konteyner 3x, sanal makine 128x

yalitilan boyut basina bellek tabani
  paylasilan sunucu   1 boyut  4.0 MB/boyut
  konteyner           4 boyut  3.0 MB/boyut
  sanal makine        5 boyut  102.4 MB/boyut

16384 MB'lik tek makine, 1024 MB ayrilmis, ornek basina uygulama 180 MB
  paylasilan sunucu   ornek  184 MB   sigan  83   yalitim yuku %2.2
  konteyner           ornek  192 MB   sigan  80   yalitim yuku %6.3
  sanal makine        ornek  692 MB   sigan  22   yalitim yuku %74.0

25 ornegin sirayla baslatilmasi ve tasidigi disk
  paylasilan sunucu       0.75 sn   disk     0 MB   yalitilan 1/5
  konteyner               7.50 sn   disk  2100 MB   yalitilan 4/5
  sanal makine          625.00 sn   disk 31000 MB   yalitilan 5/5
```

Mertebeler okunduğunda tablo iki kümeye ayrılır. Bellek tabanı mertebesinde paylaşılan sunucu ile
konteyner **aynı yerdedir**: ikisi de 10^1. Sanal makine iki mertebe yukarıdadır: 10^3. Başlangıç
gecikmesinde ise konteyner tek mertebe yukarı çıkar (10^2), sanal makine üç mertebe (10^4). Yani
konteynerin paylaşılan sunucudan ayrıldığı yer bellek değil, başlangıç ve disktir; sanal
makinenin ikisinden de ayrıldığı yer her üç kalemdir.

Yalıtılan boyut başına düşen bellek tabanı bunu tek sayıya indirir. Paylaşılan sunucu 4,0
MB/boyut, konteyner **3,0 MB/boyut**, sanal makine 102,4 MB/boyut. Konteynerin birim başına en
ucuz düzey olması, çekirdeği yinelemeden dört boyut ayırabilmesinden gelir. Sanal makine ise beş
boyutun beşini de ayırır ama beşinci boyutu — çekirdeği — ayırmanın bedeli tek başına diğer
dördünün toplamının kırk katıdır. Birim ölçüsünün paylaşılan sunucuyu konteynerin altına
düşürmesi ilk bakışta ters görünür, çünkü paylaşılan sunucunun toplam bedeli daha küçüktür.
Ters görünen şey bölenin değişmesidir: 4 MB tek bir boyuta bölünür, 12 MB dörde. Toplam bedele
bakan bir karşılaştırma "en ucuz düzey hangisi" sorusunu, birim bedele bakan bir karşılaştırma
ise "ayrılan boyut başına ne ödeniyor" sorusunu yanıtlar; yalıtım bütçesinde ikinci soru
karar taşır, çünkü satın alınan şey bayt değil ayrılan ölçüttür. İki soru aynı tablodan
okunur ve birbirinin yerine geçmez.

Yoğunluk sayımı bunu koşum tarafına çevirir. Aynı 16 GB'lık makineye paylaşılan sunucuda 83,
konteynerde 80, sanal makinede 22 örnek sığar. Yalıtım yükü — belleğin ne kadarının uygulamaya
değil, yalıtıma gittiği — sırasıyla %2,2, %6,3 ve %74,0'tür. Sanal makinede belleğin dörtte
üçüne yakını uygulamanın kendisine gitmez. Yirmi beş örneğin sırayla başlatılması 0,75
saniye, 7,50 saniye ve 625 saniye sürer; taşınan disk 0, 2.100 ve 31.000 MB'dır.

Yirmi beş sayısı kurgudan gelir: beş servisin beşer kopyası. Başlangıç süresinin karar taşıdığı
yer de oradadır. Gecelik toplu iş her gece bir kez başlar ve 625 saniye onun için tolere
edilebilir bir sayıdır; okuma toplayıcı ise bir kusur sonrası yeniden başlatıldığında saha
ekiplerinin beklediği süredir ve orada 7,50 saniye ile 625 saniye arasındaki fark bir kesinti
süresidir. Aynı model girdisi iki servis için iki ayrı anlam taşır — bu yüzden başlangıç
gecikmesi tek bir eşikle değil, o düzeyde koşan işin yeniden başlatma sıklığıyla birlikte
okunur. Disk kalemi de aynı biçimde çarpılır: 84 MB'lık bir çıktı yirmi beş örnekte 2.100 MB'a
çıkar ve bu sayı, katmanların paylaşılıp paylaşılmadığına göre değişir; paylaşımın nasıl
çalıştığı sonraki dersin konusudur.

#### Yalıtımın Delindiği Yer

Matrisin son bölümü her düzeyin deliğini adlandırır. Paylaşılan sunucuda dört ölçüt açıktır:
çekirdek, dosya sistemi, ağ ve süreç tablosu. Kurgudaki ağda bu şu demektir — gecelik toplu iş
bir tarife dosyasını yanlış yola yazarsa doğrulayıcı onu okur; faturalama servisi bir kapıyı
tutarsa iş emri servisi aynı kapıyı açamaz; biri belleği tüketirse hepsi birden durur.

Konteynerde delik tektir ve adı **çekirdektir**. Beş servis kendi dosya sistemini, kendi ağ
uzayını, kendi süreç tablosunu ve kendi kullanıcısını görür; ama beşi de aynı çekirdeğe sistem
çağrısı yapar. Bunun üç sonucu vardır. Önceki dersin ölçtüğü saat değeri hâlâ ortaktır: beş
servis aynı saati okur, saat dilimi ayarı çıktının içindedir ama saatin kendisi değildir.
Çekirdek sürümü ortaktır: bir servisin ihtiyaç duyduğu çekirdek davranışı diğerlerini de
ilgilendirir. Ve çekirdekteki bir kusur beş yalıtımın altından birden geçer — dört ölçütün
ayrılmış olması bu yolu kapatmaz.

Disk tabanı sütunundaki sıfır da bir deliğin işaretidir. Paylaşılan sunucu hiçbir şey
taşımadığı için hiçbir bayt ödemez; ödemediği şey ortam farkının kapatılmasıdır. Bu üç kalemin
hepsinde en ucuz düzey olması bir üstünlük değil, hiçbir şey satın almamış olmasının
sonucudur. Bedel tablosu tek başına okunduğunda paylaşılan sunucu her satırda kazanır; ölçüt
tablosuyla birlikte okunduğunda 1/5 ile 0/18'i de birlikte taşıdığı görünür. Yalıtım bütçesinin
iki tabloyu ayrı ayrı değil yan yana okumayı gerektirmesinin nedeni budur.

Sanal makinede ölçütlerin hiçbiri açık değildir; delik ölçüt tablosunda görünmez, bedel
tablosunda görünür. 102,4 MB/boyut ve 10^4 mertebesinde başlangıç, yalıtımın tam olmasının
fiyatıdır. Üç düzey arasındaki seçim bu yüzden "hangisi daha iyi" sorusu değildir: **hangi
ölçütün ayrılması gerektiği** sorusudur ve ayrılmayan her ölçüt bir hata sınıfını açık bırakır.

#### Özet

- Yalıtım tek bir şey değil, beş ölçütün her birinde ayrı ayrı verilen bir karardır: çekirdek,
  dosya sistemi, ağ, süreç tablosu, kullanıcı.
- Üç düzey sırasıyla 1/5, 4/5 ve 5/5 ölçüt ayırır. Yinelenen şey bu sıralamayı belirler:
  paylaşılan sunucu hiçbir şeyi, konteyner kütüphane kümesini, sanal makine kütüphane kümesi ile
  işletim sistemi kopyasını yineler.
- Ortamdan çıkarılan fark düzeye bağlıdır: paylaşılan sunucuda 0/18, konteynerde 6/18, sanal
  makinede 7/18. Yedincisi çekirdek içeri girdiği için kapanan dosya adı eşleştirme kuralıdır.
- Bedel mertebe olarak yazılır: bellek tabanı 10^1, 10^1 ve 10^3; başlangıç 10^1, 10^2 ve 10^4.
  Yalıtılan boyut başına bellek tabanı 4,0, 3,0 ve 102,4 MB'dır — konteyner birim başına en ucuz
  düzeydir.
- Aynı makineye 83, 80 ve 22 örnek sığar; yalıtım yükü %2,2, %6,3 ve %74,0'tür. Konteynerin
  deliği tektir ve adı çekirdektir: saat değeri, çekirdek sürümü ve çekirdek kusuru dört
  ölçütün altından birden geçer.

#### Sonraki Adım

Bu ders konteynerin dört ölçütü ayırdığını yazdı ama nasıl ayırdığını hiç sormadı. Aynı
çekirdek üzerinde koşan iki süreçten biri diğerinin süreçlerini nasıl görmüyor — süreç tablosu
tek bir tablo olduğuna göre, "görmemek" ne demek? Aynı şey bellek sınırı için de geçerli: sınırı
uygulayan denetim grubuysa, sınır aşıldığında verilen karar nedir? Ve beş servis aynı kütüphane
kümesini taşıyorsa, aynı dosyalar diske beş kez mi yazılıyor? Sonraki ders bu üç soruya karşılık
gelen üç mekanizmayı ayrı ayrı modeller — ad alanı bir görünürlük eşlemesi, denetim grubu bir pay
dağıtıcı, birleşim dosya sistemi bir katman yığını olarak — ve her birinin yalıtımı nerede
deldiğini yazar.

### Alt Yapı Teknolojileri

Source: https://academia.sh/tr/kurslar/konteynerler/alt-yapi-teknolojileri

Konteyner yalıtımını kuran üç mekanizma ayrı ayrı modellenir: ad alanı bir görünürlük eşlemesi, denetim grubu bir pay dağıtıcı, birleşim dosya sistemi bir katman yığını olarak; her birinin bedeli ve delindiği yer sayılır.

Önceki ders konteynerin beş ölçütten dördünü ayırdığını yazdı: dosya sistemi, ağ, süreç tablosu
ve kullanıcı. Nasıl ayırdığını sormadı. Soru boş değildir, çünkü ayrılan şeyler tek makinede tek
tanedir — süreç tablosu bir tanedir, bellek bir tanedir, disk bir tanedir. "Ayırmak" burada
ikinci bir tane üretmek olamaz; başka bir şey olmalıdır.

Bu ders o başka şeyi üç ayrı mekanizma olarak modeller. **Ad alanı** aynı tablonun üzerine bir
görünürlük eşlemesi koyar. **Denetim grubu** aynı kaynağı pay olarak dağıtır ve sınır aşıldığında
bir karar verir. **Birleşim dosya sistemi** aynı dosyaları bir yığın olarak üst üste koyar.
Üçünün ortak yanı şudur: hiçbiri yeni bir kaynak üretmez, üçü de **tek bir kaynağın üzerine bir
kural** koyar. Kuralın olduğu yerde delik de vardır ve her bölüm deliği adıyla yazar.

**KK13.** Üç mekanizma ayrı ayrı modellenir; birlikte çalıştıkları hâl modellenmez. **KK14.**
Ad alanı modelinde tek bir küresel tablo vardır ve görünüm hiçbir kaydı silmez.

#### Ad Alanı: Bir Görünürlük Eşlemesi

```js
// olcum-agi/adalani.mjs — ad alani bir gorunurluk eslemesi olarak (model)
// Tek bir kuresel surec tablosu vardir; ad alani o tablonun uzerine bir
// gorunurluk eslemesi koyar. Kayit silinmez, yalniz gorunmez olur.

const TABLO = [                       // kuresel tablo: numara, ad, sahip ad alani
  [1, "yonetici", "kok"], [17, "gunluk-toplayici", "kok"], [41, "zamanlayici", "kok"],
  [102, "okuma-toplayici", "A"], [103, "okuma-isleyici", "A"], [110, "okuma-kuyrugu", "A"],
  [121, "dogrulayici", "A"], [140, "dogrulama-onbellegi", "A"],
  [205, "faturalama", "B"], [206, "fatura-yazici", "B"], [219, "tarife-cozucu", "B"],
  [233, "is-emri", "B"], [301, "gecelik-is", "C"], [305, "gecelik-yazici", "C"],
];

// Gorunurluk kurali: bir ad alani yalniz kendi kayitlarini gorur ve onlari
// 1'den baslayan yerel numaralarla numaralandirir. Kok her kaydi gorur.
function gorunum(adAlani) {
  const kayitlar = adAlani === "kok" ? TABLO : TABLO.filter((k) => k[2] === adAlani);
  return kayitlar.map(([kuresel, ad], i) => ({ yerel: i + 1, kuresel, ad }));
}

const ALANLAR = ["kok", "A", "B", "C"];
console.log(`kuresel tabloda ${TABLO.length} kayit var; hicbir gorunum kayit silmez`);
console.log("ad alani".padEnd(10) + "goren".padEnd(8) + "gormeyen".padEnd(10) + "yerel -> kuresel");
for (const a of ALANLAR) {
  const g = gorunum(a);
  console.log(a.padEnd(10) + `${g.length}/${TABLO.length}`.padEnd(8) +
    String(TABLO.length - g.length).padEnd(10) +
    g.slice(0, 3).map((k) => `${k.yerel}->${k.kuresel}`).join(" ") + (g.length > 3 ? " ..." : ""));
}

console.log("\nayni kayit iki numara tasir:");
for (const a of ["A", "B", "C"]) {
  const ilk = gorunum(a)[0];
  console.log(`  ${ilk.ad.padEnd(20)} ${a} icinde ${ilk.yerel}, kok icinde ${ilk.kuresel}`);
}

const HAVUZ = 32768;
console.log(`\ndelik: numara havuzu ortaktir (${HAVUZ} numara, ${TABLO.length} kullanimda)`);
const enBuyuk = ALANLAR.slice(1)
  .map((a) => [a, gorunum(a).length]).sort((x, y) => y[1] - x[1])[0];
console.log(`  kok ${gorunum("kok").length} kaydin hepsini gorur; ad alanlari birbirini gormez`);
console.log(`  en cok kayit acan ad alani ${enBuyuk[0]} (${enBuyuk[1]} kayit); ` +
  `havuzu tuketen ad alani digerlerini de durdurur`);
console.log(`  gorunmeyen kayit yine de kaynak tuketir: kok gorunumunde ` +
  `${TABLO.length - gorunum("A").length} kayit A'nin disinda ama ayni makinede`);
```

```
kuresel tabloda 14 kayit var; hicbir gorunum kayit silmez
ad alani  goren   gormeyen  yerel -> kuresel
kok       14/14   0         1->1 2->17 3->41 ...
A         5/14    9         1->102 2->103 3->110 ...
B         4/14    10        1->205 2->206 3->219 ...
C         2/14    12        1->301 2->305

ayni kayit iki numara tasir:
  okuma-toplayici      A icinde 1, kok icinde 102
  faturalama           B icinde 1, kok icinde 205
  gecelik-is           C icinde 1, kok icinde 301

delik: numara havuzu ortaktir (32768 numara, 14 kullanimda)
  kok 14 kaydin hepsini gorur; ad alanlari birbirini gormez
  en cok kayit acan ad alani A (5 kayit); havuzu tuketen ad alani digerlerini de durdurur
  gorunmeyen kayit yine de kaynak tuketir: kok gorunumunde 9 kayit A'nin disinda ama ayni makinede
```

Ad alanı bir silme değil, bir **filtredir**. On dört kaydın hepsi tabloda durur; A görünümü
beşini, B dördünü, C ikisini görür. Aynı kayıt iki numara taşır — okuma toplayıcı A içinde 1'dir,
kök içinde 102 — ve bu ikilik yalıtımın tam olarak neye dayandığını gösterir: **numaranın kendisi
değil, numaranın hangi tablodan okunduğu** ayrılmıştır.

Deliğin üç adı vardır. Kök görünümü on dört kaydın hepsini görür; yalıtım aşağı doğru değil,
yalnız yana doğru çalışır. Numara havuzu ortaktır: 32.768 numaranın tümü paylaşılır ve havuzu
tüketen bir ad alanı diğerlerinin kayıt açmasını da engeller. Ve görünmeyen kayıt yok değildir —
A'nın görmediği dokuz kayıt aynı makinede işlemci ve bellek tüketmeyi sürdürür. Ad alanının
bedeli bayt değil, bir eşleme tablosudur; kapattığı ortam farkı ise sıfırdır.

#### Denetim Grubu: Bir Pay Dağıtıcı

**KK15.** Sert sınırlı kaynak ile esnek paylaşılan kaynak ayrı modellenir; istekler sırayla
gelir. **KK16.** Sınır aşıldığında verilen karar kaynağın türüne bağlıdır.

```js
// olcum-agi/denetimgrubu.mjs — denetim grubu bir pay dagitici olarak (model)
// Iki ayri davranis modellenir: sert sinirli kaynakta sinir asilinca verilen
// KARAR, esnek paylasilan kaynakta agirliga gore yapilan DAGITIM.

const GRUP = {                          // kurgu bolgesel olcum aginin uc grubu
  toplama:    { bellekSiniriMB: 512, agirlik: 20, islemciTalebi: 60 },
  faturalama: { bellekSiniriMB: 768, agirlik: 50, islemciTalebi: 90 },
  gecelikIs:  { bellekSiniriMB: 256, agirlik: 30, islemciTalebi: 80 },
};

// Sert sinir: her istek sirayla gelir; sinir asilirsa karar kaynagin turune baglidir.
const KARAR = { bellek: "istegi yapan surec sonlandirilir", tanitici: "istek reddedilir" };
const ISTEK = {                         // MB cinsinden ardisik bellek istekleri
  toplama: [180, 140, 120, 90], faturalama: [300, 260, 150, 200], gecelikIs: [120, 100, 90],
};

console.log("sert sinir: bellek (MB)");
console.log("grup".padEnd(12) + "sinir".padEnd(8) + "verilen".padEnd(9) +
  "sigmayan".padEnd(11) + "karar (bellek)");
let toplamVerilen = 0, toplamSigmayan = 0;
for (const [ad, istekler] of Object.entries(ISTEK)) {
  const sinir = GRUP[ad].bellekSiniriMB;
  let kullanim = 0, sigmayan = [];
  for (const i of istekler) (kullanim + i <= sinir) ? (kullanim += i) : sigmayan.push(i);
  toplamVerilen += kullanim; toplamSigmayan += sigmayan.length;
  console.log(ad.padEnd(12) + String(sinir).padEnd(8) + String(kullanim).padEnd(9) +
    `${sigmayan.length} istek`.padEnd(11) + (sigmayan.length ? KARAR.bellek : "-"));
}
console.log(`toplam verilen ${toplamVerilen} MB, sigmayan ${toplamSigmayan} istek; ` +
  `sinirlarin toplami ${Object.values(GRUP).reduce((t, g) => t + g.bellekSiniriMB, 0)} MB`);
console.log(`ayni asim dosya taniticisinda baska bir karar verir: ${KARAR.tanitici}`);

// Esnek paylasim: agirlik bir sinir degil, rekabet halinde pay oranidir.
const TOPLAM_PAY = 100;
const agirlikToplam = Object.values(GRUP).reduce((t, g) => t + g.agirlik, 0);
const talepToplam = Object.values(GRUP).reduce((t, g) => t + g.islemciTalebi, 0);
console.log(`\nesnek paylasim: islemci (toplam ${TOPLAM_PAY} birim, talep ${talepToplam} birim)`);
console.log("grup".padEnd(12) + "agirlik".padEnd(9) + "talep".padEnd(8) +
  "rekabette".padEnd(11) + "tek basina");
for (const [ad, g] of Object.entries(GRUP)) {
  const pay = Math.round((g.agirlik / agirlikToplam) * TOPLAM_PAY);
  console.log(ad.padEnd(12) + String(g.agirlik).padEnd(9) + String(g.islemciTalebi).padEnd(8) +
    String(Math.min(pay, g.islemciTalebi)).padEnd(11) + Math.min(TOPLAM_PAY, g.islemciTalebi));
}
console.log("delik: agirlik bir tavan degildir; rekabet yoksa grup payinin " +
  `${Math.round(Math.min(TOPLAM_PAY, GRUP.gecelikIs.islemciTalebi) / ((GRUP.gecelikIs.agirlik / agirlikToplam) * TOPLAM_PAY) * 10) / 10}` +
  " katini alir");
console.log(`delik: sinirlarin toplami ${Object.values(GRUP).reduce((t, g) => t + g.bellekSiniriMB, 0)}` +
  " MB, makinenin belleginden bagimsizdir; asiri bindirme cekirdek karariyla sonuclanir");
```

```
sert sinir: bellek (MB)
grup        sinir   verilen  sigmayan   karar (bellek)
toplama     512     440      1 istek    istegi yapan surec sonlandirilir
faturalama  768     710      1 istek    istegi yapan surec sonlandirilir
gecelikIs   256     220      1 istek    istegi yapan surec sonlandirilir
toplam verilen 1370 MB, sigmayan 3 istek; sinirlarin toplami 1536 MB
ayni asim dosya taniticisinda baska bir karar verir: istek reddedilir

esnek paylasim: islemci (toplam 100 birim, talep 230 birim)
grup        agirlik  talep   rekabette  tek basina
toplama     20       60      20         60
faturalama  50       90      50         90
gecelikIs   30       80      30         80
delik: agirlik bir tavan degildir; rekabet yoksa grup payinin 2.7 katini alir
delik: sinirlarin toplami 1536 MB, makinenin belleginden bagimsizdir; asiri bindirme cekirdek karariyla sonuclanir
```

Denetim grubunun iki ayrı davranışı vardır ve ikisi karıştırıldığında yanlış beklenti üretir.
Sert sınırda karar keskindir: üç grupta da bir istek sınıra sığmaz ve bellekte verilen karar
isteği yapan sürecin sonlandırılmasıdır — reddedilmek değil, sonlandırılmak. Aynı aşım dosya
tanıtıcısında başka bir karar verir; istek reddedilir ve süreç koşmayı sürdürür. Bu ayrım
kurgudaki gecelik iş için doğrudan sonuç taşır: 256 MB sınırla 220 MB verilir, üçüncü istek
sığmaz ve iş yarıda durur.

Esnek paylaşımda sınır yoktur, **ağırlık** vardır. Talep 230 birim, makine 100 birimdir;
rekabette paylar 20, 50 ve 30 olur. Ama ağırlık bir tavan değildir: rekabet yokken gecelik iş
payının 2,7 katını alır. İkinci delik daha sessizdir — üç sınırın toplamı 1.536 MB'dır ve bu
sayının makinenin belleğiyle bir ilgisi yoktur. Sınırlar makinenin belleğinden büyük
konabilir; aşırı bindirme durumunda karar denetim grubunda değil, çekirdekte verilir. Denetim
grubunun bedeli de bayt değildir, muhasebedir; kapattığı ortam farkı yine sıfırdır.

#### Birleşim Dosya Sistemi: Bir Katman Yığını

**KK17.** Katmanlar gerçek dizinlerdir ve dosyalar gerçekten yazılıp ölçülür; silme işareti bir
dosya adı öneki ile modellenir. **KK18.** Yalnız en üst katman yazılabilir. **KK19.** Paylaşım
hesabı, alt katmanların içerik olarak aynı olduğunu varsayar.

```js
// olcum-agi/katman.mjs — birlesim dosya sistemi bir katman yigini olarak
// Katmanlar GERCEK dizinlerdir; dosyalar gercekten yazilir ve gercekten olculur.
import { mkdirSync, writeFileSync, readdirSync, statSync, copyFileSync, rmSync } from "node:fs";
import { join, dirname } from "node:path";

const KOK = "/tmp/olcum-agi-katman";
const KATMAN = ["katman0-taban", "katman1-kural", "katman2-yazilabilir"];  // altan uste
const ISARET = ".silindi.";                   // silme isareti onekidir; alttakini gizler
const ICERIK = {
  "katman0-taban": { "etc/tarife.json": 4096, "lib/cozumleyici.bin": 61440,
    "lib/kural.bin": 20480, "uygulama/toplayici.mjs": 3072, "uygulama/eski-ayar.json": 1024 },
  "katman1-kural": { "lib/kural.bin": 24576, "etc/kural-surum.txt": 32 },
  "katman2-yazilabilir": { "uygulama/toplayici.mjs": 3584, "uygulama/yeni-kural.mjs": 2048,
    ["uygulama/" + ISARET + "eski-ayar.json"]: 0 },
};

rmSync(KOK, { recursive: true, force: true });
for (const [k, dosyalar] of Object.entries(ICERIK))
  for (const [yol, boyut] of Object.entries(dosyalar)) {
    mkdirSync(dirname(join(KOK, k, yol)), { recursive: true });
    writeFileSync(join(KOK, k, yol), Buffer.alloc(boyut, k.charCodeAt(6)));
  }

const listele = (k, alt = "") => readdirSync(join(KOK, k, alt), { withFileTypes: true })
  .flatMap((g) => g.isDirectory() ? listele(k, join(alt, g.name)) : [join(alt, g.name)]);
const bayt = (k, yol) => statSync(join(KOK, k, yol)).size;
const katmanBayt = (k) => listele(k).reduce((s, y) => s + bayt(k, y), 0);

// Birlestirme: en ustteki katman kazanir; ustteki silme isareti alttakini gizler.
const gorunum = new Map(), golge = [], gizli = [];
for (const k of [...KATMAN].reverse())
  for (const yol of listele(k)) {
    if (yol.split("/").pop().startsWith(ISARET)) { gizli.push(yol.replace(ISARET, "")); continue; }
    gorunum.has(yol) ? golge.push(`${yol} (${gorunum.get(yol)} ustte)`) : gorunum.set(yol, k);
  }
for (const y of gizli) gorunum.delete(y);

console.log("katman".padEnd(22) + "dosya".padEnd(8) + "bayt");
for (const k of KATMAN)
  console.log(k.padEnd(22) + String(listele(k).length).padEnd(8) + katmanBayt(k));
console.log("\nbirlesik gorunum (yol -> saglayan katman)");
for (const [yol, k] of [...gorunum].sort()) console.log("  " + yol.padEnd(28) + k);
console.log(`gorunen ${gorunum.size} dosya, ` +
  `${[...gorunum].reduce((s, [y, k]) => s + bayt(k, y), 0)} bayt`);
console.log(`golgelenen ${golge.length}: ${golge.join(", ")}`);
console.log(`silme isaretiyle gizlenen ${gizli.length}: ${gizli.join(", ")} ` +
  `(alt katmanda hala ${bayt(KATMAN[0], gizli[0])} bayt duruyor)`);
console.log(`okuma icin kopyalanan dosya: 0 (${gorunum.size} dosya yerinde okunur)`);

// Kopya-yukari: alt katmandaki bir dosyaya yazilinca once ust katmana kopyalanir.
const HEDEF = "etc/tarife.json", ALT = gorunum.get(HEDEF);
mkdirSync(dirname(join(KOK, KATMAN[2], HEDEF)), { recursive: true });
copyFileSync(join(KOK, ALT, HEDEF), join(KOK, KATMAN[2], HEDEF));
writeFileSync(join(KOK, KATMAN[2], HEDEF), Buffer.alloc(4096 + 16, 122));
console.log(`\nkopya-yukari: ${HEDEF} ${ALT} icinde ${bayt(ALT, HEDEF)} bayt; ` +
  `16 baytlik degisiklik icin tasinan ${bayt(ALT, HEDEF)} bayt`);
console.log(`  alt katmandaki kopya degismedi (${bayt(ALT, HEDEF)} bayt), ` +
  `ustteki ${bayt(KATMAN[2], HEDEF)} bayt`);

const N = 5;                                  // salt okunur alt katmanlar paylasilir
const alt = katmanBayt(KATMAN[0]) + katmanBayt(KATMAN[1]), ust = katmanBayt(KATMAN[2]);
console.log(`\n${N} ornek alt katmanlari paylasirsa ${alt} + ${N} x ${ust} = ` +
  `${alt + N * ust} bayt; paylasmasaydi ${N} x ${alt + ust} = ${N * (alt + ust)} bayt ` +
  `(kazanc ${N * (alt + ust) - (alt + N * ust)} bayt)`);
rmSync(KOK, { recursive: true, force: true });
```

```
katman                dosya   bayt
katman0-taban         5       90112
katman1-kural         2       24608
katman2-yazilabilir   3       5632

birlesik gorunum (yol -> saglayan katman)
  etc/kural-surum.txt         katman1-kural
  etc/tarife.json             katman0-taban
  lib/cozumleyici.bin         katman0-taban
  lib/kural.bin               katman1-kural
  uygulama/toplayici.mjs      katman2-yazilabilir
  uygulama/yeni-kural.mjs     katman2-yazilabilir
gorunen 6 dosya, 95776 bayt
golgelenen 2: lib/kural.bin (katman1-kural ustte), uygulama/toplayici.mjs (katman2-yazilabilir ustte)
silme isaretiyle gizlenen 1: uygulama/eski-ayar.json (alt katmanda hala 1024 bayt duruyor)
okuma icin kopyalanan dosya: 0 (6 dosya yerinde okunur)

kopya-yukari: etc/tarife.json katman0-taban icinde 4096 bayt; 16 baytlik degisiklik icin tasinan 4096 bayt
  alt katmandaki kopya degismedi (4096 bayt), ustteki 4112 bayt

5 ornek alt katmanlari paylasirsa 114720 + 5 x 9744 = 163440 bayt; paylasmasaydi 5 x 124464 = 622320 bayt (kazanc 458880 bayt)
```

Üç katmanda on dosya vardır, birleşik görünümde altı. Fark iki gölgelemeden ve bir silme
işaretinden gelir: kural ikilisi ile toplayıcı üstteki katmandan okunur, eski ayar dosyası ise
hiç okunmaz. Okuma için **hiçbir dosya kopyalanmaz** — altı dosyanın altısı bulunduğu katmanda
yerinde okunur. Bu, önceki dersin "kütüphane kümesi örnek başına bir kez yinelenir" satırını
düzelten sayıdır: beş örnek alt katmanları paylaştığında toplam 163.440 bayt, paylaşmasaydı
622.320 bayt olurdu; kazanç 458.880 bayttır.

Bedel yazma anında ödenir. Alt katmandaki bir dosyaya yazmak için o dosya önce üst katmana
kopyalanır: 16 baytlık bir değişiklik için 4.096 bayt taşınır. Oran dosya boyutuyla büyür ve
tarife dosyası yerine çözümleyici ikilisine yazılsaydı taşınan bayt 61.440 olurdu.

Delik üç yerdedir. Silinen dosya silinmemiştir: eski ayar dosyası görünümde yoktur ama alt
katmanda 1.024 bayt olarak durmayı sürdürür — çıktıyı okuyan biri onu bulabilir. Kopya-yukarı
gecikmesi yazma anında ortaya çıkar ve önceden görünmez. Ve dosyaların hepsi çıktının içinde
olsa da dosya sahipliği ile ad eşleştirme kuralı alttaki dosya sisteminden gelir; ilk dersin
sınır kovasında duran boyut tam olarak buydu.

#### Özet

- Üç mekanizmanın hiçbiri yeni kaynak üretmez; üçü de tek bir kaynağın üzerine bir kural koyar
  ve kuralın olduğu yerde delik vardır.
- Ad alanı bir görünürlük eşlemesidir: 14 kayıtlık tabloda görünümler 5, 4 ve 2 kayıt görür,
  kök 14'ünü de görür. Numara havuzu ortaktır ve görünmeyen kayıt kaynak tüketmeyi sürdürür.
- Denetim grubu bir pay dağıtıcıdır ve iki davranışı vardır: sert sınırda karar (bellekte süreci
  sonlandırma, tanıtıcıda isteği reddetme), esnek paylaşımda ağırlığa göre dağıtım. Ağırlık tavan
  değildir — rekabet yokken bir grup payının 2,7 katını alır.
- Birleşim dosya sistemi bir katman yığınıdır: 10 dosya 6'ya iner, okuma için 0 dosya kopyalanır,
  beş örnek paylaşımla 458.880 bayt tasarruf eder. Yazma anında 16 baytlık değişiklik 4.096 bayt
  taşır.
- Ortamdan fark çıkaran mekanizma yalnız birleşim dosya sistemidir; ad alanı ile denetim grubu
  0 fark kapatır, ölçüt ayırır. Silinen dosya alt katmanda durur, dosya sahipliği ve ad
  eşleştirme kuralı alttan gelir.

#### Sonraki Adım

Bu üç mekanizma tek bir makinenin çekirdeğine aittir ve o çekirdeğin sunduğu biçimde çalışır.
Ölçülen katman yığını ise bir makineye ait değildir: dizinlerden oluşan bir yığındır, kopyalanıp
taşınabilir. Peki taşındığı yerde aynı biçimde açılacağını ne garanti eder — katmanların hangi
sırayla yığılacağını, hangi yapılandırmayla çalıştırılacağını ve bir katmanın taşınırken
bozulmadığını kim yazar? Sonraki ders bu sorunun karşılığı olan belirtimlerin ortak yapısını
ele alır: bir imaj biçimi belirtiminin taşıdığı sözü üç parçaya ayırır, özet zincirini denetleyen
küçük bir doğrulayıcı yazar ve taşınabilirliğin nereye kadar gittiğini — aynı belirtime uyan iki
çalışma zamanı arasında neyin değişmediğini, neyin değiştiğini — sayar.

### Konteyner Standartları

Source: https://academia.sh/tr/kurslar/konteynerler/konteyner-standartlari

Bir imaj biçimi belirtiminin taşıdığı söz üç parçaya ayrılır ve özet zincirini denetleyen bir doğrulayıcı yazılır; taşınabilirliğin nereye kadar gittiği sayılır: aynı belirtime uyan iki çalışma zamanı arasında ne değişmez, ne değişir.

Önceki ders yalıtımı kuran üç mekanizmayı ölçtü ve üçünün de tek bir makinenin çekirdeğine ait
olduğunu gösterdi. Ölçülen katman yığını ise bir makineye ait değildir: dizinlerden oluşan bir
yığındır ve kopyalanıp taşınabilir. Taşınabilir olmak, taşındığı yerde **aynı biçimde
açılacağının** güvencesi değildir. Katmanların hangi sırayla yığılacağı, hangi yapılandırmayla
çalıştırılacağı ve bir katmanın yolda bozulmadığı bir yerde yazılı olmalıdır.

O yazının türü **belirtimdir**. Bu derste hiçbir belirtimin, gövdenin ya da ürünün adı geçmez;
belirtimler ortak yapılarıyla anılır, çünkü ölçülen şey bir belgenin adı değil, o belgenin
taşıdığı sözdür. **KK20.** Bir imaj biçimi belirtimi üç şey tanımlar: bir **yapılandırma
nesnesi**, bir **katman listesi** ve içerik adresli bir **özet zinciri**. Bir çalışma zamanı
belirtimi ise bu üçünü okuyup bir süreç başlatmanın kurallarını tanımlar.

Sözün üç parçası üç ayrı işe yarar. Yapılandırma nesnesi neyin çalıştırılacağını söyler: giriş
noktası, çalışma dizini, kullanıcı, ortam değişkenleri. Katman listesi çıktının hangi
parçalardan oluştuğunu ve her parçanın kaç bayt olduğunu söyler. Özet zinciri ise parçaların
**kimliğini ve sırasını** birbirine bağlar; zincir sayesinde bir katmanın değişip değişmediği,
değiştiyse yığının hangi noktasından itibaren başka bir çıktı olduğu hesaplanabilir.

İki belirtim türünün arasındaki sınır da burada çizilir. İmaj biçimi belirtimi **durur hâldeki**
çıktıyı tanımlar: hangi baytlar, hangi sırayla, hangi kimlikle. Çalışma zamanı belirtimi ise
**koşan hâli** tanımlar: bu tanımı okuyan bir çalışma zamanının hangi adımları hangi sırayla
atacağını. Birinin bittiği yer diğerinin başladığı yerdir ve bu dersin ikinci bölümünde ölçülen
farklar tam olarak o sınırın üstünde durur. Bir belirtimin adı bilinmeden de sözü okunabilir,
çünkü söz her zaman aynı biçimdedir: neyi bağlıyor, neyi yoruma bırakıyor.

Kurgudaki bölgesel ölçüm ağının çıktısı bu üç parçayla tanımlanır. Aşağıdaki doğrulayıcı o
tanımı denetler.

#### Zincirin Denetlenmesi

**KK21.** İmaj tanımı kurgudur; özetler `node:crypto` ile gerçekten hesaplanır ve ilk on iki
onaltılık basamağa kısaltılır. **KK22.** Zincir kuralı tektir: `z(0) = fark(0)` ve
`z(n) = özet(z(n-1) + " " + fark(n))`. **KK23.** Katmanların içerik boyutları önceki derslerin
ölçtüğü değerlerdir.

```js
// olcum-agi/dogrulayici.mjs — bir imaj biciminin ozet zincirini denetleyen dogrulayici
// Imaj tanimi KURGUDUR; ozetler node:crypto ile gercekten hesaplanir.
import { createHash } from "node:crypto";

const ozet = (v) => "s256:" + createHash("sha256").update(v).digest("hex").slice(0, 12);

// Katman icerikleri (kurgu): her katman bir bayt bloguyla temsil edilir.
const katmanIcerik = [
  ["taban-kok", Buffer.alloc(7_600_000, 48)],
  ["cozumleyici", Buffer.alloc(44_800_000, 49)],
  ["kural-paketi", Buffer.alloc(31_200_000, 50)],
  ["uygulama", Buffer.alloc(50_787, 51)],
];

// Belirtimin tasidigi soz uc parcadir: yapilandirma nesnesi, katman listesi,
// icerik adresli ozet zinciri. Zincir: z(0)=ozet(fark0), z(n)=ozet(z(n-1)+" "+fark(n)).
const zincirle = (farklar) =>
  farklar.reduce((z, f, i) => [...z, i === 0 ? f : ozet(z[i - 1] + " " + f)], []);

function imajTanimi(icerikler) {
  const katmanlar = icerikler.map(([ad, blok]) => ({ ad, boyut: blok.length, fark: ozet(blok) }));
  const zincir = zincirle(katmanlar.map((k) => k.fark));
  const yapilandirma = { girisNoktasi: "gecelik-is", calismaDizini: "/uygulama",
    kullanici: "olcum", zincir };
  return { yapilandirma, yapilandirmaOzeti: ozet(JSON.stringify(yapilandirma)), katmanlar };
}

// Dogrulayici: her katmanin ozeti yeniden hesaplanir, sonra zincir yeniden kurulur.
function dogrula(tanim, icerikler) {
  const bozuk = [];
  tanim.katmanlar.forEach((k, i) => { if (ozet(icerikler[i][1]) !== k.fark) bozuk.push(i); });
  const beklenen = zincirle(tanim.katmanlar.map((k) => k.fark));
  const zincirKirigi = beklenen.findIndex((z, i) => z !== tanim.yapilandirma.zincir[i]);
  return { bozuk, zincirKirigi, kirikSayisi: zincirKirigi < 0 ? 0 : beklenen.length - zincirKirigi };
}

const TANIM = imajTanimi(katmanIcerik);
console.log("katman listesi (icerik adresli)");
TANIM.katmanlar.forEach((k, i) => console.log("  " + String(i).padEnd(3) + k.ad.padEnd(14) +
  String(k.boyut).padStart(9) + " bayt  fark " + k.fark + "  zincir " + TANIM.yapilandirma.zincir[i]));
console.log("yapilandirma nesnesi ozeti: " + TANIM.yapilandirmaOzeti);
console.log("saglam tanim: " + JSON.stringify(dogrula(TANIM, katmanIcerik)));

// Tek bir bayt degistirilir: bagin nerede koptugu ve kac halkanin etkilendigi.
const bozukIcerik = katmanIcerik.map(([a, b], i) => i === 1
  ? [a, Buffer.concat([b.subarray(0, 5), Buffer.from("X"), b.subarray(6)])] : [a, b]);
const sonuc = dogrula(TANIM, bozukIcerik);
console.log("\n1 numarali katmanda 1 bayt degistirildi (44800000 baytin biri)");
console.log("  ozeti tutmayan katman: " +
  sonuc.bozuk.map((i) => `${i} (${TANIM.katmanlar[i].ad})`).join(", "));
console.log("  yeni fark: " + ozet(bozukIcerik[1][1]) + ", tanimdaki: " + TANIM.katmanlar[1].fark);

// Bozuk katman tanimla birlikte yeniden yayimlanirsa zincir nerede kirilir?
const yenidenTanim = imajTanimi(bozukIcerik);
const kirik = TANIM.yapilandirma.zincir
  .map((z, i) => z === yenidenTanim.yapilandirma.zincir[i]).indexOf(false);
console.log("  zincir " + kirik + ". halkada kirilir; " +
  (TANIM.yapilandirma.zincir.length - kirik) + "/" + TANIM.yapilandirma.zincir.length +
  " halka degisir, alttaki " + kirik + " halka aynen kalir");
console.log("  yapilandirma ozeti de degisir: " + yenidenTanim.yapilandirmaOzeti);
console.log("  yeniden tasinacak bayt: " +
  bozukIcerik.slice(kirik).reduce((t, [, b]) => t + b.length, 0) + "/" +
  katmanIcerik.reduce((t, [, b]) => t + b.length, 0));
```

```
katman listesi (icerik adresli)
  0  taban-kok       7600000 bayt  fark s256:1048de0c7a68  zincir s256:1048de0c7a68
  1  cozumleyici    44800000 bayt  fark s256:b0c7e6c28bfa  zincir s256:926ac9255c57
  2  kural-paketi   31200000 bayt  fark s256:99a2f404c480  zincir s256:08f1f6ea4359
  3  uygulama          50787 bayt  fark s256:3fbeb763e66c  zincir s256:ebdca65649c3
yapilandirma nesnesi ozeti: s256:06c1368bd196
saglam tanim: {"bozuk":[],"zincirKirigi":-1,"kirikSayisi":0}

1 numarali katmanda 1 bayt degistirildi (44800000 baytin biri)
  ozeti tutmayan katman: 1 (cozumleyici)
  yeni fark: s256:9b5c4ad8de20, tanimdaki: s256:b0c7e6c28bfa
  zincir 1. halkada kirilir; 3/4 halka degisir, alttaki 1 halka aynen kalir
  yapilandirma ozeti de degisir: s256:212730e2163b
  yeniden tasinacak bayt: 76050787/83650787
```

Doğrulayıcı sağlam tanımda hiçbir kusur bulmaz: bozuk katman listesi boş, zincir kırığı yok.
Sonra 44.800.000 baytlık katmanın **tek bir baytı** değiştirilir ve o katmanın içerik özeti
tutmaz. Bu, içerik adresliliğin tanımıdır — ad içeriğin kendisinden türetildiği için içerik
değiştiğinde ad da değişir ve eşleşme tek bir karşılaştırmayla yakalanır. Yakalamanın hangi
baytta olduğunu bilmek gerekmez ve aranan bayt sayısı da sonucu değiştirmez: 44,8 milyon baytta
bir bayt ile yarısı değişmiş bir katman aynı biçimde, tek bir eşitsizlikle raporlanır. Bu, bir
katmanı ada göre değil içeriğe göre adlandırmanın tek cümlelik karşılığıdır.

Zincirin katkısı bir adım ötededir. Bozuk katman tanımla birlikte yeniden yayımlanırsa zincir
**1. halkada kırılır**: dört halkanın üçü değişir, alttaki bir halka aynen kalır. Yani bir
katmanın değişmesi kendisini ve **üstündeki her şeyi** yeni bir kimliğe taşır, altındakileri
taşımaz. Bu asimetri taşınacak baytı belirler: 83.650.787 baytlık yığından 76.050.787 baytı
yeniden taşınmalıdır, 7.600.000 baytlık taban olduğu yerde kalır. Zincirin sırayı da bağlaması
bu yüzdendir — katmanların yeri değiştirilirse aynı farklar başka bir zincir üretir ve iki çıktı
aynı sayılmaz.

Yapılandırma nesnesinin özeti de değişir, çünkü zincir onun içinde yazılıdır. Bu, üç parçanın
tek bir kimliğe bağlandığı yerdir: bir çıktıyı tek bir özet adlandırır ve o özet hem neyin
çalıştırılacağını hem hangi baytlardan oluştuğunu kapsar.

Doğrulamanın da bir bedeli vardır ve yalıtım bütçesinin bu derse düşen kalemi odur: tam
doğrulama yığının tamamını okur — 83.650.787 bayt — ve dört içerik özeti ile üç zincir halkası
hesaplar. Bedel çıktı büyüdükçe doğrusal artar ve her taşımada yeniden ödenir. Bunun karşılığında
alınan şey tek bir baytın değişmediğinin **kanıtlanabilir** olmasıdır; bir katmanın yalnız
adına bakan bir denetimde bu kanıt yoktur.

Zincirin bağlamadığı şey de yazılmalıdır. Özet, katmanın **içeriğini** bağlar, o içeriğin nasıl
üretildiğini bağlamaz. Aynı katmanın iki kez üretilmesi aynı özeti verecek diye bir söz yoktur;
zincir yalnız elindeki baytların değişmediğini söyler, o baytların yeniden üretilebilir olduğunu
söylemez. Bu ayrım kurgudaki ağda somuttur: doğrulayıcı bozulmuş bir katmanı yakalar, ama iki
ayrı makinede iki kez üretilen çözümleyici katmanı farklı özet verirse bunu bir bozulma olarak
değil, iki ayrı çıktı olarak rapor eder. Doğrulayıcının söylediği ile söylemediği arasındaki
sınır tam olarak buradadır ve sınırı bilmeden okunan bir "doğrulandı" satırı olduğundan
fazlasını taahhüt eder.

#### Taşınabilirlik Nereye Kadar Gidiyor

Özet zinciri baytların taşındığını denetler. Taşınan baytların **aynı biçimde davranacağını**
denetlemez. Belirtimin bağladığı şeyler ile yoruma bıraktığı şeyler ayrı ayrı sayılmalıdır.

**KK24.** On özellik iki kümeye ayrılır — belirtimin bağladığı beş, yoruma bıraktığı beş — ve
ayrım modeldir. **KK25.** Taşınabilirlik oranı özellik sayısı üzerinden hesaplanır;
ağırlıklandırma yapılmaz.

```js
// olcum-agi/tasinabilirlik.mjs — ayni belirtime uyan iki calisma zamani karsilastirilir (model)
// Ozellikler iki kumeye ayrilir: belirtimin BAGLADIGI ve belirtimin YORUMA BIRAKTIGI.

const BAGLI = ["kok dosya sistemi duzeni", "katman sirasi ve birlesim kurali",
  "icerik ozeti algoritmasi", "giris noktasi ve calisma dizini", "ortam degiskeni kumesi"];
const YORUMA_BIRAKILAN = ["kaynak siniri yorumu", "gunluk cikisinin yazildigi yer",
  "varsayilan ag kipi", "kullanici eslemesi", "durdurma sinyali ve bekleme suresi"];

// Iki calisma zamani; bagli ozelliklerde ayni degeri tasimak zorundadir.
const CZ = {
  "calisma zamani 1": {
    "kok dosya sistemi duzeni": "katman yigini", "katman sirasi ve birlesim kurali": "alttan uste",
    "icerik ozeti algoritmasi": "s256", "giris noktasi ve calisma dizini": "gecelik-is @ /uygulama",
    "ortam degiskeni kumesi": "tanimda yazan 6 degisken",
    "kaynak siniri yorumu": "sinir asilinca surec durdurulur",
    "gunluk cikisinin yazildigi yer": "dosya", "varsayilan ag kipi": "kopru",
    "kullanici eslemesi": "birebir", "durdurma sinyali ve bekleme suresi": "nazik, 10 sn",
  },
  "calisma zamani 2": {
    "kok dosya sistemi duzeni": "katman yigini", "katman sirasi ve birlesim kurali": "alttan uste",
    "icerik ozeti algoritmasi": "s256", "giris noktasi ve calisma dizini": "gecelik-is @ /uygulama",
    "ortam degiskeni kumesi": "tanimda yazan 6 degisken",
    "kaynak siniri yorumu": "sinir asilinca istek reddedilir",
    "gunluk cikisinin yazildigi yer": "dosya", "varsayilan ag kipi": "ana makine agi",
    "kullanici eslemesi": "kaydirmali", "durdurma sinyali ve bekleme suresi": "nazik, 10 sn",
  },
};

const [A, B] = Object.keys(CZ);
const ayni = (o) => CZ[A][o] === CZ[B][o];
console.log("ozellik".padEnd(38) + "kume".padEnd(10) + "iki calisma zamaninda");
for (const o of [...BAGLI, ...YORUMA_BIRAKILAN])
  console.log(o.padEnd(38) + (BAGLI.includes(o) ? "bagli" : "yoruma").padEnd(10) +
    (ayni(o) ? "ayni" : `FARK  ${CZ[A][o]} / ${CZ[B][o]}`));

const degisen = YORUMA_BIRAKILAN.filter((o) => !ayni(o));
console.log(`\nbagli ozellik ${BAGLI.length}, yoruma birakilan ${YORUMA_BIRAKILAN.length}`);
console.log(`degismeden kalan ${BAGLI.length + (YORUMA_BIRAKILAN.length - degisen.length)}/10, ` +
  `degisen ${degisen.length}/10: ${degisen.join(", ")}`);
console.log(`tasinabilirlik: ciktinin kendisi %100 tasinir (ozet zinciri denetlenir), ` +
  `davranisin %${(BAGLI.length + YORUMA_BIRAKILAN.length - degisen.length) * 10}'u tasinir`);
console.log(`onceki derslerin sayimiyla: ortamdan 6 fark cikarildi, ` +
  `calisma zamani farki olarak ${degisen.length} yeni boyut geri geldi`);
```

```
ozellik                               kume      iki calisma zamaninda
kok dosya sistemi duzeni              bagli     ayni
katman sirasi ve birlesim kurali      bagli     ayni
icerik ozeti algoritmasi              bagli     ayni
giris noktasi ve calisma dizini       bagli     ayni
ortam degiskeni kumesi                bagli     ayni
kaynak siniri yorumu                  yoruma    FARK  sinir asilinca surec durdurulur / sinir asilinca istek reddedilir
gunluk cikisinin yazildigi yer        yoruma    ayni
varsayilan ag kipi                    yoruma    FARK  kopru / ana makine agi
kullanici eslemesi                    yoruma    FARK  birebir / kaydirmali
durdurma sinyali ve bekleme suresi    yoruma    ayni

bagli ozellik 5, yoruma birakilan 5
degismeden kalan 7/10, degisen 3/10: kaynak siniri yorumu, varsayilan ag kipi, kullanici eslemesi
tasinabilirlik: ciktinin kendisi %100 tasinir (ozet zinciri denetlenir), davranisin %70'u tasinir
onceki derslerin sayimiyla: ortamdan 6 fark cikarildi, calisma zamani farki olarak 3 yeni boyut geri geldi
```

Bağlı beş özellikte iki çalışma zamanı aynı değeri taşır ve taşımak zorundadır: kök dosya
sistemi düzeni, katman sırası ile birleşim kuralı, içerik özeti algoritması, giriş noktası ile
çalışma dizini, ortam değişkeni kümesi. Bunlar belirtimin sözüdür ve uyan her çalışma zamanında
sabittir. Yoruma bırakılan beş özellikte ise bağlayıcılık yoktur; modelde beşinden **üçü** iki
çalışma zamanında farklı çıkar.

Farkın hangi üçü olduğu önemlidir. Kaynak sınırı yorumu, önceki derste ölçülen kararın ta
kendisidir: bir çalışma zamanında sınırı aşan süreç durdurulur, diğerinde istek reddedilir.
Kurgudaki gecelik iş için bu, aynı çıktının bir yerde yarıda kesilmesi, başka bir yerde hatayı
alıp sürmesi demektir. Ağ kipi ile kullanıcı eşlemesi de aynı türdendir — çıktı taşınır, davranış
taşınmaz.

Aynı değeri taşıyan iki özellik de dikkatle okunmalıdır. Günlük çıkışının yazıldığı yer ile
durdurma sinyali modelde iki çalışma zamanında da aynıdır; ama ikisi de yoruma bırakılan
kümededir, yani aynı olmaları bir güvence değil, bir rastlantıdır. Bu ikisine dayanan bir
varsayım — örneğin gecelik işin durdurulurken on saniye bekleyeceği — üçüncü bir çalışma zamanında
sessizce bozulabilir ve bozulduğu yalnız koşumda görünür. İlk dersteki ayrım burada aynen
yinelenir: bildirilmiş fark bir karar konusudur, bildirilmemiş fark bir sürprizdir. Yoruma
bırakılan beş özelliğin hepsi, değerleri şu an aynı olsa bile, bildirilmemiş fark
adaylarıdır.

Kapatmanın yolu da bu sayımdan çıkar. Yoruma bırakılan beş özellik, çalışma zamanının
yapılandırmasında açıkça yazıldığı anda bağlı kümeye geçer: kaynak sınırı yorumu seçilir, ağ
kipi belirtilir, kullanıcı eşlemesi bildirilir. Bu, belirtimi değiştirmez; belirtimin boş
bıraktığı yeri **çıktıyı çalıştıran tarafın** doldurmasıdır. Bedeli de bellidir — beş satırlık
bir bildirim ve o bildirimin her çalışma zamanı için ayrı ayrı doğrulanması. Bildirim yazılmadığı
sürece taşınabilirlik %70'te kalır ve kalan %30 her taşımada yeniden ölçülmek zorundadır; yazıldığı
anda ise ölçüm bir kez yapılır ve sonucu künyede durur.

Sayı da buradadır. Çıktının kendisi **%100 taşınır**, çünkü özet zinciri baytların aynı olduğunu
denetler. Davranışın ise %70'i taşınır. **Yalıtımın delindiği yer bu derste belirtimin
kendisidir**: özet zinciri baytları bağlar, davranışı bağlamaz ve bağlamadığı her özellik yeni
bir fark boyutudur. İlk ders ortamdan 6 fark çıkarmıştı; çalışma zamanı farkı olarak 3 boyut geri
gelir. Fark sıfırlanmaz, yeri değişir: ortam künyesinden çalışma zamanı yapılandırmasına taşınır.
Kazancın ölçülebilir kalması da bu yüzden yeni bir künyeye bağlıdır — bu kez çalışma zamanının
künyesine.

#### Özet

- Bir imaj biçimi belirtimi üç şey tanımlar: yapılandırma nesnesi, katman listesi ve içerik
  adresli özet zinciri. Üçü tek bir kimliğe bağlanır.
- Doğrulayıcı 44.800.000 baytlık bir katmandaki tek bayt değişikliğini içerik özetinden yakalar;
  içerik adresliliğin tanımı budur.
- Zincir asimetriktir: bir katman değişince kendisi ve üstündekiler yeni kimlik alır, altındakiler
  almaz. Modelde 4 halkanın 3'ü değişir ve 83.650.787 baytın 76.050.787'si yeniden taşınır.
- Aynı belirtime uyan iki çalışma zamanı bağlı beş özellikte aynıdır; yoruma bırakılan beş
  özellikten üçü farklıdır. Çıktı %100, davranış %70 taşınır.
- Belirtim baytları bağlar, davranışı bağlamaz: ortamdan çıkarılan 6 farkın karşılığında çalışma
  zamanı farkı olarak 3 boyut geri gelir. Fark yok olmaz, künye değiştirir.

#### Sonraki Adım

Bu ders çıktının kimliğini ve taşınabilirliğinin sınırını yazdı; hepsi **durur hâldeki** çıktı
üzerineydi. Çıktı bir kez çalıştırıldığında ise ölçülmemiş bir şey ortaya çıkar: koşan konteyner
durur, ama arkasında ne bırakır? Önceki derste yazılabilir katmanın kopya-yukarı ile büyüdüğü
görülmüştü; ağ adı, birim ve süreç kaydı da bir yerde tutulur. Sonraki ders yaşam döngüsünü bir
durum makinesi olarak kurar, her geçişte tutulan kaynağı sayar ve durdurulmuş ama temizlenmemiş
konteynerlerin bir dönem boyunca ne kadar biriktirdiğini ölçer.

### Konteyner Yaşam Döngüsü

Source: https://academia.sh/tr/kurslar/konteynerler/konteyner-yasam-dongusu

Oluşturma, çalıştırma, durdurma ve temizleme bir durum makinesi olarak kurulur; her geçişte tutulan kaynak sayılır ve durdurulmuş ama temizlenmemiş konteynerlerin bir dönem boyunca biriktirdiği katman, ağ adı ve birim ölçülür.

Önceki dersler yalıtımın ne olduğunu, neyi kaç bayta ayırdığını ve çıktının nasıl adlandırılıp
taşındığını ölçtü. Hepsi tek bir ana bakıyordu: çıktının **var olduğu** ana. Bir konteynerin
ise başladığı, koştuğu, durduğu ve ortadan kalktığı ayrı anlar vardır ve bu anlar arasında
tutulan şey her seferinde aynı değildir.

Bu ders yaşam döngüsünü bir **durum makinesi** olarak kurar. Beş durum vardır — yok,
oluşturulmuş, çalışıyor, durdurulmuş, temizlenmiş — ve dört geçiş bu durumlar arasında dolaşır.
Ölçülen şey durumların kendisi değil, her geçişten sonra **tutulmaya devam eden kaynaktır**.
Kurgudaki bölgesel ölçüm ağı bu ölçüm için elverişlidir: gecelik toplu iş her gece başlar ve
biter, servisler kusur sonrası yeniden başlatılır ve hiçbir koşum kendiliğinden ortadan
kalkmaz.

Sorunun neden ölçüm gerektirdiği de buradan görülür. Önceki dört ders yalıtımın bedelini
çıktının **var olma** anında saydı: kaç bayt taşınıyor, kaç ölçüt ayrılıyor, kaç mertebe
başlangıç gecikmesi isteniyor. O sayıların hiçbiri zamanla çarpılmamıştı. Bir konteyner ise
tek bir nesne değil, tekrar tekrar üretilen bir nesnedir; aynı çıktı bir dönemde onlarca kez
oluşturulur ve her oluşum kendi izini bırakır. Bu dersin eklediği eksen zamandır ve eklenmesi
tek bir soruyu ölçülebilir kılar: bedel bir kez mi ödeniyor, yoksa her koşumda yeniden mi.

**KK26.** Yaşam döngüsü beş durumludur ve geçiş tablosunda yazmayan geçiş yoktur. **KK27.**
Tutulan kaynak dört kalemdir: yazılabilir katman, ağ adı, birim, süreç kaydı. **KK28.**
Yazılabilir katmanın büyümesi model değeridir; ilk koşum 340 MB, ikinci koşum 96 MB ekler.

#### Dört Geçiş ve Tutulan Kaynak

```js
// olcum-agi/dongu.mjs — konteyner yasam dongusu bir durum makinesi olarak (model)
// Her gecis, tutulan kaynagi degistirir. Tutulan kaynak dort kalemdir.

const KALEM = ["katmanMB", "agAdi", "birim", "surecKaydi"];

// Gecis tablosu: [gecis, once, sonra, kalem degisimleri]
const GECIS = [
  ["olustur",  "yok",          "olusturulmus", { katmanMB: 0, agAdi: 1, birim: 1 }],
  ["calistir", "olusturulmus", "calisiyor",    { surecKaydi: 1, katmanMB: 340 }],
  ["durdur",   "calisiyor",    "durdurulmus",  { surecKaydi: -1 }],
  ["calistir", "durdurulmus",  "calisiyor",    { surecKaydi: 1, katmanMB: 96 }],
  ["durdur",   "calisiyor",    "durdurulmus",  { surecKaydi: -1 }],
  ["temizle",  "durdurulmus",  "temizlenmis",  { katmanMB: "sifirla", agAdi: -1 }],
];

let durum = "yok", durdurulmusHal = null;
const tutulan = Object.fromEntries(KALEM.map((k) => [k, 0]));
const yaz = (etiket) => console.log(etiket.padEnd(13) + durum.padEnd(15) +
  KALEM.map((k) => String(tutulan[k]).padStart(k.length)).join("   "));

console.log("gecis".padEnd(13) + "durum".padEnd(15) + KALEM.join("   "));
yaz("(baslangic)");
for (const [ad, once, sonra, delta] of GECIS) {
  if (durum !== once) throw new Error(`${ad}: ${once} bekleniyordu, ${durum} bulundu`);
  for (const [k, d] of Object.entries(delta)) tutulan[k] = d === "sifirla" ? 0 : tutulan[k] + d;
  durum = sonra;
  if (durum === "durdurulmus") durdurulmusHal = { ...tutulan };
  yaz(ad);
}

console.log("\ndurdurulmus ama temizlenmemis bir konteynerin tuttugu: " +
  KALEM.map((k) => `${k} ${durdurulmusHal[k]}`).join(", "));
console.log("durmakla dusen tek kalem surec kaydidir; diger uc kalem yerinde kalir");
console.log("temizlemeden sonra da sifirlanmayan kalem: birim " + tutulan.birim +
  " (birimin yasam dongusu konteynerinkinden ayridir)");
```

```
gecis        durum          katmanMB   agAdi   birim   surecKaydi
(baslangic)  yok                   0       0       0            0
olustur      olusturulmus          0       1       1            0
calistir     calisiyor           340       1       1            1
durdur       durdurulmus         340       1       1            0
calistir     calisiyor           436       1       1            1
durdur       durdurulmus         436       1       1            0
temizle      temizlenmis           0       0       1            0

durdurulmus ama temizlenmemis bir konteynerin tuttugu: katmanMB 436, agAdi 1, birim 1, surecKaydi 0
durmakla dusen tek kalem surec kaydidir; diger uc kalem yerinde kalir
temizlemeden sonra da sifirlanmayan kalem: birim 1 (birimin yasam dongusu konteynerinkinden ayridir)
```

Tablo iki şeyi ayırır. **Oluşturma** ile **çalıştırma** aynı adım değildir: oluşturma ağ adını
ve birimi ayırır ama hiçbir süreç başlatmaz; yazılabilir katman sıfır bayttır. Çalıştırma süreç
kaydını ekler ve katmanı büyütür — ilk koşumda 340 MB, ikinci koşumda 96 MB daha. Büyüme
kalıcıdır, çünkü yazılabilir katman koşumdan koşuma taşınır; ikinci durduruluşta tutulan 436
MB'dır.

Asıl ayrım durdurma satırındadır. **Durmakla düşen tek kalem süreç kaydıdır.** Yazılabilir
katman, ağ adı ve birim yerinde kalır. Bir konteynerin durmuş olması, bıraktığı şeylerin
serbest kaldığı anlamına gelmez — durdurma bir süreç işlemidir, kaynak işlemi değildir.
Serbest bırakan geçiş temizlemedir ve o da her şeyi bırakmaz: birim temizlemeden sonra da 1'de
kalır, çünkü birimin yaşam döngüsü konteynerinkinden ayrıdır. Kurgudaki ağda bu, gecelik işin
ara dosyalarını yazdığı birimin, işi çalıştıran konteyner silindikten sonra da diskte durması
demektir.

Oluşturulmuş ama hiç çalıştırılmamış durum da ayrı bir kalemdir ve kolay gözden kaçar: katman
sıfır bayttır, süreç kaydı sıfırdır, ama ağ adı ile birim çoktan ayrılmıştır. Bu durumdaki bir
konteyner disk açısından bedavaya yakındır, ad uzayı açısından değildir — adı tutar ve o adla
ikinci bir konteyner oluşturulamaz. Bedelin tek bir kalemle ölçülmesinin yanıltıcı olduğu yer
budur; dört kalemin dördü ayrı ayrı sayılmadıkça "boş duran konteyner bir şey tüketmiyor"
cümlesi kurulabilir ve cümle üç kalemde yanlıştır.

Durum makinesinin geçiş denetimi de bir şey söyler: tabloda yazmayan geçiş yoktur ve model
denetimi düşürür. Durdurulmuş bir konteyner yeniden çalıştırılabilir, ama temizlenmiş bir
konteyner çalıştırılamaz. Yaşam döngüsünün tek yönlü olan tek geçişi temizlemedir; ondan sonra
geri dönüş yoktur.

#### Bir Dönem Boyunca Birikim

Tek bir konteynerin 436 MB tutması küçük bir sayıdır. Sayının anlamı, kaç konteynerin bu hâlde
beklediğine bağlıdır.

**KK29.** Bir dönem 30 gündür; servislerin koşum sıklıkları model girdisidir. **KK30.** Disk
512.000 MB, ağ adı havuzu 4.096 addır. **KK31.** Temizleme kuralı "son N dönemi tut"
biçimindedir; kısmi temizleme modellenmez.

```js
// olcum-agi/birikim.mjs — durdurulmus ama temizlenmemis konteynerlerin birikimi (model)
// Bir donem 30 gundur. Her kosum bir konteyner birakir: yazilabilir katman, ag adi, birim.

const DONEM_GUN = 30, DISK_MB = 512_000, AD_HAVUZU = 4_096, CIKTI_MB = 84.11;
const SERVIS = {                        // kurgu bolgesel olcum agi: siklik ve kalan katman
  gecelikIs:   { herKacGun: 1,  katmanMB: 436 },
  faturalama:  { herKacGun: 3,  katmanMB: 52 },
  toplayici:   { herKacGun: 4,  katmanMB: 26 },
  dogrulayici: { herKacGun: 6,  katmanMB: 14 },
  isEmri:      { herKacGun: 10, katmanMB: 8 },
};

console.log(`bir donem = ${DONEM_GUN} gun; temizleme yapilmazsa donem sonunda tutulan`);
console.log("servis".padEnd(14) + "kosum".padEnd(8) + "kosum basina".padEnd(14) + "donem birikimi");
let donemMB = 0, donemKosum = 0;
for (const [ad, s] of Object.entries(SERVIS)) {
  const kosum = Math.floor(DONEM_GUN / s.herKacGun);
  donemMB += kosum * s.katmanMB; donemKosum += kosum;
  console.log(ad.padEnd(14) + String(kosum).padEnd(8) + `${s.katmanMB} MB`.padEnd(14) +
    `${kosum * s.katmanMB} MB`);
}
console.log(`toplam: ${donemKosum} konteyner, ${donemMB} MB, ${donemKosum} ag adi, ` +
  `${donemKosum} birim, 0 surec kaydi`);
console.log(`donem birikimi tek bir ciktinin ${Math.round(donemMB / CIKTI_MB)} katidir ` +
  `(${CIKTI_MB} MB)`);

console.log("\ntemizleme kurali tarandi (son N donem tutulur)");
console.log("N".padEnd(6) + "katman".padEnd(13) + "ag adi".padEnd(10) + "birim".padEnd(9) +
  "diskin yuzdesi");
for (const N of [0, 1, 2, 3, 6, 12]) {
  const mb = N * donemMB, adet = N * donemKosum;
  console.log(String(N).padEnd(6) + `${mb} MB`.padEnd(13) + String(adet).padEnd(10) +
    String(adet).padEnd(9) + "%" + ((mb / DISK_MB) * 100).toFixed(1));
}

const diskDonem = DISK_MB / donemMB, adDonem = AD_HAVUZU / donemKosum;
console.log(`\nhic temizlenmezse ${DISK_MB} MB disk ${diskDonem.toFixed(1)} donemde ` +
  `(${Math.round(diskDonem * DONEM_GUN)} gun) dolar`);
console.log(`${AD_HAVUZU} adlik ag adi havuzu ${adDonem.toFixed(1)} donemde ` +
  `(${Math.round(adDonem * DONEM_GUN)} gun) tukenir`);
console.log(`once tukenen: ${diskDonem < adDonem ? "disk" : "ad havuzu"}; ` +
  `iki sinir arasindaki oran ${(adDonem / diskDonem).toFixed(1)}`);
console.log(`N=1 kurali diskin %${((donemMB / DISK_MB) * 100).toFixed(1)}'ini surekli tutar; ` +
  `N=0 kurali hicbir kosumu geri getirilemez kilar`);
```

```
bir donem = 30 gun; temizleme yapilmazsa donem sonunda tutulan
servis        kosum   kosum basina  donem birikimi
gecelikIs     30      436 MB        13080 MB
faturalama    10      52 MB         520 MB
toplayici     7       26 MB         182 MB
dogrulayici   5       14 MB         70 MB
isEmri        3       8 MB          24 MB
toplam: 55 konteyner, 13876 MB, 55 ag adi, 55 birim, 0 surec kaydi
donem birikimi tek bir ciktinin 165 katidir (84.11 MB)

temizleme kurali tarandi (son N donem tutulur)
N     katman       ag adi    birim    diskin yuzdesi
0     0 MB         0         0        %0.0
1     13876 MB     55        55       %2.7
2     27752 MB     110       110      %5.4
3     41628 MB     165       165      %8.1
6     83256 MB     330       330      %16.3
12    166512 MB    660       660      %32.5

hic temizlenmezse 512000 MB disk 36.9 donemde (1107 gun) dolar
4096 adlik ag adi havuzu 74.5 donemde (2234 gun) tukenir
once tukenen: disk; iki sinir arasindaki oran 2.0
N=1 kurali diskin %2.7'ini surekli tutar; N=0 kurali hicbir kosumu geri getirilemez kilar
```

Bir dönemde 55 konteyner geride kalır ve 13.876 MB tutar. Sayının büyüklüğü tek bir kaynaktan
gelir: gecelik iş toplamın 13.080 MB'ını, yani **%94'ünü** tek başına üretir. Sıklık ile koşum
başına bırakılan katmanın çarpımı bunu açıklar — otuz koşum ve koşum başına 436 MB. Diğer dört
servisin toplamı 796 MB'dır. Temizleme kuralı yazılırken bakılacak yerin hangisi olduğu bu
çarpımdan okunur; servis sayısı değil, sıklık ile katman büyümesinin çarpımı belirler. İş emri
servisi dönemde üç kez koşar ve 24 MB bırakır; ona uygulanacak bir kural toplamı ölçülebilir
biçimde değiştirmez — 13.876 MB'ın 24'ünü kapatır. Aynı kural gecelik işe uygulandığında ise
13.080 MB'ı, yani birikimin %94'ünü kapatır. Beş servise tek bir kural yazmak ile en çok üreten
servise ayrı bir kural yazmak arasındaki fark, bu iki sayının oranıdır.

Dönem birikimi tek bir çıktının **165 katıdır**. İlk derste ölçülen 84,11 MB'lık çıktı, bir ay
temizlenmemiş koşumların yanında küçük kalır. Yalıtım bütçesinin bu derse düşen kalemi budur:
yalıtımın bedeli yalnız çıktının boyutu değil, o çıktının **her koşumda bıraktığı izdir**. İlk
dersin sorduğu soru "fark ortamdan çıkarılırsa çıktı ne kadar büyür" idi ve yanıt 84,11 MB
olmuştu; bu ders aynı soruyu bir dönem boyunca sorar ve yanıt 13.876 MB olur. İki sayı aynı
kararın iki farklı ölçeğidir ve yalnız birincisine bakan bir hesap, ikincisini bir sürpriz
olarak bırakır.

Tarama kuralın maliyetini doğrudan verir. N=1'de disk sürekli %2,7 dolu tutulur, N=3'te %8,1,
N=12'de %32,5. Hiç temizlenmezse disk 36,9 dönemde — 1.107 günde — dolar. Ağ adı havuzu ise 74,5
dönemde tükenir; iki sınır arasındaki oran 2,0'dır, yani disk ad havuzundan iki kat önce biter.
Bu sıralama kuralın hangi kaynağa göre yazılacağını belirler: bir temizleme kuralı adları
korumak için değil, diski korumak için konur.

Kuralın diğer ucu da sayılmalıdır. N=0 diski hiç tutmaz ama koşumdan geriye hiçbir şey
bırakmaz — yazılabilir katman, kusurlu bir koşumun ardından geriye kalan tek kanıttır ve
temizlenmiş bir konteynerin katmanı geri getirilemez. Kurgudaki gecelik iş yarıda kaldığında
bakılacak yer o katmandır: kısmi fatura dosyaları, işlenmiş sayaç sayısı, son yazılan kayıt.
N=0 kuralı bu kanıtı koşum biter bitmez siler; N=12 kuralı diskin üçte birini kalıcı olarak
tutar. Arada yazılacak sayı bir tercih değil, iki ölçülmüş büyüklük arasındaki dengedir:
saklanan gün sayısı ile tutulan disk yüzdesi. Tarama tabloyu verdiği için tartışma "temizlik
yapılsın mı" olmaktan çıkar, "kaç dönemlik kanıt kaç yüzde diske değer" olur.

#### Yalıtımın Delindiği Yer

Yaşam döngüsünün üç deliği vardır ve üçü de "durmuş olmak" ile "yok olmak" arasındaki farktan
gelir.

Birincisi **yazılabilir katmandır**. Durmuş bir konteynerin katmanı diskte durur ve içindeki her
şey — gecelik işin ara dosyaları, kısmi fatura çıktıları, günlükler — okunabilir hâldedir.
Önceki derste ölçülen silme işareti burada ikinci kez karşımıza çıkar: katmandan silinen bir
dosya alt katmanda kalıyordu; şimdi katmanın kendisi de konteyner durduktan sonra kalıyor.
Yalıtım koşarken çalışır, koşum bittikten sonra geride kalan bayt üzerinde çalışmaz.

İkincisi **ağ adıdır**. Durdurulmuş bir konteynerin adı ad uzayında yer tutmayı sürdürür; aynı
adla yeni bir konteyner oluşturulamaz. Bu, kurgudaki gecelik iş için doğrudan bir kusur
kaynağıdır — bir gecenin işi temizlenmeden ertesi gecenin işi aynı adla başlatılamaz.

Üçüncüsü **birimdir** ve en sessiz olanıdır: temizleme geçişi bile onu bırakmaz. Yaşam
döngüsünün dışında kalan tek kalem odur ve dört kalemin içinde tek bir geçişle sıfırlanmayan
tek kalem olduğu için, birikimi de kendi kuralıyla ölçülmek zorundadır.

Üçünün ortak nedeni de yazılmalıdır: **temizleme geçişini kendiliğinden yapan bir şey yoktur.**
Oluşturma, çalıştırma ve durdurma bir işin akışı içinde gerçekleşir — gecelik iş başlar, biter,
durur. Temizleme o akışın parçası değildir; ayrıca istenmesi gereken bir geçiştir. Yaşam
döngüsünün dört geçişinden üçü işin kendisi tarafından tetiklenir, dördüncüsü bir kural
tarafından. Kural yazılmadığında döngü dört geçişli değil, üç geçişli çalışır ve yukarıdaki
tarama tam olarak o üç geçişli hâlin bedelini sayar.

#### Özet

- Yaşam döngüsü beş durumlu bir durum makinesidir; oluşturma ile çalıştırma ayrı adımlardır —
  oluşturma ağ adı ile birimi ayırır, çalıştırma süreç kaydını ekler ve katmanı büyütür.
- Durmakla düşen tek kalem süreç kaydıdır. Yazılabilir katman, ağ adı ve birim yerinde kalır;
  modelde durdurulmuş bir konteyner 436 MB, 1 ağ adı ve 1 birim tutar.
- Temizleme de her şeyi bırakmaz: birim temizlemeden sonra da durur, çünkü yaşam döngüsü
  konteynerinkinden ayrıdır. Temizleme tek yönlü olan tek geçiştir.
- Bir dönemde 55 konteyner ve 13.876 MB birikir; bunun %94'ünü tek başına gecelik iş üretir.
  Birikim tek bir çıktının 165 katıdır.
- Temizleme kuralı tarandı: N=1'de diskin %2,7'si, N=3'te %8,1'i, N=12'de %32,5'i sürekli
  tutulur. Hiç temizlenmezse disk 36,9 dönemde dolar, ad havuzu 74,5 dönemde tükenir; kural diski
korumak için yazılır, adları korumak için değil.
- Yalıtım koşarken çalışır, koşumdan sonra kalan bayt üzerinde çalışmaz: katman okunabilir
  kalır, ad yer tutar, birim hiç gitmez. Temizleme geçişini kendiliğinden yapan bir şey yoktur;
  dört geçişten yalnız üçü işin akışı içinde gerçekleşir.

#### Sonraki Adım

Bu konu yalıtımın ne olduğunu ölçtü: hangi ölçütün ayrıldığını, ayrılmanın kaç megabayt ve kaç
mertebe başlangıç gecikmesi istediğini, hangi mekanizmanın hangi kuralı koyduğunu, çıktının nasıl
adlandırılıp taşındığını ve koştuktan sonra geride ne bıraktığını. Bütün bu ölçümlerin ortak bir
boşluğu var: çalıştırılan şeyin **nereden geldiği** hiç sorulmadı. Ölçülen 84,11 MB'lık çıktı,
gölgelenen katmanlar ve özet zinciri hep hazır kabul edildi — oysa o yığını biri üretti.

Sorular buradan başlar. O çıktı nasıl üretiliyor; hangi adım hangi katmanı doğuruyor? Neden tek
bir dosya değil de bir **yığın** — bir dosya olsaydı ne kaybedilirdi, yığın olduğu için ne
kazanılıyor? Katmanların sırası kim tarafından ve neye göre belirleniyor; sıra değişince
zincirin yeniden hesaplanan bölümü ne kadar büyüyor? Sonraki konu çıktının üretimini ele alır ve
ilk sorusu şudur: bir çıktının katmanlara ayrılmasının ölçülebilir karşılığı nedir?

## İmajlar

Yapı çıktısının katmanlara ayrılması: katman modelinin yeniden kullanımı, imaj tanım dosyasının yönergeleri ve derleme bağlamı, yönerge sırasının önbellek isabetine ve derleme süresine etkisi, derleme araçlarının çıktıdan ayrılması, temel imaj seçiminin boyut, güncellenebilirlik ve saldırı yüzeyi dengesi, sürüm izlenebilirliği için etiketleme düzeni, imaj depoları ve tarama, imzalama ve en az yetkiyle çalışma.

### İmaj ve Katman Modeli

Source: https://academia.sh/tr/kurslar/konteynerler/imaj-ve-katman-modeli

İmaj sıralı bir katman listesi olarak ölçülür: iki servisin ortak temel üzerine kurulduğunda kaç katmanı paylaştığı, kaç baytın bir kez saklandığı ve düz kopyalamaya göre kazancın ne olduğu sayılır; bir alt katman değişince kaç üst katmanın zincir kimliğinin geçersizleştiği ve silinen bir dosyanın depoda kaç bayt olarak durduğu ölçülür.

Önceki konu yalıtımın ne olduğunu tanımladı ve bir sürecin çevresine çekilen sınırın neyi kaç
bayta yalıttığını ölçtü. O ölçümlerin tamamı boyunca çalıştırılan şeyin **nereden geldiği** hiç
sorulmadı. Yalıtılan süreç bir dosya sisteminin içinde açılır ve o dosya sistemi bir yerden
gelir; bu ders onun nereden geldiğini ölçer.

Önceki kursta yapı çıktısı tek bir nesne olarak tanımlanmış, değişmezlik ilkesi ve içerik özeti
orada ölçülmüştü. Konteyner çıktısı aynı ilkeye bağlıdır — bir kez üretilir, yerinde
değiştirilmez, kimliği içeriğinin özetidir — ama tek dosya değildir. **İmaj**, sıralı bir
**katman** listesidir ve bu ayrımın nedeni sayılabilir: iki servis aynı tabanı kullandığında tek
dosyalık çıktı iki tam kopya saklar, katmanlı çıktı ortak bölümü bir kez saklar. Sorular
şunlardır: kaç katman paylaşılır, kaç bayt bir kez saklanır, bir alt katman değişince üstünde ne
geçersizleşir?

**IM1.** Aşağıdaki katman modeli `node` ile kurulur ve gerçek dizinler üzerinde koşar; gerçek bir
konteyner çalıştırılmaz. Katman bir dizindir, imaj bir katman adı listesidir. **IM2.** İçerik
özeti, sıralı yol ve bayt çiftleri üzerinden alınan sha256 değerinin ilk on iki onaltılık
basamağıdır; kısaltma okunabilirlik içindir ve önceki kursta kullanılan biçimle aynıdır.
**IM3.** Kurgudaki bölgesel ölçüm ağının iki servisi ölçülür: su sayacı okumalarını toplayan
servis ve okumaları doğrulayan servis. Ağ ve servisler kurgudur.

#### Katman, İmaj, Zincir Kimliği

Katman modelinin üç parçası vardır. **Katman**, bir dosya kümesidir ve kendi içerik özetiyle
adlandırılır. **İmaj**, alttan üste sıralı bir katman listesidir; çalıştırılacak dosya sistemi bu
listenin sırayla üst üste bindirilmesiyle oluşur. **Zincir kimliği**, bir katmanın imaj içindeki
konumunu adlandırır ve önceki zincir kimliği ile katmanın içerik özetinden türetilir — aynı
katman iki farklı imajda aynı içerik özetini taşır ama altındaki yığın farklıysa farklı zincir
kimliği alır. Ayrım, ölçümün geri kalanının dayandığı yerdir.

**IM4.** Katman içerikleri satır sayısıyla ölçeklenen üretilmiş dosyalardır; ölçülen büyüklükler
bu üretimin gerçek bayt karşılığıdır, bir ürünün gerçek imaj boyutu değildir.

```js
// olcum-agi/katman.mjs — katmanlari gercek dizinler olarak kurar (model)
import { mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

const KOK = "katmanlar";
const ozetle = (s) => createHash("sha256").update(s).digest("hex").slice(0, 12);

// Katman govdesi: satir sayisiyla olceklenen, uretimi yinelenebilir dosya icerigi.
export const govde = (ad, satir) =>
  Array.from({ length: satir }, (_, i) => `// ${ad} ${i}\nexport const a${i} = ${i * 7};\n`).join("");

export const KATMAN = {
  "temel-kok": { "kok/kabuk.js": govde("kabuk", 30), "kok/saat.js": govde("saat", 14) },
  "ortak-kutuphane": { "lib/olcu.js": govde("olcu", 120), "lib/kod.js": govde("kod", 46) },
  "ortak-ayar": { "ayar/bolge.json": '{"bolge":"kuzey","sayac":41820}\n' },
  "toplayici-bagimlilik": { "bagimlilik/kuyruk.js": govde("kuyruk", 190), "bagimlilik/csv.js": govde("csv", 62) },
  "toplayici-kaynak": { "uygulama/topla.js": govde("topla", 34), "uygulama/giris.js": govde("giris", 9) },
  "dogrulayici-bagimlilik": { "bagimlilik/kural.js": govde("kural", 88) },
  "dogrulayici-kaynak": { "uygulama/dogrula.js": govde("dogrula", 27), "uygulama/giris.js": govde("dgiris", 11) },
};

export const kur = (tanim = KATMAN) => {
  rmSync(KOK, { recursive: true, force: true });
  for (const [ad, dosyalar] of Object.entries(tanim))
    for (const [yol, icerik] of Object.entries(dosyalar)) {
      mkdirSync(`${KOK}/${ad}/${yol}`.split("/").slice(0, -1).join("/"), { recursive: true });
      writeFileSync(`${KOK}/${ad}/${yol}`, icerik);
    }
};

// Diskteki gercek dizini gezer; katmanin dosya listesi, bayti ve icerik ozeti buradan cikar.
export const gez = (ad, alt = "") => {
  const kok = `${KOK}/${ad}${alt}`;
  return readdirSync(kok, { withFileTypes: true }).flatMap((g) =>
    g.isDirectory() ? gez(ad, `${alt}/${g.name}`) : [`${alt}/${g.name}`.slice(1)]).sort();
};

export const katman = (ad) => {
  const yollar = gez(ad);
  const govdeler = yollar.map((y) => readFileSync(`${KOK}/${ad}/${y}`));
  return { ad, dosya: yollar.length, yollar,
    bayt: govdeler.reduce((t, g) => t + g.length, 0),
    ozet: ozetle(yollar.map((y, i) => `${y}\n${govdeler[i]}`).join("\n")) };
};

// Zincir kimligi: onceki zincirin kimligi ile katmanin icerik ozetinden turer.
export const zincir = (adlar) =>
  adlar.reduce((z, ad) => [...z, ozetle((z.at(-1) ?? "") + katman(ad).ozet)], []);

export const TOPLAYICI = ["temel-kok", "ortak-kutuphane", "ortak-ayar", "toplayici-bagimlilik", "toplayici-kaynak"];
export const DOGRULAYICI = ["temel-kok", "ortak-kutuphane", "ortak-ayar", "dogrulayici-bagimlilik", "dogrulayici-kaynak"];
```

Yığının sırası rastgele değildir: altta değişme sıklığı en düşük olan taban, üstte en sık değişen
uygulama kaynağı durur. Bu sıranın karşılığı bir sonraki ölçümde çıkar.

#### Paylaşılan Katmanın Ölçüsü

İki servis aynı taban üzerine kurulur: kök dosyaları, ortak ölçü kütüphanesi ve bölge ayarı üç
katmandır. Üstlerinde her servisin kendi bağımlılık ve kaynak katmanları durur.

```js
// olcum-agi/olc.mjs — iki imajin katman paylasimini ve depo kazancini sayar
import { kur, katman, zincir, TOPLAYICI, DOGRULAYICI } from "./katman.mjs";

kur();
const hepsi = [...new Set([...TOPLAYICI, ...DOGRULAYICI])].map(katman);
console.log("katman".padEnd(24) + "dosya  bayt   icerik ozeti");
for (const k of hepsi)
  console.log(k.ad.padEnd(24) + String(k.dosya).padStart(3) + String(k.bayt).padStart(8) + "   " + k.ozet);

const boyut = (imaj) => imaj.reduce((t, ad) => t + katman(ad).bayt, 0);
const paylasilan = TOPLAYICI.filter((ad) => DOGRULAYICI.includes(ad));
const depo = hepsi.reduce((t, k) => t + k.bayt, 0);
const duz = boyut(TOPLAYICI) + boyut(DOGRULAYICI);

console.log(`\nokuma-toplayici: ${TOPLAYICI.length} katman, ${boyut(TOPLAYICI)} bayt`);
console.log(`dogrulayici    : ${DOGRULAYICI.length} katman, ${boyut(DOGRULAYICI)} bayt`);
console.log(`paylasilan katman ${paylasilan.length}, bir kez saklanan ${boyut(paylasilan)} bayt`);
console.log(`duz kopyalama ${duz} bayt / katman deposu ${hepsi.length} katman ${depo} bayt`);
console.log(`kazanc ${duz - depo} bayt, %${(100 - (depo * 100) / duz).toFixed(1)}`);
console.log("zincir kimlikleri (toplayici): " + zincir(TOPLAYICI).map((z) => z.slice(0, 8)).join(" "));
```

```
katman                  dosya  bayt   icerik ozeti
temel-kok                 2    1497   7388fc361497
ortak-kutuphane           2    5730   9a6817a68730
ortak-ayar                1      32   48b10c98a78a
toplayici-bagimlilik      2    9291   906f2e7219b3
toplayici-kaynak          2    1482   af5db4e03ee4
dogrulayici-bagimlilik    1    3131   fcc132f093eb
dogrulayici-kaynak        2    1363   f578b5ac2648

okuma-toplayici: 5 katman, 18032 bayt
dogrulayici    : 5 katman, 11753 bayt
paylasilan katman 3, bir kez saklanan 7259 bayt
duz kopyalama 29785 bayt / katman deposu 7 katman 22526 bayt
kazanc 7259 bayt, %24.4
zincir kimlikleri (toplayici): 6555bbc9 54daa57b adf0e7c1 3334e87d ad53ce55
```

İki imajın toplam boyutu **29 785 bayt**, katman deposunda saklanan ise **22 526 bayt**: ortak üç
katmanın **7 259 baytı** iki imaj için bir kez saklanır, kazanç yüzde 24,4'tür. Ölçünün okunması
gereken yeri oran değil, oranın **nereden geldiğidir**: kazanç, paylaşılan tabanın toplam içindeki
payı kadardır. Her servisin kendi bağımlılık katmanı büyüdükçe oran düşer, ortak taban büyüdükçe
yükselir. İki servis yerine yirmi servis aynı tabanı kullanırsa aynı 7 259 bayt yirmi imaj için bir
kez saklanır ve tasarruf katman sayısıyla değil, **paylaşan imaj sayısıyla** çarpılır.

Bu, önceki kursun tek dosyalık yapı çıktısına göre kazanılan şeydir. Aynı ölçüde ortamdan çıkarılan
fark da buradadır: ortak kütüphanenin sürümü ve bölge ayarı artık çalıştırılan makinede kurulu
olan bir şey değil, çıktının **içinde** duran ve özetle adlandırılan iki katmandır. Ortamda o
kütüphanenin başka bir sürümünün bulunması ölçümü değiştirmez, çünkü ölçülen dosya sistemi
katmanlardan doğar.

#### Bir Alt Katman Değiştiğinde

Katmanlı çıktının bedeli, katmanların birbirine zincirle bağlı olmasıdır. Ortak kütüphaneye tek bir
satır eklenir ve iki imajda ne değiştiği sayılır.

**IM5.** Değişiklik en küçük ölçekte tutulur — ortak kütüphanenin bir dosyasına tek satır eklenir —
ki geçersizleşen zincirin uzunluğu değişikliğin büyüklüğünden değil, yalnız konumundan gelsin.
**IM6.** Silme, üst katmana konan bir işaret dosyasıyla modellenir: `silindi/` önekiyle yazılan
boş bir yol, alttaki aynı yolu görünümden çıkarır. Gerçek birleşim dosya sistemlerinde bu işaretin
biçimi başkadır, davranışı aynıdır.

```js
// olcum-agi/degis.mjs — alt katman degisince ne gecersizlesir, silinen dosya nerede kalir
import { writeFileSync, readFileSync } from "node:fs";
import { kur, katman, zincir, govde, KATMAN, TOPLAYICI, DOGRULAYICI } from "./katman.mjs";

kur();
const once = { t: zincir(TOPLAYICI), d: zincir(DOGRULAYICI) };
const oncekiOzet = Object.fromEntries([...TOPLAYICI, ...DOGRULAYICI].map((a) => [a, katman(a).ozet]));
const eskiBayt = katman("ortak-kutuphane").bayt;

writeFileSync("katmanlar/ortak-kutuphane/lib/olcu.js", govde("olcu", 121)); // tek satir eklendi
const sonra = { t: zincir(TOPLAYICI), d: zincir(DOGRULAYICI) };
const ozetDegisen = [...new Set([...TOPLAYICI, ...DOGRULAYICI])].filter((a) => katman(a).ozet !== oncekiOzet[a]);
const zincirDegisen = ["t", "d"].map((i) => once[i].filter((z, s) => z !== sonra[i][s]).length);

console.log(`icerik ozeti degisen katman : ${ozetDegisen.length} (${ozetDegisen.join(", ")})`);
console.log(`zincir kimligi gecersizlesen: ${zincirDegisen.reduce((a, b) => a + b)} (imaj basina ${zincirDegisen.join(", ")})`);
console.log(`icerigi ayni kalan ust katman: ${[...new Set([...TOPLAYICI, ...DOGRULAYICI])].length - ozetDegisen.length - 1}`);
console.log(`deponun buyumesi: ${katman("ortak-kutuphane").bayt} bayt (eski surum ${eskiBayt} bayt yerinde durur)`);

// Ust katman: bir yolu golgeler, bir yolu siler. Silme, alt katmani degil gorunumu degistirir.
kur({ ...KATMAN, "toplayici-yama": { "ayar/bolge.json": '{"bolge":"kuzey","sayac":41820,"tur":"yama"}\n',
  "silindi/lib/kod.js": "" } });
const yigin = [...TOPLAYICI, "toplayici-yama"];

const gorunum = new Map();
let golge = 0, silinenBayt = 0;
for (const ad of yigin)
  for (const yol of katman(ad).yollar) {
    if (yol.startsWith("silindi/")) {
      const hedef = yol.slice(8);
      silinenBayt += readFileSync(`katmanlar/${gorunum.get(hedef)}/${hedef}`).length;
      gorunum.delete(hedef);
    } else {
      if (gorunum.has(yol)) { golge++; silinenBayt += readFileSync(`katmanlar/${gorunum.get(yol)}/${yol}`).length; }
      gorunum.set(yol, ad);
    }
  }

const yiginBayt = yigin.reduce((t, ad) => t + katman(ad).bayt, 0);
console.log(`\nyigindaki katman ${yigin.length}, yigindaki toplam bayt ${yiginBayt}`);
console.log(`birlesim gorunumundeki dosya ${gorunum.size}, golgelenen yol ${golge}, silinen yol 1`);
console.log(`gorunumde olmayan ama depoda duran bayt: ${silinenBayt}`);
console.log("gorunumde lib/kod.js var mi: " + gorunum.has("lib/kod.js"));
```

```
icerik ozeti degisen katman : 1 (ortak-kutuphane)
zincir kimligi gecersizlesen: 8 (imaj basina 4, 4)
icerigi ayni kalan ust katman: 5
deponun buyumesi: 5767 bayt (eski surum 5730 bayt yerinde durur)

yigindaki katman 6, yigindaki toplam bayt 18077
birlesim gorunumundeki dosya 8, golgelenen yol 1, silinen yol 1
gorunumde olmayan ama depoda duran bayt: 1559
gorunumde lib/kod.js var mi: false
```

Tek satırlık bir değişiklik **bir** katmanın içerik özetini değiştirir ama **sekiz** zincir
kimliğini geçersizleştirir: iki imajın her birinde değişen katman ve onun üstündeki üç katman.
Üstteki beş ayrı katmanın içeriği bit düzeyinde aynı kalır; geçersizleşen şey içerik değil,
**konumdur**. Değişiklik ne kadar aşağıdaysa geçersizleşen zincir o kadar uzar — bir satırın
maliyeti, satırın kendisi değil, üstünde kaç katman olduğudur.

İkinci bedel depodadır. Değişmezlik ilkesi gereği eski katman silinmez: depo **5 730 baytlık**
sürümü yerinde tutar ve **5 767 baytlık** yenisini yanına ekler. Katman deposu yalnız büyür; iki
sürüm arasında bir satırlık fark olması bunu değiştirmez, çünkü katman içerik özetiyle adlanır ve
içeriği değişen katman yeni bir nesnedir. Küçük ve sık değişen katmanları üste, büyük ve seyrek
değişenleri alta koymanın karşılığı bu iki sayıdır.

Aynı ölçüm ters yönden de okunur. Aynı model üzerinde iki uç denendiğinde sayılar şöyle çıkar: en
üstteki kaynak katmanı değiştiğinde geçersizleşen zincir kimliği toplayıcı imajında **1**,
doğrulayıcı imajında **0**; en alttaki kök katmanı değiştiğinde imaj başına **5**, toplam **10**.
Aynı büyüklükteki bir değişikliğin bedeli konumuna göre birden ona kadar oynar. Yığın sırası bu
yüzden bir düzen tercihi değil, bir
maliyet kararıdır: her katmanın konumu, o katman değiştiğinde ödenecek bedeli önceden belirler.
Ölçünün karar taşıyan biçimi budur — katman sayısı tek başına iyi ya da kötü değildir, katmanın
**hangi sıklıkta değiştiği** ile **üstünde kaç katman olduğu** çarpımı bir bedeldir.

#### Birleşim Görünümünün Gizlediği

Katmanlar üst üste bindirilirken bir **birleşim dosya sistemi** görünümü doğar: aynı yol iki
katmanda varsa üstteki kazanır, üst katmandaki bir silme işareti alttaki dosyayı görünümden
çıkarır. Ölçüm, görünümün gizlediğini sayar. Altı katmanlık yığın **18 077 bayt** taşır, birleşim
görünümünde **8 dosya** görünür; bir yol gölgelenmiş, bir yol silinmiştir. Ama görünümden çıkan
**1 559 bayt** hâlâ depodadır ve imajın parçasıdır.

Yalıtımın delindiği yer burasıdır. Katman modeli dosya sistemini ortamdan çıkarır ve çıktının
içine alır; karşılığında çıktının **geçmişini de** içine alır. Bir üst katmanda silinen dosya
görünümde yoktur, imajda vardır: alt katman diskte durduğu ve içerik özetiyle çözülebildiği için
imajı eline geçiren biri o dosyayı okuyabilir. Silme bir görünüm işlemidir, bir depo işlemi
değil.

```bash
# dogrula.sh — katmanlar diskte gercek dizin mi, silinen dosya hala orada mi
find katmanlar -type f | wc -l
ls katmanlar/toplayici-yama/silindi/lib/
wc -c katmanlar/ortak-kutuphane/lib/kod.js
```

```
14
kod.js
1527 katmanlar/ortak-kutuphane/lib/kod.js
```

Kabuktan bakıldığında sekiz katmanın toplam on dört dosyası diskte durur; üst katmanda silme
işareti bulunan `lib/kod.js`, alt katmanda 1 527 baytlık gerçek içeriğiyle okunabilir hâldedir.
Birleşim görünümü onu göstermez, dosya sistemi gösterir.

#### Katmanın Taşımadığı

Buraya kadarki ölçümler katman modelinin çıktının içine **aldığı** şeyi saydı: dosya sistemi
düzeni, kütüphane sürümü, ayar dosyası. Yalıtım bütçesinin diğer yarısı, içine alınmayanlardır.
Katman bir dosya kümesidir — süreç değildir, çekirdek değildir, saat değildir. Yığının en
altındaki `temel-kok` katmanı da yalnız dosyalardan oluşur; imaj çalıştığında altında duran
çekirdek, çalıştırıldığı makinenin çekirdeğidir.

Bunun üç somut karşılığı vardır. Birincisi işlemci mimarisi: katmanlardaki çalıştırılabilir
dosyalar bir mimari için üretilir, imaj bu bağı içinde taşımaz, dışarıda varsayar. İçerik özeti
dosyaların baytlarını adlandırır, o baytların hangi mimaride anlam taşıdığını değil; aynı özet,
başka bir mimaride çalışmayan bir dosya sistemi tanımlayabilir. İkincisi çekirdek arayüzü: katman
içindeki bir dosya ortamda bulunmayan bir çekirdek yeteneğini çağırırsa imaj kusursuz biçimde
çözülür, zincir kimlikleri doğrulanır ve süreç yine de başlamaz. Üçüncüsü makine künyesi: önceki
kursta yeniden üretilebilirliği bozan kaynaklar arasında sayılan zaman damgası ve mutlak yol,
katman içeriğine yazıldığı anda katmanın içerik özetine karışır. Katman modeli bu kaynakları
kapatmaz; yalnız nereye yazıldıklarını görünür kılar, çünkü artık ölçülebilir bir nesnenin
içindedirler.

Ayrım tek cümleye iner: imaj **ne çalıştırılacağını** taşır, **nerede çalıştırılacağını** taşımaz.
Bu dersin ölçtüğü 22 526 baytlık katman deposu, ortamdan çıkarılan farkın tamamıdır; geri kalanı
makinenin kendisi olarak durur. Yalıtım bütçesinin bu dersteki hesabı üç kalemdir: çıkarılan fark
bir dosya sistemi, bedeli 22 526 bayt ile her değişiklikte sekiz zincir kimliği, delindiği yer ise
görünümden çıkıp depoda kalan 1 559 bayttır.

#### Özet

- İmaj tek dosyalık bir yapı çıktısı değil, alttan üste sıralı bir katman listesidir; her katman
  kendi içerik özetiyle, imaj içindeki her konum ise önceki zincirden ve katman özetinden türeyen
  bir zincir kimliğiyle adlanır.
- Ortak temel üzerine kurulan iki imaj üç katmanı paylaştı: düz kopyalamada 29 785 bayt olan
  toplam, katman deposunda 22 526 bayta indi; 7 259 bayt bir kez saklandı, kazanç yüzde 24,4 oldu.
  Kazanç paylaşan imaj sayısıyla çarpılır, katman sayısıyla değil.
- Ortak kütüphaneye eklenen tek satır bir katmanın içerik özetini değiştirdi ama sekiz zincir
  kimliğini geçersizleştirdi; içeriği bit düzeyinde aynı kalan beş üst katman yeni konum kimliği
  aldı. Değişiklik yığında ne kadar aşağıdaysa geçersizleşen zincir o kadar uzar.
- Değişmezlik gereği eski katman silinmez: 5 730 baytlık sürüm yerinde kalır, 5 767 baytlık yenisi
  yanına eklenir. Katman deposu yalnız büyür.
- Birleşim görünümü siler gibi görünür, depo silmez. Bir yol gölgelendiğinde ve bir yol
  silindiğinde görünümde 8 dosya kaldı, ama görünümün dışına çıkan 1 559 bayt imajın içinde durmayı
  sürdürdü.

#### Sonraki Adım

Bu dersin katmanları elle tanımlandı: hangi dosyanın hangi katmana gireceği bir nesne
sabitinde yazılıydı. Gerçek bir imaj böyle kurulmaz — katmanlar, sırayla uygulanan yönergelerden
doğar ve her yönerge bir katman üretir. Bu da iki yeni soru açar. Birincisi, yönergelerin
kopyalayacağı dosyalar nereden gelir: derleme bağlamı olarak adlandırılan bu kümeye kaç dosya ve
kaç bayt girer, girenlerin kaçı imaja hiç ulaşmaz? İkincisi, bu kümeyi daraltan dışlama kuralı
kaç dosyayı ve kaç baytı keser? Sonraki ders bir imaj tanım dosyası biçimi tanımlar, yönergeleri
sırayla uygulayan bir yorumlayıcı yazar ve derleme bağlamının büyüklüğünü ölçer.

### İmaj Tanım Dosyası

Source: https://academia.sh/tr/kurslar/konteynerler/imaj-tanim-dosyasi

Katmanların elle değil yönergelerden doğduğu ölçülür: beş yönergelik bir tanım dosyası biçimi tanımlanır, yönergeleri sırayla uygulayan bir yorumlayıcı yazılır ve derleme bağlamının büyüklüğü sayılır — ağaca giren dosya ve bayt, dışlama kuralının kestiği pay ve bağlamda olup imaja hiç girmeyen dosyalar.

Önceki dersin katmanları elle tanımlanmıştı: hangi dosyanın hangi katmana gireceği bir nesne
sabitinde yazılıydı. Bu, ölçümü yapmaya yetiyordu ama bir imajın nasıl kurulduğunu anlatmıyordu.
Gerçek bir imaj, sırayla uygulanan **yönergelerden** doğar; her yönerge bir öncekinin üstüne bir
katman koyar ve sonuncusu bittiğinde imaj hazırdır.

Bu ders o yönergeleri ve yönergelerin beslendiği kümeyi ölçer. Kopyalanacak dosyalar bir yerden
gelir: yönergelerin okuyabileceği dosya kümesine **derleme bağlamı** denir ve bağlamın büyüklüğü
bir bedeldir. Ölçülecek üç sayı vardır — bağlama kaç dosya ve kaç bayt girer, dışlama kuralı kaç
dosyayı ve kaç baytı keser, bağlama girip imaja hiç ulaşmayan kaç dosya kalır?

**IM7.** Aşağıdaki yönerge kümesi ve tanım dosyası biçimi bu ders için tanımlanmıştır; beş
yönergeden oluşur ve gerçek imaj tanım dosyası biçimlerinden çok daha küçüktür. Yorumlayıcı `node`
ile yazılan bir modeldir. **IM8.** `calistir` yönergesi gerçek bir komut çalıştırmaz; modellenmiş
bir komut tablosundan dosya üretir — bağımlılık kurulumunun çıktısı böyle temsil edilir.
**IM9.** Ağaç, kurgudaki bölgesel ölçüm ağının okuma toplayıcı servisinin kaynak ağacıdır; dosya
içerikleri satır sayısıyla ölçeklenmiş üretilmiş metindir. **IM10.** Dışlama kuralı önek
eşleşmesiyle çalışır; gerçek kurallar desen dili kullanır, bu ölçümün sayılarını değiştirmez.

#### Beş Yönerge

Tanım dosyası bir metin dosyasıdır ve her satırı bir yönergedir. Yönergeler sırayla uygulanır;
sıra, önceki dersin yığın sırasının aynısıdır — altta duran önce çalışır.

| Yönerge | Ne yapar | Katman üretir mi |
|---|---|---|
| `temel` | Yığının en altına bir başlangıç dosya kümesi koyar | evet |
| `kopyala` | Derleme bağlamından bir yolu imajın içine kopyalar | evet |
| `calistir` | Bir komut çalıştırıp ürettiği dosyaları katman yapar | evet |
| `ayar` | İmajın üstverisine bir değer yazar | hayır |
| `giris` | Konteyner başladığında çalışacak yolu üstveriye yazar | hayır |

Son iki yönerge dosya sistemine dokunmaz; imajın **üstverisini** yazar. Ayrım ölçülecektir: bir
imajın katman sayısı, tanım dosyasının satır sayısı değildir.

#### Derleme Bağlamı

Ağaç diske kurulur ve dışlama kuralının ne kestiği sayılır. Bağlam, yönergelerin okuyabileceği
kümedir; dışlama kuralına takılan bir yol, hiçbir yönerge onu istemese bile okunmaz.

```js
// olcum-agi/proje.mjs — kurgu servisin kaynak agacini diske kurar, derleme baglamini olcer
import { mkdirSync, writeFileSync, readdirSync, readFileSync, rmSync } from "node:fs";

export const KOK = "proje";
export const metin = (ad, satir) => Array.from({ length: satir }, (_, i) => `// ${ad} ${i}\n`).join("");

export const TANIM = `temel     kosum-tabani
kopyala   ayar/bolge.json  /uygulama/ayar/bolge.json
calistir  bagimlilik-kur
kopyala   kaynak           /uygulama/kaynak
ayar      BOLGE=kuzey
giris     /uygulama/kaynak/giris.js
`;

const AGAC = {
  "imaj-tanimi": TANIM,
  "ayar/bolge.json": '{"bolge":"kuzey","sayac":41820}\n',
  "ayar/yerel.json": '{"kapi":8080,"kutuk":"ayrintili"}\n',
  "kaynak/giris.js": metin("giris", 20),
  "kaynak/topla.js": metin("topla", 140),
  "kaynak/kuyruk.js": metin("kuyruk", 95),
  "kaynak/sinama/topla.sinama.js": metin("sinama-topla", 210),
  "kaynak/sinama/kuyruk.sinama.js": metin("sinama-kuyruk", 160),
  "bagimlilik/csv/dizin.js": metin("csv", 900),
  "bagimlilik/kuyruk/dizin.js": metin("kuyruk-bag", 1400),
  "belge/mimari.md": metin("belge", 240),
  "gunluk/kosum-1873.log": metin("gunluk", 2600),
  "yapi-artigi/paket-onceki.js": metin("artik", 1100),
  ".yerel-ayar/duzenleyici.json": metin("duzenleyici", 12),
  "okuma-ornekleri/gun-1.csv": metin("ornek", 380),
};

// Dislama kurali: bu oneklerle baslayan yollar baglama hic alinmaz.
export const DISLAMA = ["bagimlilik/", "gunluk/", "yapi-artigi/", ".yerel-ayar/", "belge/"];

export const kurAgac = () => {
  rmSync(KOK, { recursive: true, force: true });
  for (const [yol, icerik] of Object.entries(AGAC)) {
    mkdirSync(`${KOK}/${yol}`.split("/").slice(0, -1).join("/"), { recursive: true });
    writeFileSync(`${KOK}/${yol}`, icerik);
  }
};

export const gez = (kok, alt = "") =>
  readdirSync(`${kok}${alt}`, { withFileTypes: true }).flatMap((g) =>
    g.isDirectory() ? gez(kok, `${alt}/${g.name}`) : [`${alt}/${g.name}`.slice(1)]).sort();

export const bayt = (yol) => readFileSync(`${KOK}/${yol}`).length;
export const disarida = (yol, kural = DISLAMA) => kural.some((d) => yol.startsWith(d));
export const baglam = (kural = DISLAMA) => gez(KOK).filter((y) => !disarida(y, kural));
export const toplam = (yollar) => yollar.reduce((t, y) => t + bayt(y), 0);
```

```js
// olcum-agi/olcbaglam.mjs — agac, dislama ve baglam buyuklugu
import { kurAgac, gez, bayt, disarida, baglam, toplam, KOK, TANIM } from "./proje.mjs";

kurAgac();
const tumu = gez(KOK);
const kesilen = tumu.filter((y) => disarida(y));
console.log("imaj tanimi:\n" + TANIM);
console.log("ust dizin".padEnd(16) + "dosya   bayt   baglamda");
const grup = {};
for (const y of tumu) (grup[y.includes("/") ? y.split("/")[0] + "/" : "(kok)"] ??= []).push(y);
for (const [d, yollar] of Object.entries(grup))
  console.log(d.padEnd(16) + String(yollar.length).padStart(3) + String(toplam(yollar)).padStart(8) +
    "   " + (disarida(yollar[0]) ? "hayir" : "evet"));
console.log(`\nagac toplami    : ${tumu.length} dosya, ${toplam(tumu)} bayt`);
console.log(`dislama kesti   : ${kesilen.length} dosya, ${toplam(kesilen)} bayt`);
console.log(`derleme baglami : ${baglam().length} dosya, ${toplam(baglam())} bayt ` +
  `(agacin %${((toplam(baglam()) * 100) / toplam(tumu)).toFixed(1)} bayti)`);
```

```
imaj tanimi:
temel     kosum-tabani
kopyala   ayar/bolge.json  /uygulama/ayar/bolge.json
calistir  bagimlilik-kur
kopyala   kaynak           /uygulama/kaynak
ayar      BOLGE=kuzey
giris     /uygulama/kaynak/giris.js

ust dizin       dosya   bayt   baglamda
.yerel-ayar/      1     206   hayir
ayar/             2      66   evet
bagimlilik/       2   35280   hayir
belge/            1    3010   hayir
gunluk/           1   37890   hayir
(kok)             1     203   evet
kaynak/           5   10505   evet
okuma-ornekleri/  1    4830   evet
yapi-artigi/      1   14290   hayir

agac toplami    : 15 dosya, 106280 bayt
dislama kesti   : 6 dosya, 90676 bayt
derleme baglami : 9 dosya, 15604 bayt (agacin %14.7 bayti)
```

Ağaç **15 dosya ve 106 280 bayttır**; dışlama kuralı **6 dosya ve 90 676 baytı** keser, bağlama
**9 dosya ve 15 604 bayt** kalır — ağacın bayt olarak yüzde 14,7'si. Kesilen payın büyüklüğü
tesadüf değildir: bir kaynak ağacının hacminin çoğu kurulu bağımlılıklar, kütükler ve önceki yapı
artıklarıdır. Dışlama kuralı yazılmazsa bu 90 676 bayt her derlemede okunur ve bir sonraki dersin
ölçeceği önbellek kararına da girer.

Kuralın atladığı bir şey de sayıdadır: `okuma-ornekleri/` dizini bağlamda kalmıştır ve 4 830 bayt
taşır. Dışlama kuralı yalnız yazıldığı yolları keser; yazılmayan her yol bağlamdadır.

#### Yorumlayıcı

Yorumlayıcı tanım dosyasını satır satır okur, her satır için ayrı bir katman dizini açar ve
yönergeyi o dizine uygular. Kopyalanan yolların kaydı tutulur; ölçümün sonundaki fark buradan
çıkar.

```js
// olcum-agi/yorumla.mjs — yonergeleri sirayla uygular, her yonergeden bir katman uretir (model)
import { mkdirSync, writeFileSync, readFileSync, rmSync, statSync } from "node:fs";
import { gez, disarida, metin, DISLAMA, KOK } from "./proje.mjs";

const TABAN = { "kok/kabuk.js": metin("kabuk", 60), "kok/kosum.js": metin("kosum", 420) };
const KOMUT = { "bagimlilik-kur": { "uygulama/bagimlilik/csv.js": metin("csv-kurulu", 900),
  "uygulama/bagimlilik/kuyruk.js": metin("kuyruk-kurulu", 1400) } };
const yaz = (dizin, dosyalar) => {
  for (const [yol, icerik] of Object.entries(dosyalar)) {
    mkdirSync(`${dizin}/${yol}`.split("/").slice(0, -1).join("/"), { recursive: true });
    writeFileSync(`${dizin}/${yol}`, icerik);
  }
};

export const derle = (tanim, kural = DISLAMA) => {
  rmSync("imaj", { recursive: true, force: true });
  const okunan = new Set(), ustveri = {}, katmanlar = [];
  tanim.trim().split("\n").forEach((satir, i) => {
    const [yonerge, ...arg] = satir.trim().split(/\s+/);
    const dizin = `imaj/kat-${i}`;
    mkdirSync(dizin, { recursive: true });
    if (yonerge === "temel") yaz(dizin, TABAN);
    else if (yonerge === "calistir") yaz(dizin, KOMUT[arg[0]]);
    else if (yonerge === "kopyala") {
      const [kaynak, hedef] = arg;
      const tekil = !statSync(`${KOK}/${kaynak}`).isDirectory();
      const bas = tekil || kaynak === "." ? "" : `${kaynak}/`;
      const alt = (tekil ? [kaynak] : gez(KOK, bas ? `/${kaynak}` : "")).filter((y) => !disarida(y, kural));
      for (const y of alt) {
        const ic = `${dizin}/${hedef.slice(1)}` + (tekil ? "" : `/${y.slice(bas.length)}`);
        mkdirSync(ic.split("/").slice(0, -1).join("/"), { recursive: true });
        writeFileSync(ic, readFileSync(`${KOK}/${y}`));
        okunan.add(y);
      }
    } else ustveri[yonerge] = arg.join(" ");
    const yollar = gez(dizin);
    katmanlar.push({ sira: i, yonerge, arg: arg.join(" "), dosya: yollar.length,
      bayt: yollar.reduce((t, y) => t + statSync(`${dizin}/${y}`).size, 0) });
  });
  return { katmanlar, okunan, ustveri };
};
```

```js
// olcum-agi/olcyorum.mjs — yorumlayiciyi kosturur, baglam ile imaj arasindaki farki sayar
import { derle } from "./yorumla.mjs";
import { kurAgac, baglam, toplam, TANIM } from "./proje.mjs";

kurAgac();
const { katmanlar, okunan, ustveri } = derle(TANIM);
console.log("sira yonerge   arguman".padEnd(58) + "tur          dosya   bayt");
for (const k of katmanlar)
  console.log(`  ${k.sira}  ${k.yonerge.padEnd(9)} ${k.arg}`.padEnd(58) +
    (k.dosya ? "dosya sistemi" : "ustveri      ") + String(k.dosya).padStart(4) + String(k.bayt).padStart(8));

const dosyaKatmani = katmanlar.filter((k) => k.dosya > 0);
const imajBayt = dosyaKatmani.reduce((t, k) => t + k.bayt, 0);
const girmeyen = baglam().filter((y) => !okunan.has(y));
const sinama = [...okunan].filter((y) => y.includes("/sinama/"));
console.log(`\nustveri: ${JSON.stringify(ustveri)}`);
console.log(`imaj: ${dosyaKatmani.length} dosya sistemi katmani, ${katmanlar.length - dosyaKatmani.length} ustveri yonergesi, ${imajBayt} bayt`);
console.log(`baglamdan okunan ${okunan.size} dosya, ${toplam([...okunan])} bayt`);
console.log(`baglamda olup imaja hic girmeyen ${girmeyen.length} dosya, ${toplam(girmeyen)} bayt: ${girmeyen.join(" ")}`);
console.log(`imaja giren ama kosumda gerekmeyen ${sinama.length} dosya, ${toplam(sinama)} bayt`);
console.log(`derleme baglami ${toplam(baglam())} bayt okundu, imaja ${toplam([...okunan])} bayt gecti`);
```

```
sira yonerge   arguman                                    tur          dosya   bayt
  0  temel     kosum-tabani                               dosya sistemi   2    6060
  1  kopyala   ayar/bolge.json /uygulama/ayar/bolge.json  dosya sistemi   1      32
  2  calistir  bagimlilik-kur                             dosya sistemi   2   45780
  3  kopyala   kaynak /uygulama/kaynak                    dosya sistemi   5   10505
  4  ayar      BOLGE=kuzey                                ustveri         0       0
  5  giris     /uygulama/kaynak/giris.js                  ustveri         0       0

ustveri: {"ayar":"BOLGE=kuzey","giris":"/uygulama/kaynak/giris.js"}
imaj: 4 dosya sistemi katmani, 2 ustveri yonergesi, 62377 bayt
baglamdan okunan 6 dosya, 10537 bayt
baglamda olup imaja hic girmeyen 3 dosya, 5067 bayt: ayar/yerel.json imaj-tanimi okuma-ornekleri/gun-1.csv
imaja giren ama kosumda gerekmeyen 2 dosya, 7340 bayt
derleme baglami 15604 bayt okundu, imaja 10537 bayt gecti
```

Altı satırlık tanım dosyası **dört** dosya sistemi katmanı üretir; iki satır yalnız üstveri yazar
ve yığına hiçbir bayt eklemez. Önceki dersin ölçüsüyle söylenirse: `ayar` ve `giris` satırlarının
değiştirilmesi hiçbir katmanın içerik özetini değiştirmez, çünkü ortada katman yoktur.

#### Bağlama Girip İmaja Girmeyen

Üç sayı yan yana konduğunda tanım dosyasının asıl maliyeti görünür. Bağlam **15 604 bayttır**,
bağlamdan okunan **10 537 bayttır**, imaj **62 377 bayttır**. Üçü de farklıdır ve farkların
her biri ayrı bir şey söyler.

Bağlam ile okunan arasındaki fark **3 dosya ve 5 067 bayttır**: yerel ayar dosyası, tanım
dosyasının kendisi ve örnek okuma verisi bağlama girer, hiçbir yönerge onları istemez, imaja hiç
ulaşmazlar. Yine de derleme adımının okuma erişimindedirler — bağlamda olmak, imaja girmekten
bağımsız bir izindir. Bu, dışlama kuralının yalnız boyut için yazılmadığını gösterir.

Okunan ile imaj arasındaki fark ters yöndedir: imaj bağlamdan aldığından yaklaşık **altı kat** büyüktür.
Farkın kaynağı `calistir` yönergesidir; tek satır **45 780 bayt** üretir, imajın yüzde 73'ü o
katmandır. Dışlama kuralının kestiği 35 280 baytlık bağımlılık ağacı imajdan çıkmış değildir,
**kaynağı değişmiştir**: yazan kişinin makinesinde kurulmuş hâliyle kopyalanmak yerine yönergenin
kendisi tarafından üretilir. Ortamdan çıkarılan fark budur — kurulu bağımlılıkların sürümü artık
bir makinenin durumu değil, tanım dosyasının bir satırıdır.

Bedelin son kalemi `kopyala kaynak` satırındadır. Bir dizin kopyalandığında içine ne girdiğini
yönerge söylemez: sınama dosyaları da imaja girer, **2 dosya ve 7 340 bayt**, imajın koşumda
gerekmeyen yüzde 11,8'i. Yönerge dosya değil dizin adı taşıdığı için bu, tanım dosyasına bakarak
görülemez; ancak katman içeriği sayılarak görülür. Yalıtımın eksik kaldığı yer burasıdır: imaj
**neyin gerektiğini** değil, **neyin kopyalandığını** kaydeder.

#### Seçici Kopyalama ile Bağlamı Kopyalamak

Yönergenin ne kadarını seçtiği bir karardır ve karar üç biçimde sayılabilir. **IM11.** İkinci bir
tanım dosyası tüm bağlamı tek yönergeyle kopyalar; üçüncü ölçüm aynı tanımı dışlama kuralı boşken
koşturur. **IM12.** Koşumda gereken dosyalar açıkça listelenir: sınama dizini dışındaki kaynak
dosyaları ve bölge ayarı. Geri kalan her şey imaja girdiyse gereksizdir.

```js
// olcum-agi/genis.mjs — secici kopyalama ile tum baglami kopyalamayi karsilastirir
import { derle } from "./yorumla.mjs";
import { kurAgac, toplam, TANIM } from "./proje.mjs";

const GENIS = `temel     kosum-tabani
kopyala   .  /uygulama
calistir  bagimlilik-kur
giris     /uygulama/kaynak/giris.js
`;

const GEREKLI = (y) => (y.startsWith("kaynak/") && !y.includes("/sinama/")) || y === "ayar/bolge.json";

kurAgac();
const olc = (etiket, tanim, kural) => {
  const { katmanlar, okunan } = derle(tanim, kural);
  const k = katmanlar.filter((x) => x.dosya > 0);
  const gereksiz = [...okunan].filter((y) => !GEREKLI(y));
  console.log(etiket.padEnd(30) + String(k.length).padStart(2) + String(k.reduce((t, x) => t + x.dosya, 0)).padStart(7) +
    String(k.reduce((t, x) => t + x.bayt, 0)).padStart(9) + String(gereksiz.length).padStart(9) +
    String(toplam(gereksiz)).padStart(8));
};
console.log("tanim".padEnd(30) + "kat  dosya     bayt  gereksiz   bayt");
olc("secici kopyalama", TANIM);
olc("tum baglam, dislama var", GENIS);
olc("tum baglam, dislama yok", GENIS, []);
```

```
tanim                         kat  dosya     bayt  gereksiz   bayt
secici kopyalama               4     10    62377        2    7340
tum baglam, dislama var        3     13    67444        5   12407
tum baglam, dislama yok        3     19   158120       11  103083
```

Üç satır aynı uygulamayı üretir ve boyutları **62 377**, **67 444** ve **158 120** bayttır. İlk
iki satır arasındaki fark küçüktür — 5 067 bayt — çünkü dışlama kuralı zaten ağır dizinleri
kesmiştir. Üçüncü satırda kural boşaltıldığında imaj **2,5 kat** büyür ve içindeki gereksiz dosya
sayısı ikiden **on bire**, gereksiz bayt 7 340'tan **103 083**'e çıkar. İmajın üçte ikisi
çalıştırılmayacak dosyalardan oluşur: kütükler, önceki yapı artıkları, belgeler ve yerel ayar.

İki tanımın katman sayısı da farklıdır: seçici tanım dört, geniş tanım üç dosya sistemi katmanı
üretir. Daha az katman burada iyi bir sonuç değildir — geniş tanımda kaynak ile ayar aynı
yönergeye girdiği için ayrı katmanlara ayrılamamıştır, üstelik bağımlılık kurulumundan **önce**
kopyalanmıştır. Bu iki ayrıntının bedeli bu derste görünmez; sonraki dersin ölçtüğü şey tam olarak
odur.

#### Özet

- İmaj tanım dosyası bir yönerge dizisidir; yönergeler sırayla uygulanır ve her biri bir öncekinin
  üstüne katman koyar. Altı satırlık tanım dört dosya sistemi katmanı üretti, iki satır yalnız
  üstveri yazdı — katman sayısı satır sayısı değildir.
- Derleme bağlamı, yönergelerin okuyabileceği dosya kümesidir. 15 dosyalık 106 280 baytlık ağaçtan
  dışlama kuralı 6 dosya ve 90 676 baytı kesti; bağlama 9 dosya ve 15 604 bayt kaldı.
- Bağlamda olup imaja hiç girmeyen 3 dosya ve 5 067 bayt sayıldı. Bağlamda olmak imaja girmekten
  ayrı bir izindir; dışlama kuralı yalnız boyut için yazılmaz.
- `calistir` yönergesi tek satırda 45 780 bayt üretti — imajın yüzde 73'ü. Dışlanan bağımlılık
  ağacı imajdan çıkmadı, kaynağı bir makinenin durumundan tanım dosyasının bir satırına geçti.
- Dizin kopyalayan yönerge 2 sınama dosyasını ve 7 340 baytı imaja soktu. İmaj neyin gerektiğini
  değil neyin kopyalandığını kaydeder; fark ancak katman içeriği sayılarak görünür.
- Aynı uygulama üç tanımla derlendiğinde imaj 62 377, 67 444 ve 158 120 bayt çıktı. Dışlama kuralı
  boşken tüm bağlamı kopyalayan tanım imajı 2,5 katına çıkardı ve içindeki gereksiz dosya sayısını
  ikiden on bire taşıdı.

#### Sonraki Adım

Bu ölçüm tek bir derlemeyi saydı. Yönergeler her derlemede baştan uygulandığında `calistir`
satırının ürettiği 45 780 bayt da her seferinde yeniden üretilir — oysa bağımlılık listesi
değişmediyse çıktısı da değişmez. Katmanların içerik özetiyle adlandığı bilindiğine göre, değişmemiş
bir yönergenin katmanı yeniden üretilmek yerine saklanandan alınabilir. Sonraki ders bu saklamayı
kurar ve tek bir kararın sayısını verir: bağımlılık kurulumu ile kaynak kopyalamanın hangi sırada
yazıldığı, aynı değişiklik dizisinde önbellek isabet oranını, yeniden üretilen katman sayısını ve
kopyalanan toplam baytı ne kadar oynatır?

### Katman Önbelleği

Source: https://academia.sh/tr/kurslar/konteynerler/katman-onbellegi

Yönerge sırasının derleme işine etkisi ölçülür: katman anahtarı önceki katmanın anahtarıyla yönergenin girdi özetinden türetilir, iki yönerge sırası aynı sekiz derlemelik değişiklik dizisinde koşturulur ve önbellek isabet oranı, yeniden üretilen katman sayısı ile yazılan bayt karşılaştırılır.

Önceki ders her yönergeden bir katman doğduğunu ölçtü ve tek bir `calistir` satırının imajın
büyük bölümünü ürettiğini saydı. O ölçüm tek bir derlemeyi kapsıyordu. Bir servis günde birkaç
kez derlenirse aynı satır her seferinde aynı işi yapar — oysa girdisi değişmediyse çıktısı da
değişmez ve katmanlar zaten içerik özetiyle adlanır.

Bu ders o tekrarı kaldıran yapıyı kurar. **Katman önbelleği**, üretilmiş bir katmanı anahtarıyla
saklar; sonraki derlemede aynı anahtar çıkarsa katman yeniden üretilmez, saklanandan alınır.
Anahtarın neyden türediği bir karar doğurur: yönergelerin **sırası**. Ölçülecek şey de odur —
bağımlılık kurulumunun mu yoksa kaynak kopyalamanın mı önce yazıldığı, aynı değişiklik dizisinde
isabet oranını, yeniden üretilen katman sayısını ve yazılan baytı ne kadar oynatır?

**IM13.** Önbellek `node` ile kurulan bir modeldir; anahtarlar bir eşlemede tutulur, katmanlar
gerçekten `onbellek/<anahtar>/` altına yazılır. Ağaç önceki dersin ağacının bu ölçüm için
sadeleştirilmiş biçimidir; bayt değerleri o dersin sayılarıyla karşılaştırılmaz. **IM14.** Katman
anahtarı, önceki katmanın anahtarı ile yönergenin **girdi özetinden** türer. `kopyala` için girdi
özetine kopyalanacak dosyaların içeriği girer; `calistir` için yalnız satırın kendisi girer.
**IM15.** Süre ölçülmez. Ölçülen nicelikler koşumdan bağımsızdır: yeniden üretilen katman sayısı
ve yazılan bayt.

#### Katman Anahtarı

Önbelleğin çalışması tek kurala bağlıdır: **bir katmanın anahtarı, altındaki her şeyi ve kendi
girdisini kapsamalıdır.** Kapsamazsa değişmiş bir alt katmanın üstündeki katman yanlışlıkla
isabet eder ve imaj tutarsız çıkar. Kural, önceki dersin zincir kimliğinin aynısıdır; farkı,
zincire yönergenin girdisinin de katılmasıdır.

```js
// olcum-agi/onbellek.mjs — katman onbellekli yorumlayici (model)
import { mkdirSync, writeFileSync, readFileSync, readdirSync, appendFileSync, statSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

const KOK = "proje";
const ozet = (s) => createHash("sha256").update(s).digest("hex").slice(0, 12);
export const metin = (ad, n) => Array.from({ length: n }, (_, i) => `// ${ad} ${i}\n`).join("");

const AGAC = {
  "bagimlilik-listesi": "csv 2.4\nkuyruk 1.9\n",
  "kaynak/giris.js": metin("giris", 20),
  "kaynak/topla.js": metin("topla", 140),
  "kaynak/kuyruk.js": metin("kuyruk", 95),
  "kaynak/sinama/topla.sinama.js": metin("sinama", 210),
};
export const URETIM = {
  "kosum-tabani": { "kok/kosum.js": metin("kosum", 420), "kok/kabuk.js": metin("kabuk", 60) },
  "bagimlilik-kur": { "bagimlilik/csv.js": metin("csv", 900), "bagimlilik/kuyruk.js": metin("kuyruk-bag", 1400) },
};

export const SIRA_A = `temel     kosum-tabani
kopyala   bagimlilik-listesi  /uygulama/liste
calistir  bagimlilik-kur
kopyala   kaynak              /uygulama/kaynak`;

export const SIRA_B = `temel     kosum-tabani
kopyala   kaynak              /uygulama/kaynak
kopyala   bagimlilik-listesi  /uygulama/liste
calistir  bagimlilik-kur`;

// Degisiklik dizisi: her derlemeden once dokunulan dosya, bos ise degisiklik yok.
export const DEGISIKLIK = ["", "kaynak/topla.js", "kaynak/giris.js", "", "bagimlilik-listesi",
  "kaynak/topla.js", "kaynak/sinama/topla.sinama.js", "kaynak/kuyruk.js"];

const yazDosya = (yol, icerik) => {
  mkdirSync(yol.split("/").slice(0, -1).join("/"), { recursive: true });
  writeFileSync(yol, icerik);
};
export const kurAgac = () => {
  rmSync(KOK, { recursive: true, force: true });
  rmSync("onbellek", { recursive: true, force: true });
  for (const [y, i] of Object.entries(AGAC)) yazDosya(`${KOK}/${y}`, i);
};
export const degistir = (yol, no) => appendFileSync(`${KOK}/${yol}`, `// duzeltme ${no}\n`);

const gez = (alt = "") => readdirSync(`${KOK}/${alt}`, { withFileTypes: true }).flatMap((g) =>
  g.isDirectory() ? gez(`${alt}/${g.name}`) : [`${alt}/${g.name}`.replace(/^\//, "")]).sort();
const yollari = (kaynak) => statSync(`${KOK}/${kaynak}`).isDirectory() ? gez(kaynak) : [kaynak];

// Katman anahtari: onceki katmanin anahtari + yonergenin girdi ozeti.
// kopyala icin girdi ozetine kopyalanacak dosyalarin icerigi de girer; calistir icin yalniz satir.
const girdiOzeti = (satir) => {
  const [yonerge, kaynak] = satir.trim().split(/\s+/);
  if (yonerge !== "kopyala") return ozet(satir);
  return ozet(satir + yollari(kaynak).map((y) => y + readFileSync(`${KOK}/${y}`)).join());
};

export const derle = (tanim, onbellek) => {
  let anahtar = "";
  const sonuc = { isabet: 0, uretim: 0, bayt: 0, zincir: [] };
  for (const satir of tanim.trim().split("\n")) {
    anahtar = ozet(anahtar + girdiOzeti(satir));
    sonuc.zincir.push(anahtar);
    if (onbellek.has(anahtar)) { sonuc.isabet++; continue; }
    const [yonerge, kaynak, hedef] = satir.trim().split(/\s+/);
    const ciftler = yonerge === "kopyala"
      ? yollari(kaynak).map((y) => [`${hedef.slice(1)}/${y}`, readFileSync(`${KOK}/${y}`)])
      : Object.entries(URETIM[kaynak]);
    let bayt = 0;
    for (const [y, icerik] of ciftler) { yazDosya(`onbellek/${anahtar}/${y}`, icerik); bayt += icerik.length; }
    onbellek.set(anahtar, bayt);
    sonuc.uretim++; sonuc.bayt += bayt;
  }
  return sonuc;
};
```

Anahtarın zincirlenmesinin doğrudan sonucu şudur: bir yönerge ıskaladığında üstündeki **her**
yönerge de ıskalar, girdisi hiç değişmemiş olsa bile. Önbellek yukarıdan aşağı değil, aşağıdan
yukarı çalışır ve ilk ıskalama noktasından sonrası tümüyle yeniden üretilir.

#### İki Sıra, Aynı Değişiklik Dizisi

İki tanım dosyası aynı dört yönergeyi taşır, yalnız sıraları farklıdır. Sıra A bağımlılık
listesini kopyalayıp kurulumu yapar, kaynağı en sona bırakır; sıra B kaynağı en başa alır. Her
iki tanım da aynı sekiz derlemelik dizide koşturulur; dizinin her adımı bir dosyaya tek satır
ekler, iki koşumda da birebir aynı sırayla.

**IM16.** Değişiklik dizisi kurgudaki okuma toplayıcı servisinin bir haftalık düzeltme akışını
temsil eder: soğuk önbellekle ilk derleme, beş kaynak değişikliği, bir bağımlılık listesi
değişikliği ve hiçbir şeyin değişmediği bir derleme. **IM17.** Her sıra kendi boş önbelleğiyle başlar; önbellek koşumlar arasında
paylaşılmaz.

```js
// olcum-agi/kosum.mjs — iki yonerge sirasini ayni degisiklik dizisinde kosturur
import { kurAgac, degistir, derle, DEGISIKLIK, SIRA_A, SIRA_B } from "./onbellek.mjs";

const kosumYap = (ad, tanim) => {
  kurAgac();
  const onbellek = new Map();
  const toplam = { isabet: 0, uretim: 0, bayt: 0 };
  console.log(`\n${ad}`);
  console.log(" no  degisiklik".padEnd(38) + "isabet  uretim     bayt");
  DEGISIKLIK.forEach((d, i) => {
    if (d) degistir(d, i);
    const s = derle(tanim, onbellek);
    for (const k of Object.keys(toplam)) toplam[k] += s[k];
    console.log(`  ${i + 1}  ${d || (i ? "(degisiklik yok)" : "(ilk derleme)")}`.padEnd(38) +
      String(s.isabet).padStart(4) + String(s.uretim).padStart(8) + String(s.bayt).padStart(9));
  });
  const deneme = DEGISIKLIK.length * tanim.trim().split("\n").length;
  console.log(`  toplam: ${toplam.isabet}/${deneme} isabet (%${((toplam.isabet * 100) / deneme).toFixed(1)}), ` +
    `uretilen katman ${toplam.uretim}, yazilan ${toplam.bayt} bayt, onbellekte ${onbellek.size} girdi ` +
    `${[...onbellek.values()].reduce((a, b) => a + b, 0)} bayt`);
  return toplam;
};

const a = kosumYap("A — bagimlilik once, kaynak sonra", SIRA_A);
const b = kosumYap("B — kaynak once, bagimlilik sonra", SIRA_B);
console.log(`\nB'nin A'ya gore fazladan urettigi katman: ${b.uretim - a.uretim}`);
console.log(`B'nin A'ya gore fazladan yazdigi bayt   : ${b.bayt - a.bayt} (%${(((b.bayt - a.bayt) * 100) / a.bayt).toFixed(1)} daha fazla is)`);
```

```
A — bagimlilik once, kaynak sonra
 no  degisiklik                       isabet  uretim     bayt
  1  (ilk derleme)                       0       4    47354
  2  kaynak/topla.js                     3       1     6009
  3  kaynak/giris.js                     3       1     6023
  4  (degisiklik yok)                    4       0        0
  5  bagimlilik-listesi                  1       3    41336
  6  kaynak/topla.js                     3       1     6037
  7  kaynak/sinama/topla.sinama.js       3       1     6051
  8  kaynak/kuyruk.js                    3       1     6065
  toplam: 20/32 isabet (%62.5), uretilen katman 12, yazilan 118875 bayt, onbellekte 12 girdi 118875 bayt

B — kaynak once, bagimlilik sonra
 no  degisiklik                       isabet  uretim     bayt
  1  (ilk derleme)                       0       4    47354
  2  kaynak/topla.js                     1       3    41308
  3  kaynak/giris.js                     1       3    41322
  4  (degisiklik yok)                    4       0        0
  5  bagimlilik-listesi                  2       2    35313
  6  kaynak/topla.js                     1       3    41350
  7  kaynak/sinama/topla.sinama.js       1       3    41364
  8  kaynak/kuyruk.js                    1       3    41378
  toplam: 11/32 isabet (%34.4), uretilen katman 21, yazilan 289389 bayt, onbellekte 21 girdi 289389 bayt

B'nin A'ya gore fazladan urettigi katman: 9
B'nin A'ya gore fazladan yazdigi bayt   : 170514 (%143.4 daha fazla is)
```

#### Sıranın Sayısı

Aynı dört yönerge, aynı sekiz değişiklik, iki farklı sonuç. Sıra A **32 katman denemesinin
20'sinde** isabet eder — yüzde 62,5 — ve **12 katman** üretir. Sıra B aynı dizide **11 isabet**,
yüzde 34,4 ve **21 katman** verir. Yazılan bayt 118 875'e karşı 289 389'dur: sıra B aynı işi
yapmak için **yüzde 143,4 daha fazla** bayt yazar.

Farkın tamamı beş satırda toplanır — kaynak değişikliği olan derlemelerde. Sıra A'da kaynak
kopyalama en üsttedir, altındaki hiçbir katman etkilenmez ve yalnız **6 000 bayt** civarında bir
katman yeniden üretilir. Sıra B'de kaynak kopyalama en alttadır; kaynağa dokunulduğu anda
üstündeki liste kopyalama ve bağımlılık kurulumu da geçersizleşir ve **41 300 bayt** yazılır.
Yedi kat fark, kaynak dosyalarının küçüklüğünden değil, **üstlerinde ne olduğundan** gelir.

Beşinci derleme ters yönü gösterir. Bağımlılık listesi değiştiğinde sıra A üç katman üretir
(41 336 bayt), sıra B iki katman üretir (35 313 bayt) — bu derlemede B daha ucuzdur. Karar tek bir
derlemeye bakarak verilemez; dizinin bileşimine bakarak verilir. Sekiz derlemenin beşinde kaynak,
birinde bağımlılık listesi değişmiştir ve gerçek bir servis ağacında oran bundan daha da keskindir.
Kural şu biçimde yazılır: **yönergeler değişme sıklığına göre sıralanır, en seyrek değişen en
alta.** Bu, önceki dersin katman sırası kuralının derleme tarafındaki karşılığıdır ve iki kural
aynı ölçüden çıkar.

Dördüncü derleme de sayılır: hiçbir şey değişmediğinde iki sıra da 4 isabet ve 0 bayt verir.
Önbelleğin kazancı yalnız sıraya değil, değişikliğin varlığına da bağlıdır — değişmeyen bir ağaçta
her iki sıra da bedava çalışır.

İsabet oranının kendisi de dikkatle okunur. Payda, katman denemesi sayısıdır: dört yönergelik bir
tanımda sekiz derleme 32 deneme eder. Yönerge sayısı artarsa payda büyür ve aynı davranış daha
yüksek bir oran gösterebilir; oran tek başına iki farklı tanım dosyasını karşılaştırmaya yetmez.
Karar taşıyan iki sayı **yeniden üretilen katman** ile **yazılan bayttır**, çünkü ikisi de yapılan
işin doğrudan ölçüsüdür ve tanımın satır sayısından bağımsızdır. Bu ölçümde üçü de aynı yönü
gösterdiği için sıra kararı tartışmasızdır; göstermedikleri durumda bakılacak sütun bayt
sütunudur.

Önbelleğin kendi bedeli son sütundadır. Sıra A'nın önbelleğinde **12 girdi ve 118 875 bayt**, sıra
B'ninkinde **21 girdi ve 289 389 bayt** birikir. Hiçbir girdi silinmez: her ıskalama diske yeni bir
katman yazar, eskisi yerinde kalır. Önbellek, derleme süresini bayt karşılığında satın alan bir
depodur ve kötü sıra bu depoyu da yaklaşık 2,4 katına çıkarır.

Deponun nerede durduğu ayrıca sayılır. Katman önbelleği, derlemenin koştuğu makinede birikir; 12
ve 21 girdilik depolar o makinenin durumudur. Aynı tanım dosyası boş önbellekli bir makinede
koşturulduğunda her iki sıra da tablonun ilk satırındaki bedeli öder: 4 katman, 47 354 bayt. Sıra
kararının kazancı ancak ikinci derlemeden itibaren görünür. Önceki kursta ortam denkliği adıyla
ölçülen fark burada yeni bir biçim alır: derlemenin **sonucu** makineden bağımsızdır, **maliyeti**
değildir. Yalıtım bütçesinin bu derste ödenen kalemi budur — tekrarlanan iş ortamdan çıkarılmadı,
bir depoya taşındı ve o depo hâlâ ortamda duruyor.

#### Önbelleğin Ölçmediği

Anahtar `calistir` yönergesi için yalnız satırın kendisini kapsar. Satır değişmedikçe katman
yeniden üretilmez — kurulacak şeyin dışarıda değişmiş olması anahtara girmez. **IM18.** Dış
dünyanın değişmesi, kurulum tablosundaki içeriğin büyütülmesiyle modellenir; liste dosyasına
dokunulmaz.

```js
// olcum-agi/bayat.mjs — onbellek anahtarinin olcmedigi girdi
import { kurAgac, degistir, derle, metin, URETIM, SIRA_A } from "./onbellek.mjs";

kurAgac();
const onbellek = new Map();
const ilk = derle(SIRA_A, onbellek);
const kurulumAnahtari = ilk.zincir[2];
console.log(`ilk derleme: ${ilk.uretim} katman, ${ilk.bayt} bayt`);

// Dis dunya degisti: kurulacak bagimliligin icerigi buyudu, liste dosyasi ayni kaldi.
URETIM["bagimlilik-kur"]["bagimlilik/csv.js"] = metin("csv", 950);
const guncel = Object.values(URETIM["bagimlilik-kur"]).reduce((t, i) => t + i.length, 0);
const s = derle(SIRA_A, onbellek);
console.log(`\nliste dosyasi ayni, kurulacak icerik degisti:`);
console.log(`  isabet ${s.isabet}, yeniden uretilen katman ${s.uretim}, yazilan ${s.bayt} bayt`);
console.log(`  onbellekteki kurulum katmani ${onbellek.get(kurulumAnahtari)} bayt, guncel icerik ${guncel} bayt`);
console.log(`  fark ${guncel - onbellek.get(kurulumAnahtari)} bayt, ayni anahtar: ${s.zincir[2] === kurulumAnahtari}`);

degistir("bagimlilik-listesi", 9);
const t = derle(SIRA_A, onbellek);
console.log(`\nliste dosyasina tek satir eklenince:`);
console.log(`  isabet ${t.isabet}, yeniden uretilen katman ${t.uretim}, yazilan ${t.bayt} bayt`);
console.log(`  kurulum katmani ${onbellek.get(t.zincir[2])} bayt`);
```

```
ilk derleme: 4 katman, 47354 bayt

liste dosyasi ayni, kurulacak icerik degisti:
  isabet 4, yeniden uretilen katman 0, yazilan 0 bayt
  onbellekteki kurulum katmani 35280 bayt, guncel icerik 35830 bayt
  fark 550 bayt, ayni anahtar: true

liste dosyasina tek satir eklenince:
  isabet 1, yeniden uretilen katman 3, yazilan 41858 bayt
  kurulum katmani 35830 bayt
```

Yalıtımın delindiği yer burasıdır. Kurulacak içerik **550 bayt** büyüdüğü hâlde dört yönergenin
dördü de isabet eder, sıfır katman üretilir ve imaj eski içeriği taşımayı sürdürür. Anahtar
aynıdır, çünkü anahtar `calistir` satırının **metnini** ölçer, satırın **sonucunu** değil. Önbellek
doğru çalışmaktadır; ölçtüğü şey eksiktir.

Bu, önceki kursta ölçülen yeniden üretilebilirlik sorununun önbellek tarafındaki biçimidir. Bir
`calistir` satırı çıktısı zamana bağlı bir iş yapıyorsa — sürüm sınırı yazılmamış bir kurulum
gibi — aynı satır iki farklı zamanda iki farklı katman üretir, ama önbellek ikisini de aynı
anahtarla adlandırır. Çözüm önbellekte değil, yönergededir: `calistir`ın girdisi bir dosyaya
yazılırsa, o dosya `kopyala` ile bağlama girer ve içeriği anahtara katılır. Üçüncü blokta liste
dosyasına tek satır eklendiğinde tam olarak bu olur — anahtar değişir, kurulum katmanı yeniden
üretilir ve depodaki katman 35 830 bayta güncellenir.

Kopyalama tanesi de bir eksiklik kalemidir. Yedinci derlemede değişen dosya bir sınama
dosyasıdır; koşumda hiç çalışmayacak bir dosya, `kaynak` dizini tek yönergeyle kopyalandığı için
6 051 baytlık katmanı yeniden ürettirir. Önceki dersin ölçtüğü gereksiz dosyalar burada ikinci kez
bedel ödetir: önce imaj boyutunda, sonra önbellek isabetinde.

#### Özet

- Katman anahtarı, önceki katmanın anahtarı ile yönergenin girdi özetinden türer. Bir yönerge
  ıskaladığında üstündeki her yönerge de ıskalar; önbellek ilk ıskalama noktasından sonrasını
  tümüyle yeniden üretir.
- Aynı dört yönerge, aynı sekiz derleme: bağımlılık kurulumu önce yazıldığında 32 denemenin
  20'si isabet etti (yüzde 62,5), 12 katman üretildi, 118 875 bayt yazıldı. Kaynak kopyalama
  öne alındığında isabet 11'e düştü (yüzde 34,4), katman 21'e, yazılan bayt 289 389'a çıktı —
  yüzde 143,4 daha fazla iş.
- Fark kaynak değişikliği olan derlemelerde toplanır: sıra A'da 6 000 bayt civarı, sıra B'de
  41 300 bayt civarı. Bağımlılık listesi değiştiğinde yön tersine döner; kararı tek derleme değil,
  değişiklik dizisinin bileşimi verir.
- Önbelleğin kendi bedeli bir depodur ve yalnız büyür: sıra A 12 girdi ve 118 875 bayt, sıra B
  21 girdi ve 289 389 bayt biriktirdi.
- Anahtar `calistir` için yalnız satırın metnini ölçer. Kurulacak içerik 550 bayt büyüdüğü hâlde
  dört yönerge de isabet etti ve sıfır katman üretildi; imaj eski içeriği taşımayı sürdürdü.
  Girdi bir dosyaya yazılıp bağlama alındığında anahtar değişir ve katman güncellenir.

#### Sonraki Adım

Önbellek tekrarlanan işi kaldırdı ama imajın içeriğine hiç dokunmadı. Sekiz derlemenin
sonunda ortaya çıkan imaj, ilk derlemenin ürettiği yığının aynısıdır: bağımlılık kurulumunun
bıraktığı her şey, kopyalanan her kaynak dosyası ve kurulum sırasında kullanılan her araç son
katmanda durur. Oysa bunların bir bölümü yalnız **derleme sırasında** gerekliydi; koşum anında
çalışan tek şey uygulamanın kendisidir. Sonraki ders bu ayrımı ölçer: aynı uygulama tek aşamada
ve çok aşamada derlendiğinde son imajın boyutu, katman sayısı ve içinde kalan gereksiz dosya
sayısı ne kadar ayrılır ve çok aşamalı derlemenin önbellek tarafındaki bedeli nedir?

### Çok Aşamalı Derleme

Source: https://academia.sh/tr/kurslar/konteynerler/cok-asamali-derleme

Derleme araçlarının çıktıdan ayrılması ölçülür: aynı uygulama tek aşamalı ve çok aşamalı olarak derlenir, son imajın boyutu, katman sayısı ve içinde kalan gereksiz dosya sayısı karşılaştırılır, çok aşamalının bedeli de sayılır — üretilen toplam bayt, gönderilmeyen ara katmanlar ve önbellek davranışı.

Önceki ders tekrarlanan işi kaldıran önbelleği kurdu ve yönerge sırasının bedelini saydı. O ölçüm
boyunca imajın **içeriğine** hiç dokunulmadı: sekiz derlemenin sonunda ortaya çıkan yığın, ilk
derlemenin ürettiğinin aynısıydı. Bağımlılık kurulumunun bıraktığı her dosya, kopyalanan her
kaynak dosyası ve kurulum sırasında kullanılan her araç son katmanda duruyordu.

Oysa bunların bir bölümü yalnız **derleme sırasında** gereklidir. Bir derleyici, bir bağlayıcı,
sınama koşucusu ve geliştirme bağımlılıkları çıktıyı üretmek için kullanılır; çıktı üretildikten
sonra hiçbiri çalıştırılmaz. **Çok aşamalı derleme** bu ayrımı yönerge düzeyinde kurar: derleme
kendi aşamasında yapılır, son imaja yalnız üretilen dosyalar taşınır. Ölçülecek soru bellidir —
son imajın boyutu, katman sayısı ve içinde kalan gereksiz dosya sayısı ne kadar ayrılır ve bu
ayrımın bedeli nedir?

**IM19.** Aşağıdaki yorumlayıcı önceki derslerin modelinin genişletilmiş biçimidir: `asama`
yönergesi eklenir ve `kopyala` kaynağı `<asama>:<yol>` biçiminde yazıldığında önceki bir aşamanın
görünümünden okur. **IM20.** Derleme tabanı, koşum tabanının üstüne üç derleme aracı ekler; iki
taban arasındaki fark modelde açıkça yazılıdır. **IM21.** Paketleme modeli sınama dışı kaynak
dosyalarını ve koşum bağımlılıklarını tek dosyada birleştirir; geliştirme bağımlılıkları pakete
girmez. **IM22.** Koşumda gereken dosyalar elle tanımlanır: koşum tabanı dosyaları ve paketlenmiş
çıktı. Kurgudaki ölçüm ağının okuma toplayıcı servisi ölçülür.

#### Derleme Zamanı ile Koşum Zamanı

Tek aşamalı tanımda bütün yönergeler tek bir yığına yazılır ve o yığın hem derlemeyi yapar hem de
çalıştırılır. Çok aşamalı tanımda `asama` yönergesi yığını böler: her aşama kendi tabanından
başlar, kendi zincirini kurar ve son aşama önceki aşamalardan yalnız istediği yolları alır. Son
imaj, **son aşamanın** katmanlarıdır; önceki aşamaların katmanları imaja hiç girmez.

Ayrımın zincir üzerindeki karşılığı da vardır. Her `asama` yönergesi anahtar zincirini sıfırlar:
ikinci aşamanın taban katmanı, birinci aşamada ne olduğundan bağımsız bir anahtar alır ve kaynak
değiştiğinde bile önbellekten gelir. İki aşama arasında geçen tek bağ, taşınan dosyaların
içeriğidir. Önceki dersin kuralı burada da geçerlidir — bir katmanın anahtarı altındaki her şeyi
kapsar — ama "altındaki her şey" artık aşama sınırında kesilir. Bu, çok aşamalı derlemenin
önbellek davranışını hem daha iyi hem de daha kötü yapan tek yapısal ayrımdır: ikinci aşamanın
tabanı bedavaya gelir, ama taşınan paket her değiştiğinde kopyalama katmanı yeniden üretilir.

```js
// olcum-agi/asama.mjs — asamali derleme yorumlayicisi ve katman onbellegi (model)
import { mkdirSync, writeFileSync, readFileSync, readdirSync, appendFileSync, rmSync } from "node:fs";
import { createHash } from "node:crypto";

const KOK = "proje";
const ozet = (s) => createHash("sha256").update(s).digest("hex").slice(0, 12);
const metin = (ad, n) => Array.from({ length: n }, (_, i) => `// ${ad} ${i}\n`).join("");

const AGAC = { "kaynak/giris.js": metin("giris", 22), "kaynak/topla.js": metin("topla", 150),
  "kaynak/kuyruk.js": metin("kuyruk", 96), "kaynak/sinama/topla.sinama.js": metin("sinama", 205) };

const KOSUM_TABANI = { "kok/kosum.js": metin("kosum", 400), "kok/kabuk.js": metin("kabuk", 55) };
// Derleme tabani, kosum tabaninin ustune derleme araclarini ekler.
const DERLEME_TABANI = { ...KOSUM_TABANI, "arac/derleyici.js": metin("derleyici", 3200),
  "arac/baglayici.js": metin("baglayici", 1450), "arac/basliklar.js": metin("basliklar", 900) };

// Paketleme: sinama disi kaynak ile kosum bagimliliklari tek dosyada birlesir.
const paketle = (gorunum) => [...gorunum].filter(([y]) =>
  (y.startsWith("is/kaynak/") && !y.includes("/sinama/")) || y.startsWith("bagimlilik/kosum/"))
  .sort().map(([, i]) => i).join("");

const URETIM = {
  "derleme-tabani": () => DERLEME_TABANI,
  "kosum-tabani": () => KOSUM_TABANI,
  "bagimlilik-kur": () => ({ "bagimlilik/gelistirme/sinama-kosucu.js": metin("sinama-kosucu", 2100),
    "bagimlilik/gelistirme/derleme-eklentisi.js": metin("derleme-eklentisi", 1600),
    "bagimlilik/kosum/csv.js": metin("csv", 780), "bagimlilik/kosum/kuyruk.js": metin("kuyruk-bag", 640) }),
  "yapiyi-uret": (gorunum) => ({ "is/dagitim/paket.js": paketle(gorunum) }),
};

export const TEK = `asama     tek
temel     derleme-tabani
kopyala   kaynak  /is/kaynak
calistir  bagimlilik-kur
calistir  yapiyi-uret
giris     /is/dagitim/paket.js`;

export const COK = `asama     derleyici
temel     derleme-tabani
kopyala   kaynak  /is/kaynak
calistir  bagimlilik-kur
calistir  yapiyi-uret
asama     cikti
temel     kosum-tabani
kopyala   derleyici:/is/dagitim  /uygulama
giris     /uygulama/paket.js`;

// Ayni cok asamali tanim, aradan tum is dizinini tasiyan biciminde.
export const COK_GENIS = COK.replace("derleyici:/is/dagitim  /uygulama", "derleyici:/is  /uygulama")
  .replace("giris     /uygulama/paket.js", "giris     /uygulama/dagitim/paket.js");

const yazDosya = (yol, icerik) => {
  mkdirSync(yol.split("/").slice(0, -1).join("/"), { recursive: true });
  writeFileSync(yol, icerik);
};
export const kurAgac = () => {
  rmSync(KOK, { recursive: true, force: true });
  rmSync("imaj", { recursive: true, force: true });
  for (const [y, i] of Object.entries(AGAC)) yazDosya(`${KOK}/${y}`, i);
};
export const degistir = (yol, no) => appendFileSync(`${KOK}/${yol}`, `// duzeltme ${no}\n`);

const gez = (alt = "") => readdirSync(`${KOK}/${alt}`, { withFileTypes: true }).flatMap((g) =>
  g.isDirectory() ? gez(`${alt}/${g.name}`) : [`${alt}/${g.name}`.replace(/^\//, "")]).sort();

// Kopyalanacak ciftler: agactan ya da baska bir asamanin gorunumunden.
const kopyaCiftleri = (kaynak, hedef, asamalar) => {
  if (!kaynak.includes(":")) return gez(kaynak).map((y) =>
    [`${hedef.slice(1)}/${y.slice(kaynak.length + 1)}`, readFileSync(`${KOK}/${y}`).toString()]);
  const [ad, yol] = kaynak.split(":");
  const onek = `${yol.slice(1)}/`;
  return [...asamalar.get(ad).gorunum].filter(([y]) => y.startsWith(onek))
    .map(([y, i]) => [`${hedef.slice(1)}/${y.slice(onek.length)}`, i]);
};

export const derle = (tanim, onbellek = new Map()) => {
  const asamalar = new Map();
  let simdi = null, anahtar = "";
  const sayac = { uretim: 0, isabet: 0, bayt: 0 };
  for (const ham of tanim.trim().split("\n")) {
    const [yonerge, kaynak, hedef] = ham.trim().split(/\s+/);
    if (yonerge === "asama") {
      simdi = { ad: kaynak, gorunum: new Map(), katman: 0 };
      asamalar.set(kaynak, simdi); anahtar = ""; continue;
    }
    if (yonerge === "giris") continue; // ustveri: katman uretmez
    const girdi = yonerge === "kopyala" ? kopyaCiftleri(kaynak, hedef, asamalar).join() : "";
    anahtar = ozet(anahtar + ham + girdi);
    let ciftler;
    if (onbellek.has(anahtar)) { ciftler = onbellek.get(anahtar); sayac.isabet++; }
    else {
      ciftler = yonerge === "kopyala" ? kopyaCiftleri(kaynak, hedef, asamalar)
        : Object.entries(URETIM[kaynak](simdi.gorunum));
      for (const [y, i] of ciftler) yazDosya(`imaj/${simdi.ad}/kat-${simdi.katman}/${y}`, i);
      onbellek.set(anahtar, ciftler);
      sayac.uretim++; sayac.bayt += ciftler.reduce((t, [, i]) => t + i.length, 0);
    }
    simdi.katman++;
    for (const [y, i] of ciftler) simdi.gorunum.set(y, i);
  }
  return { asamalar, sayac, son: simdi };
};

// Kosumda gereken: kosum tabani dosyalari ve paketlenmis cikti.
export const gerekli = (yol) => yol.startsWith("kok/") || yol.endsWith("paket.js");
```

#### İki Derleme, Aynı Uygulama

İki tanım da aynı kaynaktan aynı paketi üretir. Ölçülen şey, paketin yanında ne kaldığıdır.

```js
// olcum-agi/olcimaj.mjs — tek asamali ve cok asamali derlemenin son imajlarini karsilastirir
import { kurAgac, derle, gerekli, TEK, COK, COK_GENIS } from "./asama.mjs";

const bak = (etiket, tanim) => {
  kurAgac();
  const { asamalar, sayac, son } = derle(tanim);
  const dosyalar = [...son.gorunum];
  const gereksiz = dosyalar.filter(([y]) => !gerekli(y));
  const bayt = dosyalar.reduce((t, [, i]) => t + i.length, 0);
  const ara = [...asamalar.values()].filter((a) => a !== son);
  console.log(etiket.padEnd(20) + String(son.katman).padStart(4) + String(dosyalar.length).padStart(7) +
    String(bayt).padStart(9) + String(gereksiz.length).padStart(9) +
    String(gereksiz.reduce((t, [, i]) => t + i.length, 0)).padStart(9) + String(sayac.bayt).padStart(10) +
    String(ara.reduce((t, a) => t + a.katman, 0)).padStart(6));
  return { bayt, dosyalar, gereksiz };
};

console.log("derleme".padEnd(20) + " kat  dosya     bayt  gereksiz     bayt   uretilen ara-kat");
const t = bak("tek asamali", TEK);
const c = bak("cok asamali", COK);
bak("cok asamali, genis", COK_GENIS);
console.log(`\nson imaj: ${t.bayt} -> ${c.bayt} bayt, ${(t.bayt / c.bayt).toFixed(1)} kat kucuk`);
console.log(`son imajdaki dosya: ${t.dosyalar.length} -> ${c.dosyalar.length}`);
console.log(`kosumda gerekmeyen dosya: ${t.gereksiz.length} -> ${c.gereksiz.length}`);
console.log("tek asamalidaki gereksiz dosyalar: " + t.gereksiz.map(([y]) => y).join(" "));
```

```
derleme              kat  dosya     bayt  gereksiz     bayt   uretilen ara-kat
tek asamali            4     14   237174       11   208222    237174     0
cok asamali            2      3    28952        0        0    266126     4
cok asamali, genis     2      7    35044        4     6092    272218     4

son imaj: 237174 -> 28952 bayt, 8.2 kat kucuk
son imajdaki dosya: 14 -> 3
kosumda gerekmeyen dosya: 11 -> 0
tek asamalidaki gereksiz dosyalar: arac/derleyici.js arac/baglayici.js arac/basliklar.js is/kaynak/giris.js is/kaynak/kuyruk.js is/kaynak/sinama/topla.sinama.js is/kaynak/topla.js bagimlilik/gelistirme/sinama-kosucu.js bagimlilik/gelistirme/derleme-eklentisi.js bagimlilik/kosum/csv.js bagimlilik/kosum/kuyruk.js
```

Son imaj **237 174 bayttan 28 952 bayta**, dosya sayısı **14'ten 3'e**, katman sayısı **4'ten
2'ye** iner. Tek aşamalı imajın **11 dosyası ve 208 222 baytı** koşumda hiç çalıştırılmaz —
imajın yüzde 87,8'i. Bunların arasında üç derleme aracı, dört kaynak dosyası (biri sınama
dosyası), iki geliştirme bağımlılığı ve pakete zaten girmiş iki koşum bağımlılığı vardır. Koşum
bağımlılıklarının iki kez taşınması ayrıca sayılır: hem paketin içinde hem de yanında dururlar.

Katman sayısının düşmesi de ayrı okunmalıdır. Çok aşamalı imajın iki katmanı vardır çünkü son
aşama yalnız iki yönerge çalıştırır; bu, önceki dersin ölçtüğü paylaşım kazancını da değiştirir.
Aynı tabandan türeyen başka servisler o iki katmanın **birincisini** paylaşabilir, ikincisi ise
servise özeldir. Tek aşamalı imajda paylaşılabilecek taban, derleme araçlarını da içerdiği için
hem daha büyüktür hem de derleme aracı sürümü değiştiğinde geçersizleşir — paylaşımın kazancı
değişme sıklığına bağlıdır ve derleme tabanı koşum tabanından daha sık değişir.

Ortamdan çıkarılan fark bu dersin en büyük kalemidir. Derleyici, bağlayıcı ve başlık dosyaları
koşum ortamından tümüyle çıkar; koşan konteynerin dosya sisteminde bir derleme aracı bulunmaz.
Yönerge düzeyinde bunun karşılığı iki satırdır — ikinci bir `asama` ve aşamalar arası tek bir
`kopyala`.

Üçüncü satır kazancın nereden geldiğini ayırır. Aynı çok aşamalı tanım, aradan `/is/dagitim`
yerine tüm `/is` dizinini taşıdığında son imaj **35 044 bayta** ve **7 dosyaya** çıkar; dördü
koşumda gerekmeyen kaynak dosyasıdır ve **6 092 bayt** tutar. Kazancın tamamı ikinci bir aşama
açmaktan gelmez; **taşınan yolun darlığından** gelir. İkinci aşama, birinci aşamanın neyini
almayacağına karar verildiği ölçüde işe yarar. Bu, önceki derste ölçülen dizin kopyalama
sorununun aşama sınırındaki biçimidir: `kopyala` bir yol alır, o yolun altında ne olduğunu
saymaz.

#### Çok Aşamalının Bedeli

Ayrım bedava değildir. Aynı iki tanım bir önbellekle iki kez koşturulur: ilk derleme soğuk
önbellekle, ikincisi tek bir kaynak dosyasına satır eklendikten sonra. **IM23.** Değişiklik
`kaynak/topla.js` dosyasına tek satır ekler. **IM24.** Aşama görünümü bellekte tutulur,
katmanlar diske yazılır; ölçülen baytlar gerçek dosya baytlarıdır.

```js
// olcum-agi/bedel.mjs — cok asamali derlemenin bedeli: ara katman ve onbellek davranisi
import { kurAgac, degistir, derle, TEK, COK } from "./asama.mjs";

const dizi = (etiket, tanim) => {
  kurAgac();
  const onbellek = new Map();
  const ilk = derle(tanim, onbellek).sayac;
  degistir("kaynak/topla.js", 1);
  const { asamalar, sayac, son } = derle(tanim, onbellek);
  const ara = [...asamalar.values()].filter((a) => a !== son)
    .reduce((t, a) => t + [...a.gorunum.values()].reduce((u, i) => u + i.length, 0), 0);
  const onbellekBayt = [...onbellek.values()].reduce((t, c) => t + c.reduce((u, [, i]) => u + i.length, 0), 0);
  console.log(etiket.padEnd(14) + String(ilk.uretim).padStart(4) + String(ilk.bayt).padStart(9) +
    String(sayac.isabet).padStart(8) + String(sayac.uretim).padStart(8) + String(sayac.bayt).padStart(9) +
    String(onbellek.size).padStart(8) + String(onbellekBayt).padStart(9) + String(ara).padStart(10));
};

console.log("derleme".padEnd(14) + " ilk  ilk-bayt  isabet  uretim     bayt  onbel.     bayt gonderilmez");
dizi("tek asamali", TEK);
dizi("cok asamali", COK);
```

```
derleme        ilk  ilk-bayt  isabet  uretim     bayt  onbel.     bayt gonderilmez
tek asamali      4   237174       1       3   134792       7   371966         0
cok asamali      6   266126       2       4   158018      10   424144    237202
```

Bedel dört kalemdir. Birincisi ilk derlemedir: çok aşamalı düzen **6 katman ve 266 126 bayt**
üretir, tek aşamalı **4 katman ve 237 174 bayt** — fark 28 952 bayt, tam olarak ikinci aşamanın
kendi tabanı ve taşınan paket kadar. İkincisi kaynak değişikliğidir: tek aşamalı 3 katman ve
134 792 bayt yeniden üretirken çok aşamalı **4 katman ve 158 018 bayt** üretir, çünkü paket
değiştiğinde aşamalar arası kopyalama da geçersizleşir. Üçüncüsü önbellektir: 7 girdi ve
371 966 bayta karşı **10 girdi ve 424 144 bayt**.

Dördüncü kalem en görünmezidir. Çok aşamalı düzende her derlemede **237 202 bayt** üretilir ve
hiçbir imaja girmez. Ara aşamanın katmanları gönderilmez ama üretilir; derleyen makinenin
diskinde durur, önbellekte yer kaplar ve bir sonraki derlemede yeniden kullanılır. **İmaj küçüldü,
depo küçülmedi.** Yalıtım bütçesi bu dersteki hesabını böyle kapatır: koşum ortamından 208 222
bayt çıkarıldı, karşılığında derleme ortamında 237 202 baytlık kalıcı bir yığın ve derleme başına
yüzde 17 daha fazla yazma işi doğdu.

Bu dört kalem karara nasıl çevrilir? İki sayı yan yana konur. Fazladan bedel **derleme başına**
ödenir ve 28 952 bayttır. Kazanç ise imajın **her kopyası başına** ödenir ve 208 222 bayttır: imaj
depoya yazıldığında, bir makineye çekildiğinde ve diskte durduğunda o kadar bayt eksiktir. Oran
7,2'dir — tek bir derlemenin ürettiği tek bir imaj bile fazladan bedeli fazlasıyla karşılar. Kurgu
ölçüm ağının dört servisi düşünüldüğünde fark çarpılır, çünkü fazladan bedel her derlemede bir
kez, kazanç ise her saklama ve her çekme işleminde yeniden ortaya çıkar. Tek aşamalı düzenin
savunulabildiği yer, imajın hiç saklanmadığı ve tek bir makinede üretilip orada çalıştırıldığı
durumdur; bunun dışında ölçü tek yönü gösterir.

Aşama sınırının ne olmadığı da yazılmalıdır. `kopyala derleyici:/is/dagitim` satırı bir kopyalama
sınırıdır, bir doğrulama sınırı değil. Son aşama, önceki aşamanın ürettiği paketi olduğu gibi
alır; içinde ne olduğunu sormaz. Paketleyici sınama dosyalarını dışarıda bırakmasaydı onlar da
son imaja girerdi ve ölçüm bunu yakalamazdı — çünkü `gerekli` tanımı bu ölçümde **elle**
yazılmıştır. İmajın kendisi neyin gerektiğini bilmez; taşınan yolun doğru seçildiğini yazan kişi
garanti eder. Önceki derste ölçülen eksikliğin buradaki karşılığı budur: yönerge neyin
gerektiğini değil, neyin kopyalandığını kaydeder.

#### Özet

- Çok aşamalı derleme, derleme zamanında gereken dosyaları koşum zamanında gerekenlerden ayırır:
  derleme kendi aşamasında yapılır, son imaja yalnız üretilen dosyalar taşınır.
- Aynı uygulama iki biçimde derlendiğinde son imaj 237 174 bayttan 28 952 bayta indi (8,2 kat),
  dosya sayısı 14'ten 3'e, katman sayısı 4'ten 2'ye düştü.
- Tek aşamalı imajın 11 dosyası ve 208 222 baytı koşumda hiç çalıştırılmıyordu — imajın yüzde
  87,8'i. Üç derleme aracı, dört kaynak dosyası, iki geliştirme bağımlılığı ve pakete zaten girmiş
  iki koşum bağımlılığı.
- Bedel dört kalemdir: ilk derlemede 28 952 bayt fazla üretim, kaynak değişikliğinde 3 yerine 4
  katman ve 134 792 yerine 158 018 bayt, önbellekte 7 yerine 10 girdi ve her derlemede üretilip
  hiçbir imaja girmeyen 237 202 bayt. İmaj küçüldü, derleyen makinedeki depo küçülmedi.
- Kazanç ikinci aşamayı açmaktan değil, taşınan yolun darlığından gelir: aradan `/is/dagitim`
  yerine tüm `/is` taşındığında son imaj 35 044 bayta ve 7 dosyaya çıktı, 4 gereksiz dosya ve
  6 092 bayt geri geldi.
- Aşama sınırı bir kopyalama sınırıdır, doğrulama sınırı değil. Son aşama önceki aşamanın
  ürettiğini olduğu gibi alır; koşumda neyin gerektiği tanımı yönergenin dışında, yazan kişinin
  elindedir.

#### Sonraki Adım

Son imajın üç dosyası kaldı ve ikisi tabandan geliyor: koşum tabanı 5 740 bayt, paket 23 212 bayt.
Derlemenin ürettiği her şey ölçülüp küçültüldükten sonra geriye, imajın **üretilmemiş** bölümü
kalır — altına konan taban. Bu ders boyunca taban bir sabitti; koşum tabanının hangi dosyaları
taşıdığı, hangi kabuğu ve hangi kütüphaneleri içerdiği hiç sorulmadı. Oysa taban son imajın beşte
birini oluşturuyor ve içeriği yazan kişinin ürettiği bir şey değil, dışarıdan alınan bir şey.
Sonraki ders bu seçimi ölçer: küçük bir taban ile geniş bir taban arasındaki fark kaç bayt ve kaç
dosyadır, küçük taban neyi eksik bırakır ve o eksiklik hangi durumda ödenir?

### Temel İmaj Seçimi

Source: https://academia.sh/tr/kurslar/konteynerler/temel-imaj-secimi

Üç temel imaj adayı tür nitelemesiyle ölçülür: bayt, taşınan ikili ve kütüphane sayısı, kabuk ile paket yöneticisinin varlığı sayılır; temel güncellendiğinde kaç katmanın gerçekten değiştiği hesaplanır ve küçük temelin gizli bedeli hata ayıklamada atılan ek adım ile kırılan bağımlılık baytıyla ölçülür.

Önceki ders derleme araçlarını çıktıdan ayırdı: derleyici, geliştirme başlıkları ve ara dosyalar
son çıktının dışında kaldı, çıktı küçüldü. Ama küçülen şey üst katmanlardı. Son aşama hâlâ bir
temelin üzerine kurulur ve o temel, imajın içinde yazılmayan tek bölümdür — ilk yönergeden önce
gelir, dosyalarını başkası derlemiştir, güncelleme çizelgesini başkası belirler.

Bu ders o tabanı ölçer. Soru "hangisi iyi" değil; üç aday tür için aynı niceliklerin — bayt,
taşınan ikili, güncelleme yükü — ne çıktığı ve küçük olanın hangi bedeli görünmeyen yere
ittiğidir.

**IM25.** Adaylar ürün olarak değil tür olarak anılır: **tam donanımlı temel**, **küçültülmüş
temel**, **tek ikili temel**. **IM26.** Envanter sayıları kendi üretecimizden gelir; tohum
`20260518` görünürdür ve sayılar gerçek bir ölçüm değil, büyüklük mertebesidir. **IM27.** Üstteki
uygulama katmanı üç adayda da aynıdır: bölgesel ölçüm ağının — su sayaçlarından okuma toplayan
kurgu yazılımın — okuma toplayıcı servisi, 24 MB.

#### Temelin İçinde Ne Var

Temel imaj, ortamda kurulu olan şeyleri çıktının içine alma işinin büyük kısmını yapar:
kütüphaneler, sertifika deposu, saat dilimi verisi, kabuk. Aday türleri arasındaki fark, bu
kümenin nerede kesildiğidir. Tam donanımlı temel genel amaçlı bir işletim ortamının paket
kümesini taşır; kabuk, paket yöneticisi, süreç ve ağ araçları içinde gelir. Küçültülmüş temel
aynı kümeyi tek bir çok işlevli ikiliye ve zorunlu kütüphanelere indirir, ama kabuğu ve paket
yöneticisini bırakır. Tek ikili temel yalnız uygulamanın bağlandığı kütüphaneleri, sertifikaları
ve saat dilimi verisini taşır.

```js
// olcum-agi/temel.mjs — uc temel imaj adayinin envanteri (model; urun degil tur)
export const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;

// [adet, alt bayt, ust bayt]. Sayilar ureteçten gelir; buyukluk mertebesidir.
export const ADAYLAR = [
  { ad: "tam donanimli", ikili: [480, 8e3, 220e3], kutuphane: [310, 12e3, 240e3],
    veri: [2400, 1e3, 14e3], kabuk: true, paket: true, bilesen: 148 },
  { ad: "kucultulmus", ikili: [140, 3e3, 60e3], kutuphane: [95, 8e3, 120e3],
    veri: [120, 1e3, 9e3], kabuk: true, paket: true, bilesen: 31 },
  { ad: "tek ikili", ikili: [0, 0, 0], kutuphane: [6, 30e3, 900e3],
    veri: [40, 1e3, 30e3], kabuk: false, paket: false, bilesen: 6 },
];

export const UYGULAMA = 24_000_000; // okuma toplayici servisi; uc adayda da ayni

export const envanter = (aday, tohum = 20260518) => {
  const r = uretec(tohum);
  const grup = ([n, alt, ust]) => {
    let bayt = 0;
    for (let i = 0; i < n; i++) bayt += alt + Math.round(r() * (ust - alt));
    return { n, bayt };
  };
  const i = grup(aday.ikili), k = grup(aday.kutuphane), v = grup(aday.veri);
  return { ...aday, i, k, v, dosya: i.n + k.n + v.n, bayt: i.bayt + k.bayt + v.bayt };
};

export const mb = (b) => (b / 1e6).toFixed(1) + " MB";

// Asagisi yalniz bu dosya dogrudan kosturuldugunda calisir; digerleri modul olarak alir.
if (import.meta.url === `file://${process.argv[1]}`) {
  const satirlar = ADAYLAR.map((a) => envanter(a));
  console.log("aday".padEnd(15) + "dosya".padEnd(8) + "ikili".padEnd(7) +
    "kutuphane".padEnd(11) + "kabuk".padEnd(7) + "paket yon.".padEnd(12) +
    "temel".padEnd(10) + "imaj");
  for (const s of satirlar) {
    console.log(s.ad.padEnd(15) + String(s.dosya).padEnd(8) + String(s.i.n).padEnd(7) +
      String(s.k.n).padEnd(11) + (s.kabuk ? "var" : "yok").padEnd(7) +
      (s.paket ? "var" : "yok").padEnd(12) + mb(s.bayt).padEnd(10) +
      mb(s.bayt + UYGULAMA));
  }
  const [tam, kucuk, tek] = satirlar;
  console.log(`\ntemelin imaj icindeki payi: ${(100 * tam.bayt / (tam.bayt + UYGULAMA)).toFixed(0)}% / ` +
    `${(100 * kucuk.bayt / (kucuk.bayt + UYGULAMA)).toFixed(0)}% / ` +
    `${(100 * tek.bayt / (tek.bayt + UYGULAMA)).toFixed(0)}%`);
  console.log(`imaj icinde calistirilabilir dosya: ${tam.i.n} / ${kucuk.i.n} / ${tek.i.n}`);
  console.log(`guncelleme izlenecek bilesen: ${tam.bilesen} / ${kucuk.bilesen} / ${tek.bilesen}`);
}
```

```
aday           dosya   ikili  kutuphane  kabuk  paket yon.  temel     imaj
tam donanimli  3190    480    310        var    var         111.1 MB  135.1 MB
kucultulmus    355     140    95         var    var         10.5 MB   34.5 MB
tek ikili      46      0      6          yok    yok         4.1 MB    28.1 MB

temelin imaj icindeki payi: 82% / 30% / 14%
imaj icinde calistirilabilir dosya: 480 / 140 / 0
guncelleme izlenecek bilesen: 148 / 31 / 6
```

Aynı uygulama, üç temelin üzerinde 135,1 MB ile 28,1 MB arasında değişen bir çıktı verir. Tam
donanımlı temelde imajın **yüzde 82'si** yazılmamış bölümdür: temel tek başına 3190 dosya taşır
ve bunların hiçbiri bu iş için yazılmamıştır.

İkinci satır bayt değildir. İmajın içinde çalıştırılabilir 480 dosya bulunması, bir kod
çalıştırma açığı bulunduğunda hazır bekleyen araç sayısıdır — dosya indirici, arşivleyici,
derleyici, ağ istemcisi. Saldırı yüzeyi bu sayıya eşit değildir, ama bu sayı onun üst sınırıdır
ve tek ikili temelde sıfırdır.

Üç adayın hiçbirinin değiştirmediği bir şey vardır: **çekirdek**. Temel imaj kullanıcı alanındaki
dosyaları taşır, çekirdek arayüzünü taşımaz; üç aday da altında duran çekirdeğin sunduğu sistem
çağrısı kümesine bağlıdır ve o küme imajın içinde değildir. Tek ikili temelin 46 dosyası, tam
donanımlı temelin 3190 dosyasıyla aynı çağrı yüzeyine erişir. Boyut tablosu bu yüzden yalıtımın
tamamını ölçmez — yalnız kullanıcı alanının ne kadarının çıktıya alındığını ölçer.

**Ortamdan çıkarılan fark burada sayılır:** kurulu paketler ve kütüphane sürümleri artık ortamda
değil, çıktının içindedir — 148, 31 ya da 6 bileşen olarak. Fark yok olmadı; yalnız yer
değiştirdi ve başka bir yayıncının çizelgesine bağlandı. Bir sonraki bölüm o çizelgenin bedelini
sayar.

#### Güncelleme Geldiğinde Kaç Katman Değişir

Bileşen sayısı yalnız bayt değil, **olay sıklığı** demektir. Her bileşen kendi güncellemelerini
üretir ve temel yayıncısı bunları yeni bir temel sürümü olarak yayımlar. Temel katman
değiştiğinde imajın yeniden derlenmesi gerekir; asıl soru, üstteki katmanların da değişip
değişmediğidir.

**IM28.** Bileşen başına yılda ortalama 0,9 güvenlik güncellemesi düşer ve çalışan makine sayısı
12'dir; ikisi de kurgudur. **IM29.** İmaj dört katmandır: temel, bağımlılıklar, uygulama,
yapılandırma. Yeniden derleme iki disiplinle ölçülür — belirlenimci derleme katmanlara hiçbir
değişken damga koymaz, damgalı derleme her yeniden derlenen katmana koşum zamanını yazar.

```js
// olcum-agi/guncelleme.mjs — temel guncellendiginde kac katman gercekten degisiyor (model)
import { createHash } from "node:crypto";
import { ADAYLAR, envanter, mb } from "./temel.mjs";

const ozet = (s) => createHash("sha256").update(s).digest("hex").slice(0, 10);
const gb = (b) => (b / 1e9).toFixed(1) + " GB";
const DUGUM = 12;              // olcum agindaki calisan makine sayisi (kurgu)
const YILLIK_ORAN = 0.9;       // bilesen basina yillik guvenlik guncellemesi (kurgu)

// Imaj dort katmandir: temel + uygulamanin uc katmani. Ust uc katman uc adayda ayni.
const UST = [["bagimliliklar", 16.0e6], ["uygulama", 7.2e6], ["yapilandirma", 0.8e6]];

// damga=null: belirlenimci yeniden derleme. damga=<deger>: her katmana zaman damgasi girer.
const katmanlar = (temelSurum, temelBayt, damga) => [
  { ad: "temel", bayt: temelBayt, ozet: ozet(`temel@${temelSurum}`) },
  ...UST.map(([ad, bayt]) => ({ ad, bayt, ozet: ozet(`${ad}@v1${damga ?? ""}`) })),
];

console.log("aday".padEnd(15) + "yillik guncelleme".padEnd(19) + "degisen katman".padEnd(18) +
  "degisen bayt".padEnd(14) + "yillik cekme");
for (const aday of ADAYLAR) {
  const e = envanter(aday);
  const eski = katmanlar("2026.05", e.bayt, null);
  for (const [etiket, damga] of [["belirlenimci", null], ["damgali", "-b2"]]) {
    const yeni = katmanlar("2026.06", e.bayt, damga);
    const degisen = yeni.filter((k, i) => k.ozet !== eski[i].ozet);
    const bayt = degisen.reduce((t, k) => t + k.bayt, 0);
    const yillik = Math.round(aday.bilesen * YILLIK_ORAN);
    console.log((etiket === "belirlenimci" ? aday.ad : "").padEnd(15) +
      (etiket === "belirlenimci" ? `${yillik} kez` : "").padEnd(19) +
      `${degisen.length}/4 ${etiket}`.padEnd(18) + mb(bayt).padEnd(14) +
      gb(yillik * bayt * DUGUM));
  }
}
console.log(`\nyillik cekme = yillik guncelleme x degisen bayt x ${DUGUM} dugum`);
```

```
aday           yillik guncelleme  degisen katman    degisen bayt  yillik cekme
tam donanimli  133 kez            1/4 belirlenimci  111.1 MB      177.4 GB
                                  4/4 damgali       135.1 MB      215.7 GB
kucultulmus    28 kez             1/4 belirlenimci  10.5 MB       3.5 GB
                                  4/4 damgali       34.5 MB       11.6 GB
tek ikili      5 kez              1/4 belirlenimci  4.1 MB        0.2 GB
                                  4/4 damgali       28.1 MB       1.7 GB

yillik cekme = yillik guncelleme x degisen bayt x 12 dugum
```

Tablonun iki sütunu birlikte okunmalıdır. Belirlenimci derlemede temel güncellemesi **dört
katmanın yalnız birini** değiştirir: üstteki üç katmanın içeriği aynı kaldığı için özetleri de
aynı kalır ve çekmede atlanır. Damgalı derlemede aynı güncelleme **dörtte dördünü** değiştirir,
çünkü içerik aynı olsa bile katmana yazılan koşum zamanı özeti kaydırır. Fark tam donanımlı
temelde yıllık 38,3 GB, tek ikili temelde 1,5 GB'dir. Belirlenimcilik temelin bir özelliği
değil, derleme adımının disiplinidir — temel ne kadar küçük olursa olsun, damga koyan bir derleme
her güncellemede imajın tamamını yeniden dağıtır.

Yıllık çekme sütunu asıl açıklığı gösterir: tam donanımlı temelde **177,4 GB**, tek ikili temelde
**0,2 GB**. Aradaki büyüklüğün kaynağı bayt kadar sıklıktır: tam donanımlı temel yılda 133 kez,
tek ikili temel 5 kez güncellenir ve ikincisinin izlemesi gereken bileşen sayısı 6'dır. Küçük temelin en somut
kazancı burada görünür — imajın boyutu değil, **güncelleme yüzeyinin** boyutu.

Bu, yalıtımın delindiği yerlerden birini de yazar: temel imaj çıktının içine alınmıştır ama
üretimi dışarıdadır. Yılda 133 kez, kararı verilmeyen bir değişiklik çıktının en alt katmanına
girer. Sürüm sabitlenerek durdurulabilir — o zaman da güvenlik güncellemeleri girmez.

#### Küçük Temelin Gizli Bedeli

Küçük temelin bedeli boyut tablosunda görünmez; iki yerde görünür. Birincisi bir olay
sırasındadır: kabuk, süreç listeleyici ya da ağ aracı imajın içinde yoksa, o araçla bakılacak
soruya yerinde bakılamaz. İkincisi derleme sırasındadır: temelde bulunmayan bir kütüphane, imaja
elle eklenmek zorundadır.

**IM30.** Sekiz olay türü, her birinin yerinde bakılabilmesi için gereken araç kümesi ve servisin
dokuz yerel bağımlılığı kurgudur. Araç eksikse ek adım sayısı sabit bir kurala bağlanır: paket
yöneticisi varsa 2 (kurulum ve sonrasında imajın temizlenmesi), yoksa 4 (yardımcı bir konteyner
hazırlama, ad alanını paylaştırma, çıktıyı dışarı kopyalama, yeniden çalıştırma). Her bağımlılık
ancak belli bir yalınlığa kadar temelde hazır gelir.

```js
// olcum-agi/bedel.mjs — kucuk temelin gizli bedeli: hata ayiklama adimi ve uyumluluk (model)
import { ADAYLAR, envanter, UYGULAMA, mb } from "./temel.mjs";

// Adaylarin tasidigi arac kumesi (kurgu, tur adlariyla).
const ARAC = {
  "tam donanimli": ["kabuk", "paket", "surec", "ag", "dosya", "arama"],
  "kucultulmus": ["kabuk", "paket", "surec", "dosya", "arama"],
  "tek ikili": [],
};
// Olcum agindaki sekiz olay turu ve yerinde bakmak icin gereken arac (kurgu).
const OLAY = [
  ["servis yanit vermiyor", ["surec"]], ["kuyruk birikti", ["surec", "arama"]],
  ["sertifika dogrulanmiyor", ["dosya"]], ["ad cozumlenmiyor", ["ag"]],
  ["disk doldu", ["dosya"]], ["yapilandirma yanlis okundu", ["kabuk", "arama"]],
  ["yavas yanit", ["ag", "surec"]], ["izin reddedildi", ["dosya", "kabuk"]],
];
// Servisin dokuz yerel bagimliligi: [ad, bayt, saglayan en yalin aday sirasi].
// Adaylar zenginden yalina siralidir; sira > saglayan ise bagimlilik temelde yoktur.
const BAGIMLILIK = [
  ["sifreleme kutuphanesi", 3.1e6, 2], ["sertifika deposu", 0.3e6, 2],
  ["matematik kutuphanesi", 1.2e6, 2], ["sikistirma kutuphanesi", 0.6e6, 2],
  ["yerellestirme verisi", 2.4e6, 1], ["saat dilimi veritabani", 0.9e6, 1],
  ["ad cozumleyici eklentisi", 0.9e6, 1], ["surum uyumluluk katmani", 0.6e6, 0],
  ["kabuk betigi calistirici", 0.3e6, 0],
];

console.log("aday".padEnd(15) + "yerinde bakilan".padEnd(17) + "ek adim".padEnd(9) +
  "eksik bagimlilik".padEnd(18) + "eklenen".padEnd(9) + "gercek imaj");
ADAYLAR.forEach((aday, sira) => {
  const arac = ARAC[aday.ad];
  let yerinde = 0, ekAdim = 0;
  for (const [, gerek] of OLAY) {
    if (gerek.every((g) => arac.includes(g))) yerinde += 1;
    else ekAdim += arac.includes("paket") ? 2 : 4;
  }
  const yok = BAGIMLILIK.filter(([, , saglayan]) => sira > saglayan);
  const eklenen = yok.reduce((t, [, b]) => t + b, 0);
  const nominal = envanter(aday).bayt + UYGULAMA;
  console.log(aday.ad.padEnd(15) + `${yerinde}/8 olay`.padEnd(17) +
    String(ekAdim).padEnd(9) + `${yok.length}/9`.padEnd(18) +
    mb(eklenen).padEnd(9) + mb(nominal + eklenen));
});
```

```
aday           yerinde bakilan  ek adim  eksik bagimlilik  eklenen  gercek imaj
tam donanimli  8/8 olay         0        0/9               0.0 MB   135.1 MB
kucultulmus    6/8 olay         4        2/9               0.9 MB   35.4 MB
tek ikili      0/8 olay         32       5/9               5.1 MB   33.2 MB
```

Tek ikili temelde sekiz olayın **hiçbirine** yerinde bakılamaz ve sekiz olay toplam **32 ek adım**
üretir. Bu adımlar yalnız süre değildir. Yardımcı bir konteyneri aynı ad alanına bağlamak,
imajdan çıkarılan bütün araçları o an geri getirmek demektir; yani yalıtım hata ayıklama süresince
bilerek delinir. Küçük temelin kapattığı 480 çalıştırılabilir dosya, olay anında başka bir yoldan
geri açılır — fark, artık kalıcı değil süreli olmasıdır ve o sürenin ne zaman başladığı kayda
geçebilir.

Son iki sütun boyut tablosunu düzeltir. Tek ikili temel nominal olarak küçültülmüş temelden 6,4 MB
küçüktür; ama dokuz bağımlılığın beşi temelde bulunmadığı için imaja elle eklenir ve fark **2,2
MB'ye** iner. Eksik bağımlılıklardan biri kabuk betiği çalıştırıcısıdır: kabuk olmayan bir temelde
başlangıç betiği çalışmaz, servisin doğrudan çalıştırılabilir olması gerekir. Bu, bayt olarak
küçük ama yazım olarak gerçek bir bedeldir.

Küçültülmüş temel iki tarafta da orta değeri tutar: 6/8 olay yerinde, 4 ek adım, 2 eksik
bağımlılık. Ölçülen üç nicelik birlikte bakıldığında seçim "en küçüğü" değil, **ek adımların kimin
üstüne düştüğü** sorusudur.

#### Özet

- Aynı uygulama üç temel üzerinde 135,1 MB, 34,5 MB ve 28,1 MB çıktı verir; tam donanımlı temelde
  imajın yüzde 82'si yazılmamış bölümdür ve temel tek başına 3190 dosya taşır.
- İmajın içindeki çalıştırılabilir dosya sayısı 480 / 140 / 0'dır. Bu sayı saldırı yüzeyine eşit
  değildir ama üst sınırıdır: bir kod çalıştırma açığında hazır bekleyen araç sayısıdır.
- Temel güncellemesi belirlenimci derlemede dört katmanın birini, damgalı derlemede dördünü
  değiştirir. Yıllık çekme tam donanımlı temelde 177,4 GB, tek ikili temelde 0,2 GB'dir; farkın
  kaynağı bayt kadar sıklıktır (133 güncellemeye karşı 5).
- Küçük temelin gizli bedeli sayıyla ölçülür: tek ikili temelde 8 olayın 0'ına yerinde bakılabilir,
  32 ek adım gerekir ve bu adımlar yalıtımı hata ayıklama süresince bilerek deler.
- Nominal 6,4 MB'lik boyut farkı, eksik 5 bağımlılık imaja eklendiğinde 2,2 MB'ye iner; temelin
  taşımadığı her şey imaja geri girer, yalnız başka bir yerden ve elle.

#### Sonraki Adım

Bu dersin ölçümü üç adayı sabit içerikmiş gibi ele aldı. Oysa hiçbir temel içerikle seçilmez: bir
**adla** çekilir ve o adın hangi içeriği getirdiği zamana bağlıdır. Aynı şey üretilen imajın kendisi
için de geçerlidir — 133 güncellemenin her biri yeni bir içerik üretir, ama dağıtım kayıtlarında
duran şey addır.

M22/K01'in yapı çıktısı depolarında **taşınabilir etiket** ile **değişmez sürüm** ayrımı ölçülmüştü;
o sayım tekrarlanmaz. Buradaki soru bir katman daha aşağıdadır: imaj tek bir nesne değil, bir
katman listesidir. Sonraki ders bunu sayar — aynı içerik özetine kaç ad işaret ediyor, bir etiket
taşındığında dört katmanın gerçekten kaçı değişiyor ve üretime çıkan bir dağıtımın kaynağı özetten
geriye izlenebiliyor mu?

### Etiketleme Düzeni

Source: https://academia.sh/tr/kurslar/konteynerler/etiketleme-duzeni

Katmanlı bir imaj deposunda etiket ölçülür: yirmi derlemenin kaç ayrı içerik özeti ürettiği ve aynı özete kaç etiketin işaret ettiği sayılır, etiket taşındığında dört katmanın gerçekten kaçının değiştiği hesaplanır ve bir dağıtımın kaynağına özetten geriye izlenebilme oranı üç ayrı kayıt disiplini altında ölçülür.

Önceki ders üç temel adayını sabit içerikmiş gibi ele aldı. Oysa hiçbir temel içerikle seçilmez:
bir **adla** çekilir. Aynı şey üretilen imaj için de geçerlidir — bir derleme yeni bir içerik
üretir, ama dağıtım kaydında duran şey addır.

M22/K01'in yapı çıktısı depolarında **taşınabilir etiket** ile **değişmez sürüm** ayrımı
ölçülmüştü; o sayım burada tekrarlanmaz. Buradaki soru bir katman aşağıdadır: imaj tek bir nesne
değil, bir katman listesidir. Etiket bu listenin tamamına mı işaret eder, aynı listeye kaç ad
birden işaret edebilir ve bir ad taşındığında listenin kaçıncı satırı gerçekten değişir?

**IM31.** Depo modeli iki defter tutar: imaj özeti → manifest ve etiket → imaj özeti. Erişim
denetimi ve ağ katmanı modelin dışındadır. **IM32.** Derleme geçmişi kendi üretecimizden gelir;
tohum `20260604` görünürdür, 20 derleme yapılır ve derlemelerin dörtte biri aynı kaynak sürümünden
yeniden derlemedir. **IM33.** İmaj dört katmandır — önceki dersin küçültülmüş temeli üzerine
bağımlılıklar, uygulama ve yapılandırma — ve toplamı 34,5 MB'dir.

#### Etiket Neye İşaret Eder

Etiket doğrudan bayta işaret etmez. Zincir üç düzeylidir: etiket bir **imaj özetini** gösterir,
imaj özeti bir **manifesti** adresler, manifest de katman özetlerini sırayla listeler. Aynı adın
aynı katman kümesini getirmesini sağlayan şey bu üç düzeydir.

**IM34.** Etiket düzeni şudur: her derlemeye bir değişmez sürüm etiketi (`s-1`, `s-2`, …) verilir;
`son` her derlemede taşınır, `kararli` yalnız gecelik toplu işi temiz koşan derlemelere taşınır,
`dal-hazirlik` her dördüncü derlemede taşınır. **IM35.** Derlemelerin dörtte üçü yapılandırma
nesnesine kaynak sürümü alanını yazar; boru hattı dışında elle yapılanlar yazmaz.

```js
// olcum-agi/imaj.mjs — katmanli imaj deposunda derleme gecmisi ve etiketler (model)
import { createHash } from "node:crypto";

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

// [ad, bayt, kaynaktan bagimsiz degisme olasiligi]. Uygulama katmani kaynaga baglidir.
export const KATMAN = [["temel", 10.5e6, 0.08], ["bagimliliklar", 16.0e6, 0.25],
  ["uygulama", 7.2e6, null], ["yapilandirma", 0.8e6, 0.30]];

export const gecmis = (n = 20, tohum = 20260604) => {
  const r = uretec(tohum);
  const surum = [1, 1, 1, 1];
  const liste = [];
  let kaynak = 1, gun = 0;
  for (let no = 1; no <= n; no++) {
    gun += 1 + Math.round(r() * 8);
    const yeniKaynak = r() > 0.25;      // dortte biri ayni kaynaktan yeniden derlemedir
    if (yeniKaynak) { kaynak += 1; surum[2] += 1; }
    KATMAN.forEach(([, , p], k) => { if (p !== null && r() < p) surum[k] += 1; });
    const katmanlar = KATMAN.map(([ad, bayt], k) => ({ ad, bayt, ozet: ozet(`${ad}@${surum[k]}`) }));
    liste.push({
      no, gun, kaynak: `k-${String(kaynak).padStart(3, "0")}`, katmanlar,
      imaj: ozet("manifest:" + katmanlar.map((k) => k.ozet).join("|")),
      kaynakYazildi: r() > 0.25,        // boru hatti disinda elle derlemeler alani yazmaz
      temizKostu: r() > 0.40,           // gecelik toplu is temiz kostu mu
    });
  }
  return liste;
};

// Etiket defteri: her derlemeye degismez surum etiketi, tasinabilir etiketler kosula bagli.
export const etiketle = (G) => {
  const defter = new Map();             // etiket -> imaj ozeti
  for (const d of G) {
    defter.set(`s-${d.no}`, d.imaj);
    defter.set("son", d.imaj);
    if (d.temizKostu) defter.set("kararli", d.imaj);
    if (d.no % 4 === 0) defter.set("dal-hazirlik", d.imaj);
  }
  return defter;
};

if (import.meta.url === `file://${process.argv[1]}`) {
  const G = gecmis();
  const gorulen = new Set();
  console.log("no".padEnd(4) + "gun".padEnd(5) + "kaynak".padEnd(8) + "imaj ozeti".padEnd(15) + "durum");
  for (const d of G) {
    const tekrar = gorulen.has(d.imaj);
    gorulen.add(d.imaj);
    console.log(String(d.no).padEnd(4) + String(d.gun).padEnd(5) + d.kaynak.padEnd(8) +
      d.imaj.padEnd(15) + (tekrar ? "tekrar" : "yeni"));
  }
  const defter = etiketle(G);
  const fanIn = new Map();
  for (const [etiket, imaj] of defter) fanIn.set(imaj, [...(fanIn.get(imaj) ?? []), etiket]);
  const cok = [...fanIn.entries()].sort((a, b) => b[1].length - a[1].length);
  console.log(`\n${G.length} derleme ${gorulen.size} ayri imaj ozeti uretti; ` +
    `${G.length - gorulen.size} derleme onceki bir ozeti yeniden uretti`);
  console.log(`${defter.size} etiket ${fanIn.size} ayri ozete isaret ediyor`);
  for (const [imaj, etiketler] of cok.slice(0, 3)) {
    console.log(`  ${imaj}: ${etiketler.length} etiket -> ${etiketler.join(", ")}`);
  }
  console.log(`kaynak alani yazilmis derleme: ${G.filter((d) => d.kaynakYazildi).length}/${G.length}`);
}
```

```
no  gun  kaynak  imaj ozeti     durum
1   7    k-002   1c8cb1388bf2   yeni
2   11   k-002   908c2c77fc0d   yeni
3   18   k-002   908c2c77fc0d   tekrar
4   25   k-003   d332a3b518f8   yeni
5   29   k-004   3d130b865763   yeni
6   33   k-005   9331a50e2dfd   yeni
7   41   k-006   286596c96b64   yeni
8   45   k-007   847e6e31dbfc   yeni
9   54   k-008   0542d8380547   yeni
10  60   k-009   17d20393c63f   yeni
11  66   k-010   86378989704b   yeni
12  73   k-010   86378989704b   tekrar
13  78   k-010   2f1382fedd1e   yeni
14  83   k-011   3d4cfae4fefc   yeni
15  88   k-011   774b1e11c564   yeni
16  91   k-012   8b84de0171eb   yeni
17  95   k-013   77d3df14dcdb   yeni
18  101  k-014   77112bd049c4   yeni
19  105  k-015   65de4c0ce42a   yeni
20  111  k-015   65de4c0ce42a   tekrar

20 derleme 17 ayri imaj ozeti uretti; 3 derleme onceki bir ozeti yeniden uretti
23 etiket 17 ayri ozete isaret ediyor
  65de4c0ce42a: 5 etiket -> son, kararli, dal-hazirlik, s-19, s-20
  908c2c77fc0d: 2 etiket -> s-2, s-3
  86378989704b: 2 etiket -> s-11, s-12
kaynak alani yazilmis derleme: 14/20
```

Yirmi derleme **on yedi** ayrı imaj özeti üretmiştir. Üç derleme, önceki bir derlemenin özetini
yeniden üretmiştir: kaynak da katmanlar da değişmediği için manifest aynı çıkmıştır. Bu, önceki
dersin belirlenimci derleme disiplininin görünür kanıtıdır — damga koyan bir derlemede yirmi
derleme yirmi ayrı özet verirdi.

İkinci sayı ters yöndedir: **23 etiket 17 özete işaret eder.** Bir özetin beş adı vardır —
`son`, `kararli`, `dal-hazirlik`, `s-19`, `s-20`. İkisi değişmezdir, üçü bir sonraki derlemede
başka bir özeti gösterecektir. Aynı nesneye beş addan bakılabilmesi bir kusur değil, deponun
düzenidir; kusur, dağıtım kaydına bu adlardan hangisinin yazıldığının nesneyi belirleyip
belirlemediğidir.

#### Etiket Taşındığında Ne Değişir

Etiket taşındığında yeni bir imaj adreslenir, ama imajın tamamı yeni değildir. Manifest bir
katman listesidir ve iki manifest listenin bazı satırlarını paylaşabilir.

```js
// olcum-agi/tasima.mjs — etiket tasindiginda kac katman gercekten degisti (model)
import { gecmis, KATMAN } from "./imaj.mjs";

const G = gecmis();
const TAM = KATMAN.reduce((t, [, b]) => t + b, 0);
const mb = (b) => (b / 1e6).toFixed(1) + " MB";

// Etiketin hedef aldigi derlemeler, sirayla.
const hedefler = {
  son: G,
  kararli: G.filter((d) => d.temizKostu),
  "dal-hazirlik": G.filter((d) => d.no % 4 === 0),
};

console.log("etiket".padEnd(15) + "tasima".padEnd(8) + "0 katman".padEnd(10) +
  "1 katman".padEnd(10) + "2+ katman".padEnd(11) + "yeni bayt".padEnd(11) + "tam bayt");
const ayrinti = [];
for (const [etiket, dizi] of Object.entries(hedefler)) {
  const dagilim = [0, 0, 0];
  let yeniToplam = 0;
  for (let i = 1; i < dizi.length; i++) {
    const eski = dizi[i - 1].katmanlar, yeni = dizi[i].katmanlar;
    const degisen = yeni.filter((k, j) => k.ozet !== eski[j].ozet);
    const bayt = degisen.reduce((t, k) => t + k.bayt, 0);
    yeniToplam += bayt;
    dagilim[Math.min(degisen.length, 2)] += 1;
    if (etiket === "kararli" && degisen.length !== 1) {   // 1 katman olagan hal; ozette var
      ayrinti.push(`s-${dizi[i - 1].no} -> s-${dizi[i].no}`.padEnd(16) +
        `${degisen.length}/4`.padEnd(8) +
        (degisen.map((k) => k.ad).join(", ") || "-").padEnd(39) + mb(bayt));
    }
  }
  const tasima = dizi.length - 1;
  console.log(etiket.padEnd(15) + String(tasima).padEnd(8) + String(dagilim[0]).padEnd(10) +
    String(dagilim[1]).padEnd(10) + String(dagilim[2]).padEnd(11) +
    mb(yeniToplam).padEnd(11) + mb(tasima * TAM));
}

console.log('\n"kararli" tasimalarinda bir katmandan baska sayida degisenler:');
console.log("tasima".padEnd(16) + "katman".padEnd(8) + "degisen".padEnd(39) + "yeni bayt");
for (const s of ayrinti) console.log(s);

// Katman basina: 19 "son" tasimasinin kacinda degisti?
const say = {};
for (let i = 1; i < G.length; i++)
  G[i].katmanlar.forEach((k, j) => {
    if (k.ozet !== G[i - 1].katmanlar[j].ozet) say[k.ad] = (say[k.ad] ?? 0) + 1;
  });
console.log("\nkatman basina degisim (19 tasimada): " +
  KATMAN.map(([ad]) => `${ad} ${say[ad] ?? 0}`).join(", "));
console.log(`tam imaj ${mb(TAM)}; degismez surum etiketi ${G.length}, tasinabilir etiket ` +
  `${Object.keys(hedefler).length}`);
```

```
etiket         tasima  0 katman  1 katman  2+ katman  yeni bayt  tam bayt
son            19      3         10        6          141.7 MB   655.5 MB
kararli        14      1         8         5          120.1 MB   483.0 MB
dal-hazirlik   4       0         0         4          58.5 MB    138.0 MB

"kararli" tasimalarinda bir katmandan baska sayida degisenler:
tasima          katman  degisen                                yeni bayt
s-2 -> s-3      0/4     -                                      0.0 MB
s-3 -> s-5      3/4     temel, uygulama, yapilandirma          18.5 MB
s-6 -> s-8      2/4     uygulama, yapilandirma                 8.0 MB
s-8 -> s-10     3/4     bagimliliklar, uygulama, yapilandirma  24.0 MB
s-16 -> s-17    2/4     uygulama, yapilandirma                 8.0 MB
s-18 -> s-20    2/4     uygulama, yapilandirma                 8.0 MB

katman basina degisim (19 tasimada): temel 1, bagimliliklar 2, uygulama 13, yapilandirma 7
tam imaj 34.5 MB; degismez surum etiketi 20, tasinabilir etiket 3
```

`son` etiketi on dokuz kez taşınmıştır. Bu on dokuz taşımanın **onunda yalnız bir katman**, üçünde
**hiçbir katman** değişmiştir. Sıfır katmanlı taşıma etiketin taşındığı ama içeriğin aynı kaldığı
durumdur: ad yeni bir özeti gösterir, özet aynı manifesti adresler, indirilecek bayt yoktur.
Dışarıdan bakan tarafta ise "yeni bir imaj yayımlandı" görünür ve bir dağıtım tetiklenebilir.

Bayt sütunları taşımanın gerçek ağırlığını verir: on dokuz taşımada değişen toplam **141,7 MB**,
aynı on dokuz imajın toplam boyutu ise **655,5 MB**. Katman paylaşımı taşımaların yükünün yüzde
78'ini ortadan kaldırmıştır. `dal-hazirlik` etiketi tersi uçtadır — dört taşımasının dördünde de
iki ya da daha çok katman değişmiştir, çünkü arasında dört derleme birikmiştir. **Etiketin
taşınma sıklığı azaldıkça taşıma başına değişen katman sayısı artar.**

Son satır değişimin nereden geldiğini gösterir: on dokuz taşımada uygulama katmanı 13, yapılandırma
7, bağımlılıklar 2, **temel yalnız 1 kez** değişmiştir. Bu, önceki dersin güncelleme ölçümüyle aynı
yöne bakar — alt katmanlar seyrek değişir, üst katmanlar neredeyse her derlemede.

#### Özetten Kaynağa Geri Dönüş

Şimdiye kadar ölçülen her şey ileri yöndeydi: kaynaktan derlemeye, derlemeden özete, özetten
etikete. Olay sırasında sorulan soru ters yöndedir — üretimde koşan bu nesne hangi kaynaktan
geldi?

**IM36.** Yirmi dört dağıtım kurgudur; tohum `20260707` görünürdür ve istenen adın türü —
taşınabilir etiket, değişmez sürüm etiketi ya da doğrudan özet — üreteçten gelir. Zincir iki
halkadan oluşur: dağıtım kaydından imaj özetine, imaj özetinden kaynak sürümüne.

```js
// olcum-agi/izlenebilirlik.mjs — bir dagitimin kaynagi ozetten geriye izlenebiliyor mu (model)
import { gecmis, uretec } from "./imaj.mjs";

const G = gecmis();
const r = uretec(20260707);
const ALAN_BAYT = 96;        // kaynak surumu + kosum kimligi + zaman damgasi (kurgu)
const TAM = 34.5e6;

// 24 dagitim: gun ve istenen ad. Ad ya tasinabilir etiket, ya surum etiketi, ya dogrudan ozettir.
const dagitimlar = [];
let gun = 3;
while (dagitimlar.length < 24) {
  gun += 1 + Math.round(r() * 7);
  const p = r();
  dagitimlar.push({ gun, tur: p < 0.60 ? "tasinabilir" : p < 0.85 ? "surum" : "ozet" });
}

// Etiket defteri gune gore ilerletilir: dagitim aninda cozulen sey o anki hedeftir.
const defter = new Map();
let sirada = 0;
for (const d of dagitimlar) {
  while (sirada < G.length && G[sirada].gun <= d.gun) {
    const b = G[sirada++];
    defter.set("son", b); defter.set(`s-${b.no}`, b);
    if (b.temizKostu) defter.set("kararli", b);
  }
  const surumEtiketleri = [...defter.keys()].filter((k) => k.startsWith("s-"));
  d.ad = d.tur === "tasinabilir" ? (r() < 0.6 ? "kararli" : "son")
    : d.tur === "surum" ? surumEtiketleri[surumEtiketleri.length - 1]
    : "ozet:dogrudan";
  d.hedef = d.tur === "ozet" ? defter.get("son") : defter.get(d.ad);
}

// Degismez surum etiketi, defter onu tasimayi reddettigi surece ozeti belirler; tasinabilir
// etiket belirlemez. Uc disiplin: (1) kayit yalniz istenen adi yazar, (2) cozulen ozeti de
// yazar, (3) ayrica her derleme kaynak alanini yazar.
const ozetiBelli = (d) => d.tur === "ozet" || d.tur === "surum";
const izlenebilir = (hepsindeOzet, hepsindeKaynak) =>
  dagitimlar.filter((d) => (hepsindeOzet || ozetiBelli(d)) &&
    (hepsindeKaynak || d.hedef.kaynakYazildi)).length;

const N = dagitimlar.length;
console.log("disiplin".padEnd(34) + "ozet biliniyor".padEnd(16) + "kaynak okunuyor");
console.log("kayitta yalniz istenen ad".padEnd(34) +
  `${dagitimlar.filter(ozetiBelli).length}/${N}`.padEnd(16) +
  `${izlenebilir(false, false)}/${N}`);
console.log("kayitta cozulen ozet de var".padEnd(34) + `${N}/${N}`.padEnd(16) +
  `${izlenebilir(true, false)}/${N}`);
console.log("ustune her derleme kaynagi yazar".padEnd(34) + `${N}/${N}`.padEnd(16) +
  `${izlenebilir(true, true)}/${N}`);

// Ters yon: ayni kaynak surumu kac ayri imaj ozeti uretti?
const kaynaktan = new Map();
for (const b of G) kaynaktan.set(b.kaynak, new Set([...(kaynaktan.get(b.kaynak) ?? []), b.imaj]));
const cokluk = [...kaynaktan.entries()].filter(([, s]) => s.size > 1);
console.log(`\ndagitim turu: tasinabilir ${dagitimlar.filter((d) => d.tur === "tasinabilir").length}, ` +
  `surum etiketi ${dagitimlar.filter((d) => d.tur === "surum").length}, ` +
  `dogrudan ozet ${dagitimlar.filter((d) => d.tur === "ozet").length}`);
console.log(`${kaynaktan.size} kaynak surumunun ${cokluk.length} tanesi birden cok imaj ozeti uretti: ` +
  cokluk.map(([k, s]) => `${k} -> ${s.size}`).join(", "));
console.log(`izlenebilirligin bedeli: imaj basina ${ALAN_BAYT} bayt, tam imajin ` +
  `${Math.round(TAM / ALAN_BAYT)}'te 1'i; dagitim kaydina ${N} x 1 alan`);
```

```
disiplin                          ozet biliniyor  kaynak okunuyor
kayitta yalniz istenen ad         14/24           9/24
kayitta cozulen ozet de var       24/24           17/24
ustune her derleme kaynagi yazar  24/24           24/24

dagitim turu: tasinabilir 10, surum etiketi 10, dogrudan ozet 4
14 kaynak surumunun 3 tanesi birden cok imaj ozeti uretti: k-002 -> 2, k-010 -> 2, k-011 -> 2
izlenebilirligin bedeli: imaj basina 96 bayt, tam imajin 359375'te 1'i; dagitim kaydina 24 x 1 alan
```

Üç satır bir merdivendir. Mevcut düzende yirmi dört dağıtımın **dokuzunun** kaynağı okunabilir.
Kayıp iki yerdedir: on dağıtım taşınabilir bir adla yapıldığı için hangi özeti aldığı kayıtta
yoktur ve özeti bilinen dağıtımların bir bölümünde de hedef derleme kaynak alanını yazmamıştır.
Kayda çözülen özetin de yazılması oranı **17/24**'e çıkarır; kaynak alanının her derlemede
yazılması **24/24** yapar.

Değişmez sürüm etiketiyle yapılan on dağıtımın özeti belirlemesi bir varsayıma dayanır: defterin
`s-N` etiketini taşımayı reddetmesi. Bu reddetme bir özellik değil, deponun uyguladığı bir
kuraldır — M22/K01'de ölçülen değişmezlik denetiminin buradaki karşılığıdır. Kural uygulanmazsa
ilk satırdaki 14 de düşer ve merdivenin ilk basamağı dörde iner.

İkinci sonuç ters yöndedir. On dört kaynak sürümünün **üçü** birden çok imaj özeti üretmiştir:
kaynak değişmeden bağımlılık ya da yapılandırma katmanı değişmiştir. Geriye izlenebilirlik
kaynağa kadar götürür, kaynaktan ileriye tek bir nesneye götürmez.

**Bu yalıtımın bedeli sayıyla küçüktür.** Kaynak alanı imaj başına 96 bayttır — tam imajın 359
binde biri — ve dağıtım kaydına eklenen tek bir alandır. Ölçülen kazanç, 9/24'ten 24/24'e çıkan
bir orandır. **Yalıtımın delindiği yer** ise etiket defterinin kendisidir: defter imajın içinde
değil, deponun içindedir. İmaj taşınabilir bir çıktıdır, adı taşınabilir değildir; başka bir
depoya kopyalanan imaj etiketlerini yanında götürmez, yalnız özetini götürür.

#### Özet

- Etiket üç düzeyli bir zincirdir: etiket bir imaj özetini, imaj özeti bir manifesti, manifest de
  katman özetlerini gösterir. Etiket bayta değil, listeye işaret eder.
- 20 derleme 17 ayrı imaj özeti üretmiştir; 3 derleme önceki bir özeti yeniden üretmiştir. Bu,
  belirlenimci derlemenin görünür kanıtıdır — damgalı derlemede sayı 20 olurdu.
- 23 etiket 17 özete işaret eder; bir özetin beş adı vardır. Kusur çok adlılık değil, kayda
  yazılan adın nesneyi belirleyip belirlememesidir.
- `son` etiketinin 19 taşımasının 10'unda tek katman, 3'ünde hiçbir katman değişmiştir. Değişen
  toplam 141,7 MB, aynı imajların toplamı 655,5 MB'dir: paylaşım yükün yüzde 78'ini kaldırır.
  Etiketin taşınma sıklığı azaldıkça taşıma başına değişen katman sayısı artar.
- Dağıtımın kaynağına geri izlenebilme oranı kayıt disiplinine bağlıdır: 9/24, çözülen özet de
  yazılınca 17/24, kaynak alanı her derlemede yazılınca 24/24. Bedeli imaj başına 96 bayt ve
  kayda tek alandır.

#### Sonraki Adım

Bu dersin ölçümü bir şeyi sessizce varsaydı: manifestteki her satırın karşılığı bir yerde
duruyor. O yer hiç ölçülmedi. On dokuz taşımada değişen 141,7 MB'nin karşılığında değişmeyen
513,8 MB vardır ve bu baytların kaç kopya olduğu katmanların paylaşılıp paylaşılmadığına bağlıdır.

Sonraki ders o yeri kurar ve sayar: aynı katmanı birden çok imaj referans ettiğinde depo gerçekte
ne kadar büyüyor, bir imaj silindiğinde kaç katman gerçekten silinebiliyor, saklama kuralı katman
referanslarına çarptığında ne oluyor ve yerelde zaten bulunan katman atlandığında indirilen bayt
ne kadar düşüyor?

### İmaj Depoları

Source: https://academia.sh/tr/kurslar/konteynerler/imaj-depolari

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?

### İmaj Güvenliği

Source: https://academia.sh/tr/kurslar/konteynerler/imaj-guvenligi

İmaj güvenliği üç parçada ölçülür: üst katmanda silinen bir dosyanın alt katmanda gerçekte kaç bayt olarak durduğu gerçek dizinlerde sayılır, bilinen bir kusur kümesinde koşan tarama için yakalanan, kaçan ve yanlış alarm sayıları çıkarılır, imzalama gerçek bir anahtar çiftiyle bir zincir olarak kurulur ve doğrulamanın atlandığı her adımda neyin kabul edildiği sayılır.

Önceki ders depoyu ölçtü ve bir soruyla bitti: özel imajın dokuz katmanı genel havuzda
okunabiliyorsa, o katmanların içinde ne var? Şimdiye kadar katmanlara birer bayt yığını olarak
bakıldı. Bu ders içeri bakar ve konuyu kapatır.

Üç ölçü vardır ve üçü de aynı sorunun parçalarıdır: durur hâldeki bir çıktının içinde ne olduğu
nasıl bilinir? Birincisi imajın kendi yapısından gelen bir sızıntıdır, ikincisi bilinen kusurları
bulma girişimidir, üçüncüsü çıktının kimden geldiğinin doğrulanmasıdır.

**IM43.** Katmanlar gerçek dizinlerdir ve birleşim görünümü alttan üste kurulur; bir dosyanın
silinmesi üst katmanda `.sil-` önekli bir işaret dosyasıyla gösterilir. **IM44.** Sır değerleri
kurgudur; gerçek bir sır kullanılmaz.

#### Katmanda Kalan Sır

M22/K01 bir sırrın kaç ayrı yoldan dışarı sızabildiğini taramıştı; o tarama burada tekrarlanmaz.
Buradaki olgu tek ve katmana özgüdür: **bir katman değişmez bir nesnedir.** Bir dosyayı silen
yönerge yeni bir katman üretir; alttaki katman olduğu gibi durur, çünkü onun özeti başka imajlarda
da geçer. Silme, görünümü değiştirir; baytları değiştirmez.

```js
// olcum-agi/sizinti.mjs — ust katmanda silinen dosya alt katmanda duruyor mu (gercek dizinler)
import { mkdirSync, rmSync, writeFileSync, readdirSync, readFileSync, statSync } from "node:fs";
import { join, dirname } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-katman");
rmSync(KOK, { recursive: true, force: true });

// Kurgu sir degerleri. Gercek bir sir kullanilmaz.
const SIR_A = "kurgu-erisim-anahtari-A1B2C3D4E5F6";
const SIR_B = "kurgu-veritabani-parolasi-Z9Y8X7";
const govde = (sir, dolgu) => Buffer.concat([Buffer.from(`${sir}\n`), Buffer.alloc(dolgu, "-")]);

const KATMANLAR = [
  { ad: "k1-temel", yaz: { "bin/toplayici": 4096, "lib/sifreleme": 8192 } },
  { ad: "k2-bagimlilik", yaz: { "lib/istemci": 6144, "lib/kuyruk": 3072 } },
  { ad: "k3-uygulama",
    yaz: { "app/toplayici.js": 5120, "app/gizli/erisim.anahtar": govde(SIR_A, 1664),
           "app/gecici/kurulum.log": govde(SIR_B, 2048) },
    ayniKatmandaSil: ["app/gecici/kurulum.log"] },   // katman kapanmadan once kaldirilir
  { ad: "k4-yapilandirma", yaz: { "app/ayarlar.json": 512 },
    ustKatmandaSil: ["app/gizli/erisim.anahtar"] },  // ust katmanda silme isareti birakilir
];

// Katmanlar gercek dizinlere yazilir; silme isareti ".sil-" onekli bos bir dosyadir.
for (const k of KATMANLAR) {
  for (const [yol, deger] of Object.entries(k.yaz)) {
    if (k.ayniKatmandaSil?.includes(yol)) continue;
    const tam = join(KOK, k.ad, yol);
    mkdirSync(dirname(tam), { recursive: true });
    writeFileSync(tam, typeof deger === "number" ? Buffer.alloc(deger, "x") : deger);
  }
  for (const yol of k.ustKatmandaSil ?? []) {
    const tam = join(KOK, k.ad, dirname(yol), ".sil-" + yol.split("/").pop());
    mkdirSync(dirname(tam), { recursive: true });
    writeFileSync(tam, "");
  }
}

const dolas = (kok, alt = "") => readdirSync(join(kok, alt), { withFileTypes: true })
  .flatMap((g) => g.isDirectory() ? dolas(kok, join(alt, g.name)) : [join(alt, g.name)]);

// Birlesim gorunumu: alttan uste; silme isareti gorunen dosyayi kaldirir.
const gorunum = new Map();
for (const k of KATMANLAR) {
  for (const yol of dolas(join(KOK, k.ad))) {
    const ad = yol.split("/").pop();
    if (ad.startsWith(".sil-")) gorunum.delete(join(dirname(yol), ad.slice(5)));
    else gorunum.set(yol, statSync(join(KOK, k.ad, yol)).size);
  }
}

const diskte = KATMANLAR.flatMap((k) => dolas(join(KOK, k.ad))
  .filter((y) => !y.split("/").pop().startsWith(".sil-"))
  .map((y) => [k.ad, y, statSync(join(KOK, k.ad, y)).size]));
const gorunumBayt = [...gorunum.values()].reduce((t, b) => t + b, 0);
const diskBayt = diskte.reduce((t, [, , b]) => t + b, 0);
const gizlenen = diskte.filter(([, y]) => !gorunum.has(y));

console.log(`birlesim gorunumu   : ${gorunum.size} dosya, ${gorunumBayt} bayt`);
console.log(`katmanlarda gercekte: ${diskte.length} dosya, ${diskBayt} bayt`);
console.log(`gorunumde yok, katmanda duruyor: ${gizlenen.length} dosya, ` +
  `${gizlenen.reduce((t, [, , b]) => t + b, 0)} bayt`);

for (const [ad, sir] of [["SIR_A", SIR_A], ["SIR_B", SIR_B]]) {
  const bulunan = diskte.filter(([k, y]) => readFileSync(join(KOK, k, y)).includes(sir));
  console.log(`${ad}: ${bulunan.length} katman dosyasinda, ` +
    `${bulunan.reduce((t, [, , b]) => t + b, 0)} bayt` +
    (bulunan.length ? ` -> ${bulunan.map(([k, y]) => `${k}/${y}`).join(", ")}` : ""));
}
```

```
birlesim gorunumu   : 6 dosya, 27136 bayt
katmanlarda gercekte: 7 dosya, 28835 bayt
gorunumde yok, katmanda duruyor: 1 dosya, 1699 bayt
SIR_A: 1 katman dosyasinda, 1699 bayt -> k3-uygulama/app/gizli/erisim.anahtar
SIR_B: 0 katman dosyasinda, 0 bayt
```

Birleşim görünümü altı dosya ve 27.136 bayt gösterir. Diskte yedi dosya ve 28.835 bayt vardır.
Aradaki **1.699 bayt**, üst katmanda silinmiş görünen anahtar dosyasıdır ve `k3-uygulama`
katmanında bütün olarak durmaktadır. Görünüme bakan bir denetim onu bulamaz; katmanı açan biri
doğrudan okur.

İki sırrın farkı ölçüyü tamamlar. `SIR_B` de yazılmış, ama **aynı katman kapanmadan**
kaldırılmıştır: hiçbir katman dosyasında geçmez, sıfır bayt. `SIR_A` bir üst katmanda
silinmiştir ve tam boyutuyla durur. Aradaki tek fark silmenin hangi katmanda yapıldığıdır ve
sonuç ikili bir farktır — 1.699 bayta karşı 0 bayt.

Bu, önceki dersin ölçümüyle birleştiğinde ağırlaşır. Depoda bir katman ancak hiçbir manifest onu
referans etmiyorsa silinebilirdi; ölçümde düşen imajların on yedi katmanından üçü referanslı
olduğu için diskte kalmıştı. Sır içeren bir katman da aynı kurala tabidir: onu referans eden
başka bir imaj varsa, sırrı taşıyan imaj silinse bile baytlar depoda durur. **Yalıtımın delindiği
yer budur** — çıktının içine alınan fark, çıktıdan çıkarılamıyor.

Kapatma yolu ölçümün içinde yazılıdır. `SIR_B`'nin sıfır baytı, değerin katman kapanmadan
kaldırılmasıyla elde edilmiştir: aynı yönerge içinde yazılıp silinen bir dosya katmana hiç
girmez. Daha sağlamı ise hiç yazmamaktır — değerin yalnız derleme adımı boyunca görünen bir
kaynaktan okunması ve çıktıya kopyalanmaması. İki yolun ölçülen sonucu aynıdır, 1.699 bayta
karşı 0 bayt; ama birincisi yönergelerin sırasına, ikincisi çıktının yapısına dayanır ve sıra
bozulduğunda birincisi sessizce geri döner.

#### Tarama Neyi Görür

İkinci ölçü, imajın içindeki bileşenleri bilinen bir kusur kümesiyle karşılaştırır. Tarama bir
eşleştirmedir ve eşleştirmenin iki yönde de hatası vardır.

**IM45.** Kusur kümesi kurgudur: bir bileşenin açık kaydı olma olasılığı 0,30, kaydın düzeltmesinin
sürüme geri taşınmış olma olasılığı 0,25'tir; tohum `20260909` görünürdür. **IM46.** Görünürlük
adaya bağlıdır — paket yöneticisi olan bir temelde envanter okunur, olmayanda bileşen ikilinin
içine gömülü gelir ve sürümü sayılamaz.

```js
// olcum-agi/tarama.mjs — bilinen kusur kumesinde tarama: yakalanan, kacan, yanlis alarm (model)
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;

const P_KUSUR = 0.30;    // bilesenin kusur kumesinde acik bir kaydi var
const P_YAMALI = 0.25;   // kaydi var ama duzeltme surume geri tasinmis: acik degil

// gorunurluk: bilesenin envanterde surumuyle okunabilme olasiligi. Paket yoneticisi
// olmayan bir temelde envanter yoktur; bilesen ikilinin icine gomulu gelir.
const ADAY = [["tam donanimli", 148, 0.92], ["kucultulmus", 31, 0.82], ["tek ikili", 6, 0.35]];

const tara = (bilesen, gorunurluk, tohum = 20260909) => {
  const r = uretec(tohum);
  const s = { bilesen, acik: 0, uyari: 0, yakalanan: 0, kacan: 0, yanlis: 0 };
  for (let i = 0; i < bilesen; i++) {
    const kayit = r() < P_KUSUR, gorunur = r() < gorunurluk, yamali = r() < P_YAMALI;
    const acik = kayit && !yamali;          // gercekten acik kusur
    const bulur = kayit && gorunur;         // taramanin urettigi uyari
    if (acik) s.acik += 1;
    if (bulur) s.uyari += 1;
    if (acik && bulur) s.yakalanan += 1;
    if (acik && !bulur) s.kacan += 1;
    if (bulur && !acik) s.yanlis += 1;
  }
  return s;
};

console.log("aday".padEnd(15) + "bilesen".padEnd(9) + "gorunurluk".padEnd(12) +
  "acik kusur".padEnd(12) + "uyari".padEnd(7) + "yakalanan".padEnd(11) +
  "kacan".padEnd(7) + "yanlis alarm");
const sonuc = [];
for (const [ad, n, g] of ADAY) {
  const s = tara(n, g);
  sonuc.push([ad, s]);
  console.log(ad.padEnd(15) + String(n).padEnd(9) + `%${(100 * g).toFixed(0)}`.padEnd(12) +
    String(s.acik).padEnd(12) + String(s.uyari).padEnd(7) + String(s.yakalanan).padEnd(11) +
    String(s.kacan).padEnd(7) + String(s.yanlis));
}

console.log("");
const yuzde = (a, b) => (b ? `%${(100 * a / b).toFixed(0)}` : "olcusuz");
for (const [ad, s] of sonuc) {
  console.log(`${ad.padEnd(15)} yakalama orani ${yuzde(s.yakalanan, s.acik)}, ` +
    `uyarilarin dogrulugu ${yuzde(s.yakalanan, s.uyari)}`);
}
```

```
aday           bilesen  gorunurluk  acik kusur  uyari  yakalanan  kacan  yanlis alarm
tam donanimli  148      %92         31          37     29         2      8
kucultulmus    31       %82         7           7      6          1      1
tek ikili      6        %35         1           0      0          1      0

tam donanimli   yakalama orani %94, uyarilarin dogrulugu %78
kucultulmus     yakalama orani %86, uyarilarin dogrulugu %86
tek ikili       yakalama orani %0, uyarilarin dogrulugu olcusuz
```

Tam donanımlı temelde 31 açık kusur vardır; tarama 37 uyarı üretir, bunların 29'u gerçektir,
**8'i yanlış alarmdır** ve **2 açık kusur kaçmıştır.** Yanlış alarmın kaynağı, düzeltmesi sürüme
geri taşınmış bir kayıttır: sürüm numarası kusurlu aralıkta görünür, kod değildir. Kaçmanın
kaynağı ise envanterde okunamayan bileşendir.

Son satır bu dersin en sert sayısıdır. **Tek ikili temelde tarama sıfır uyarı üretir, ama bir açık
kusur vardır.** Temiz bir rapor, kusursuz bir imaj demek değildir; sayılamayan bileşen sayılmamış
olur. Önceki derste ölçülen ödünç burada ikinci kez görünür — küçük temel gerçekten daha az kusur
taşır (148 bileşene karşı 6), ama taşıdığının görünürlüğünü de düşürür.

**Taramanın bedeli sayılabilir:** 37 uyarının 8'i boşa bakılmış uyarıdır ve her biri bir bileşenin
sürümüyle kusur kaydının elle karşılaştırılmasını gerektirir. Yakalama oranını yükseltmenin yolu
daha çok uyarı değil, envanterin okunabilir olmasıdır — bileşen listesinin imajın yanında ayrı bir
belge olarak taşınması bu boşluğu kapatır ve boyutu birkaç kilobayttır.

#### İmzalama Zinciri

Üçüncü ölçü kimlik sorusudur. Tarama "içinde ne var" der; imza "bu gerçekten bizim ürettiğimiz mi"
der. Zincir üç halkadır: içerik özeti, o özetin imzası ve çekme anındaki doğrulama.

**IM47.** İmzalama gerçek bir anahtar çiftiyle yapılır, ama anahtar her koşumda yeniden üretilir;
bu yüzden basılan şey imzanın uzunluğu ve doğrulama sonucudur, imzanın kendisi değil. **IM48.**
Çekmede indirilen katmanın özetinin yeniden hesaplanması isteğe bağlı bir adımdır.

```js
// olcum-agi/imza.mjs — imzalama zinciri: ozet, imza, dogrulama (gercek anahtarla)
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozetle = (b) => createHash("sha256").update(b).digest();
// Anahtar cifti her kosumda yeniden uretilir. Imza baytlari kosuma baglidir; bu yuzden
// basilan sey imzanin uzunlugu ve dogrulama sonucudur, imzanin kendisi degil.
const { publicKey, privateKey } = generateKeyPairSync("ed25519");

// Onceki dersin 17 ayri imaji: her manifest dort katman ozetinin listesidir.
const manifest = (i) => Buffer.from([1, 2, 3, 4]
  .map((k) => ozetle(Buffer.from(`katman-${k}-${i}`)).toString("hex").slice(0, 12)).join("\n"));
const MANIFEST = Array.from({ length: 17 }, (_, i) => manifest(i));
const IMZA = MANIFEST.map((m) => sign(null, ozetle(m), privateKey));

console.log(`imzalanan manifest ${MANIFEST.length}, imza uzunlugu ${IMZA[0].length} bayt, ` +
  `toplam ${MANIFEST.length * IMZA[0].length} bayt`);
console.log(`depo 176200 bayt; imzalarin payi binde ` +
  `${(1000 * MANIFEST.length * IMZA[0].length / 176200).toFixed(1)}`);

// Zincirin halkalari ayri ayri sinanir.
const m0 = MANIFEST[0];
const kurcalanmis = Buffer.from(m0.toString().replace(/^.{12}/, "ffffffffffff"));
const { publicKey: baskaAnahtar } = generateKeyPairSync("ed25519");
console.log("\ndogru manifest, dogru imza :", verify(null, ozetle(m0), publicKey, IMZA[0]));
console.log("kurcalanmis manifest       :", verify(null, ozetle(kurcalanmis), publicKey, IMZA[0]));
console.log("baska anahtarla dogrulama  :", verify(null, ozetle(m0), baskaAnahtar, IMZA[0]));

// Katman baytinin kendisi: manifest dogru olsa bile inen bayt degisebilir.
const katman = Buffer.alloc(7200, "u");
const beklenen = ozetle(katman).toString("hex");
const bozuk = Buffer.from(katman);
bozuk[42] = 0x41;                               // tek bayt degistirildi
console.log("inen katmanin ozeti tutuyor:", ozetle(bozuk).toString("hex") === beklenen);

// 24 cekme, uc disiplin: neyi kabul ederdi?
const CEKME = 24, KATMAN = 4;
console.log("\ndisiplin".padEnd(36) + "kabul edilen sahte manifest".padEnd(29) + "dogrulanmayan katman");
for (const [ad, imzaBakilir, katmanBakilir] of [
  ["imza dogrulanmiyor", false, false],
  ["yalniz imza dogrulaniyor", true, false],
  ["imza ve katman ozeti dogrulaniyor", true, true],
]) {
  console.log(ad.padEnd(36) + `${imzaBakilir ? 0 : CEKME}/${CEKME}`.padEnd(29) +
    `${katmanBakilir ? 0 : CEKME * KATMAN}/${CEKME * KATMAN}`);
}
```

```
imzalanan manifest 17, imza uzunlugu 64 bayt, toplam 1088 bayt
depo 176200 bayt; imzalarin payi binde 6.2

dogru manifest, dogru imza : true
kurcalanmis manifest       : false
baska anahtarla dogrulama  : false
inen katmanin ozeti tutuyor: false

disiplin                           kabul edilen sahte manifest  dogrulanmayan katman
imza dogrulanmiyor                  24/24                        96/96
yalniz imza dogrulaniyor            0/24                         96/96
imza ve katman ozeti dogrulaniyor   0/24                         0/96
```

**İmzanın bedeli en küçük kalemdir:** on yedi manifest için toplam 1.088 bayt, deponun binde
6,2'si. Doğrulamanın kendisi de bir özet karşılaştırmasından ibarettir.

Orta üç satır zincirin çalıştığını gösterir: doğru manifest doğru imzayla doğrulanır, manifestin
ilk on iki karakteri değiştirildiğinde doğrulama düşer, imza başka bir anahtarla sınandığında da
düşer. Dördüncü satır zincirin nerede bittiğini gösterir — indirilen katmanın tek baytı
değiştirildiğinde özet tutmaz, **ama bu ancak özet yeniden hesaplanırsa görülür.**

Son tablo doğrulamanın atlandığı her adımın açtığı yolu sayar. Doğrulama yoksa yirmi dört
çekmenin yirmi dördü değiştirilmiş bir manifesti kabul eder. Yalnız imza doğrulanıyorsa sahte
manifest sıfıra iner, ama **doksan altı katmanın doksan altısı** özeti sınanmadan kabul edilir:
manifest doğrudur, inen baytın manifeste uyduğu bilinmez. İki halka da sınandığında iki sayı da
sıfırdır.

Zincirin dışında kalan bir şey vardır ve **yalıtımın burada delindiği yer odur**: imza "bu imaj
güvenlidir" demez, "bu manifest bu anahtarla imzalanmıştır" der. Anahtarın kime ait olduğu, nerede
saklandığı ve kimin imzalayabildiği modelin dışındadır. İmzalama, güvenin nereye taşındığını
belirler; ortadan kaldırmaz.

Zincir önceki dersin izlenebilirlik merdivenine de bağlanır. Kaynak sürümü alanı imajın hangi
kaynaktan geldiğini söyler, imza o beyanın üretildikten sonra değişmediğini söyler. Alan
imzalanmamışsa, sonradan yazılabilir bir nottur; imza olmadan izlenebilirlik oranı bir kayıt
disiplinini ölçer, bir güvenceyi değil.

#### Özet

- Bir katman değişmez bir nesnedir; silme görünümü değiştirir, baytları değiştirmez. Ölçümde
  birleşim görünümü 6 dosya ve 27.136 bayt gösterirken diskte 7 dosya ve 28.835 bayt vardır.
- Fark 1.699 bayttır ve üst katmanda silinmiş görünen anahtar dosyasının tamamıdır. Aynı katman
  kapanmadan kaldırılan ikinci sır ise hiçbir katman dosyasında geçmez: 1.699 bayta karşı 0 bayt.
- Sır içeren katman referanslıysa, sırrı taşıyan imaj silinse bile depoda durur; çıktının içine
  alınan fark çıktıdan çıkarılamaz.
- Tarama iki yönde de yanılır: tam donanımlı temelde 31 açık kusurun 29'u yakalanır, 2'si kaçar
  ve 37 uyarının 8'i yanlış alarmdır. Tek ikili temelde tarama 0 uyarı üretir ama 1 açık kusur
  vardır — sayılamayan bileşen sayılmamış olur.
- İmzalama ucuzdur: 17 manifest için 1.088 bayt, deponun binde 6,2'si. Doğrulama yoksa 24
  çekmenin 24'ü sahte manifesti kabul eder; yalnız imza doğrulanıyorsa 96 katmanın 96'sı özeti
  sınanmadan kabul edilir. İmza güveni ortadan kaldırmaz, anahtara taşır.

#### Sonraki Adım

Bu konu boyunca çıktı küçüldü, katmanlara ayrıldı, paylaşıldı, adlandırıldı, tarandı ve imzalandı.
Ölçülen her sayı — bayt, katman, referans, uyarı, imza — **durur hâldeki** bir nesne üzerineydi.
Nesne diskte duruyordu ve hiçbir şey yapmıyordu.

Oysa bu çıktının varlık nedeni çalışmaktır. Çalışmaya başladığı anda içindeki dosyalar bir sürecin
gördüğü dosyalar olur, katmanlar üst üste bindirilip yazılabilir bir yüzeye dönüşür, imzayla
doğrulanan şey belleğe alınır. Yalıtımın hangi bölümü bu geçişten sağ çıkar? Salt okunur olduğu
ölçülen katmanlar hâlâ salt okunur mu, imajın içine alınan farkın kaçı çalışma anında yeniden
ortamdan gelir ve bir sürecin ne yapabileceğini artık ne belirler?

## Çalışma Zamanı

Yalıtımın koşum anındaki karşılığı: kaynak sınırları ve ortam değişkenleriyle çalıştırma seçenekleri, geçici dosya sisteminin veriyi kaybetmesi, birimler ve bağlama takmalarıyla kalıcılık, ağ sürücüleri ve servis adlandırma, kök kullanıcıyla çalışmanın riski, bildirimsel bileşim dosyalarıyla çok konteynerli yerel ortam, sıcak yeniden yükleme ve hata ayıklamanın geliştirici deneyimine etkisi, yetenek kısıtlama ve salt okunur dosya sistemiyle çalışma zamanı güvenliği.

### Çalıştırma Seçenekleri

Source: https://academia.sh/tr/kurslar/konteynerler/calistirma-secenekleri

Konteynerin dışarıdan aldığı iki ayar: kaynak sınırı aşıldığında bekletme, öldürme ve azaltma kararlarının gecikme, kaybedilen iş ve yeniden başlatma cinsinden bedeli, çok sıkı sınırın kendi işine ve çok gevşek sınırın komşu konteynere yansıması, ortam değişkenlerinin imajın dışında bıraktığı fark ve kaç değişkenin tanımsız bırakıldığında servisi düşürdüğü.

Bir önceki konu çıktıyı **durur hâlde** ölçtü: kaç katman, kaç bayt, hangi özet, hangi imza. O
ölçümlerin tamamı bir dosya kümesi üzerineydi ve o dosya kümesi hiç çalışmıyordu. Çalışmaya
başladığı anda ölçülen şey değişir. Yalıtımın bir bölümü sürer — dosya sistemi hâlâ imajın
dediğidir, kütüphane sürümü hâlâ donmuştur. Bir bölümü ise düşer: işlemci, bellek ve ayar artık
imajın içinden değil, **dışından** gelir.

Bu ders düşen bölümün ilk ikisini sayar: konteynere verilen **kaynak sınırı** ve **ortam
değişkenleri**. İkisinin ortak yanı şudur — hiçbiri imajın içinde yazmaz. Aynı imaj, iki farklı
sınırla iki farklı davranış gösterir; aynı imaj, eksik bir değişkenle hiç kalkmaz. Yalıtım bütçesi
açısından bu, imajın kapatamadığı bir açıktır ve büyüklüğü ölçülebilir.

Ölçümler kurgu bir **bölgesel ölçüm ağı** üzerinden yapılıyor: su sayaçlarından okuma toplayan,
doğrulayan, faturaya çeviren ve saha ekiplerine iş emri açan bir yazılım. Sistem kurgudur, sayılar
aşağıdaki koşumdan gelir.

#### Sınır Aşıldığında Verilen Karar

Bir kaynak sınırı, aşıldığında bir şey **yapılmasını** gerektirir. Sınırın kendisi bir sayıdır;
karar o sayıya çarpıldığında verilir ve üç seçenek vardır: işi bekletmek, konteyneri öldürmek,
işi azaltmak. Aşağıdaki koşum bir **pay dağıtıcı** modelidir — makinenin dönem başına dağıttığı
işlemci payını iki konteyner arasında bölüştürür ve üç kararın bedelini ayrı ayrı sayar.

- **CZ1** — Ölçüm ağının dört servisi tek makinede, dört ayrı konteyner olarak koşuyor.
- **CZ2** — Makinenin kapasitesi dönem başına 100 pay olarak normalize edildi; pay ve dönem soyut
  birimlerdir, saniye ya da çekirdek değildir.
- **CZ3** — Doğrulayıcı her dönem 40–50 pay istiyor; gecelik toplu iş 60. dönemde başlayıp 140.
  dönemde bitiyor ve o pencerede 60–80 pay istiyor.
- **CZ4** — Öldürme kararı biriken iş 300 birimi aştığında veriliyor; yeniden başlatma 5 dönem
  sürüyor ve o dönemlerde gelen iş kaybediliyor.
- **CZ5** — Azaltma kararı, sınırı aşan işin 0,6'sını tutup kalanını düşürüyor.

```js
// calisma/pay-dagitici.mjs — sinir asildiginda verilen kararin bedeli.
// MODELDIR: donem soyut bir adim, pay soyut bir birim; talep tohumlu bir uretecten cekiliyor.
const DONEM = 200, MAKINE = 100;                 // CZ2: makine kapasitesi 100 pay/donem
const BAS = 60, BIT = 140, TOHUM = 20260801;     // CZ3: gecelik isin penceresi
const KUYRUK_SINIR = 300, YENIDEN = 5;           // CZ4: oldurme esigi ve yeniden baslatma
const AZALT = 0.6;                               // CZ5: azaltmada tutulan pay

const uretec = (t) => { let s = t % 2147483647; return () => (s = (s * 48271) % 2147483647) / 2147483647; };
const talep = (r, t) => ({
  dogrulayici: 40 + Math.round(r() * 10),
  gecelik: t >= BAS && t < BIT ? 60 + Math.round(r() * 20) : 4,
});

function kosum({ karar, sinir }) {
  const r = uretec(TOHUM);
  const ad = ["dogrulayici", "gecelik"];
  const d = Object.fromEntries(ad.map((a) => [a, { kuyruk: 0, tamam: 0, kayip: 0, bekleme: 0, oldu: 0, kapali: 0, kendi: 0, makine: 0 }]));

  for (let t = 0; t < DONEM; t += 1) {
    const gelen = talep(r, t), istek = {}, pay = {};
    for (const a of ad) {
      if (d[a].kapali > 0) { d[a].kapali -= 1; d[a].kayip += gelen[a]; istek[a] = 0; pay[a] = 0; continue; }
      istek[a] = gelen[a] + d[a].kuyruk;
      pay[a] = Math.min(istek[a], sinir[a]);          // kendi sinirinda kirpma
      d[a].kendi += istek[a] - pay[a];
    }
    const toplam = pay.dogrulayici + pay.gecelik;
    for (const a of ad) {
      const verilen = toplam > MAKINE ? Math.floor((MAKINE * pay[a]) / toplam) : pay[a];
      d[a].makine += pay[a] - verilen;                // makinede kirpma: komsuya yansiyan kisim
      d[a].tamam += verilen;
      if (d[a].kapali > 0) continue;
      let artan = istek[a] - verilen;
      if (karar === "azaltma") { const tut = Math.round(artan * AZALT); d[a].kayip += artan - tut; artan = tut; }
      d[a].kuyruk = artan;
      if (karar === "oldurme" && d[a].kuyruk > KUYRUK_SINIR) {
        d[a].oldu += 1; d[a].kayip += d[a].kuyruk; d[a].kuyruk = 0; d[a].kapali = YENIDEN;
      }
      d[a].bekleme += d[a].kuyruk;
    }
  }
  return d;
}

const b = (x, n = 2) => x.toFixed(n);
const SINIR = { dogrulayici: 50, gecelik: 60 };
console.log(`model: ${DONEM} donem, makine ${MAKINE} pay/donem, sinirlar ${SINIR.dogrulayici}+${SINIR.gecelik}=` +
  `${SINIR.dogrulayici + SINIR.gecelik} (asiri taahhut), gecelik is ${BAS}-${BIT}. donem, tohum ${TOHUM}`);
console.log(`\n${"karar".padEnd(11)}${"is".padEnd(13)}${"tamam".padStart(9)}${"kayip".padStart(8)}` +
  `${"ort. bekleme".padStart(14)}${"oldurme".padStart(9)}${"kapali donem".padStart(14)}`);
const kar = {};
for (const karar of ["bekletme", "oldurme", "azaltma"]) {
  const k = kosum({ karar, sinir: SINIR });
  kar[karar] = k;
  for (const a of ["dogrulayici", "gecelik"])
    console.log(`${karar.padEnd(11)}${a.padEnd(13)}${String(k[a].tamam).padStart(9)}${String(k[a].kayip).padStart(8)}` +
      `${b(k[a].bekleme / k[a].tamam).padStart(14)}${String(k[a].oldu).padStart(9)}${String(k[a].oldu * YENIDEN).padStart(14)}`);
}
console.log(`\n${"gecelik sinir".padEnd(15)}${"gecelik tamam".padStart(15)}${"gecelik bekleme".padStart(17)}` +
  `${"dogrulayici tamam".padStart(19)}${"dogrulayici bekleme".padStart(21)}${"makine kirpmasi".padStart(17)}`);
for (const s of [20, 40, 60, 80, 100]) {
  const k = kosum({ karar: "bekletme", sinir: { dogrulayici: 50, gecelik: s } });
  console.log(`${String(s).padEnd(15)}${String(k.gecelik.tamam).padStart(15)}${b(k.gecelik.bekleme / k.gecelik.tamam).padStart(17)}` +
    `${String(k.dogrulayici.tamam).padStart(19)}${b(k.dogrulayici.bekleme / k.dogrulayici.tamam).padStart(21)}` +
    `${String(k.dogrulayici.makine + k.gecelik.makine).padStart(17)}`);
}
```

```
model: 200 donem, makine 100 pay/donem, sinirlar 50+60=110 (asiri taahhut), gecelik is 60-140. donem, tohum 20260801

karar      is               tamam   kayip  ort. bekleme  oldurme  kapali donem
bekletme   dogrulayici       9012       0          0.12        0             0
bekletme   gecelik           6092       0         10.54        0             0
oldurme    dogrulayici       9012       0          0.06        0             0
oldurme    gecelik           4124    1968          2.15        3            15
azaltma    dogrulayici       8891     121          0.02        0             0
azaltma    gecelik           4986    1106          0.33        0             0

gecelik sinir    gecelik tamam  gecelik bekleme  dogrulayici tamam  dogrulayici bekleme  makine kirpmasi
20                        3040           122.72               9012                 0.00                0
40                        5840            30.08               9012                 0.00                0
60                        6092            10.54               9012                 0.12             1033
80                        6092             5.48               8625                 6.00             2817
100                       6092             2.49               8306                10.38             4215
```

Bu sayılar **ölçüm** sınıfındadır ve tohuma bağlıdır.

#### Üç Kararın Üç Ayrı Bedeli

Gecelik işin toplam talebi her üç koşumda da 6092 birimdir; kararlar bu 6092'yi farklı yerlere
dağıtır ve toplam korunur.

**Bekletme** hiçbir iş kaybetmez: kayıp 0, tamamlanan 6092. Bedeli tamamen zamandır — tamamlanan
her birim ortalama **10,54 dönem** kuyrukta bekliyor. Doğrulayıcının beklemesi aynı koşumda 0,12
dönem; yani bekletme kararının bedelini neredeyse tümüyle sınıra dayanan işin kendisi ödüyor.

**Öldürme** bekleme sayısını 2,15 döneme indiriyor, çünkü kuyruk 300 birimi aştığında siliniyor.
Bu indirim bir muhasebe hilesidir: kuyruk kısaldı çünkü içindeki iş atıldı. Bedel iki kalemde
görünür — **1968 birim kayıp** (talebin yüzde 32'si) ve **3 öldürme**, her biri 5 dönem kapalı
kalmayla, toplam **15 dönem** hiç iş yapılmayan süre. Tamamlanan iş 6092'den 4124'e düşüyor.

**Azaltma** ikisinin ortasındadır ve sayısı en tuhaf olanıdır: bekleme 0,33 döneme iniyor, kayıp
1106 birimde kalıyor, öldürme yok. Sınırı aşan işin yüzde 40'ı düşürüldüğü için kuyruk hiç
büyümüyor. Ama azaltma koşumunda dikkat çeken sayı gecelik işin değil, **doğrulayıcının** kaybıdır:
121 birim. Doğrulayıcı hiçbir zaman kendi 50 pay sınırına dayanmadı; onu kırpan şey makinedir. Ve
azaltma kararı bütün konteynerlerde geçerli olduğu için, makinenin kırptığı işin yüzde 40'ı
doğrulayıcı için de düşürülüyor. Karar tek konteynerde verilir, sonucu iki konteynerde görünür.

Seçim veri sınıfına bağlıdır. Faturaya girecek bir okuma için 1968 birimlik kayıp kabul edilemez;
bekletme tek seçenektir ve 10,54 dönemlik gecikme ödenir. Bir eğilim grafiğini besleyen özet için
azaltma en ucuz karardır: 1106 birim kayba karşılık bekleme 10,54'ten 0,33'e iner. Öldürme
kimsenin **seçtiği** karar değildir; bellek sınırında verilen karar budur ve seçilmemiş olması
bedelini değiştirmez.

#### Sınır Taraması: Sıkı Uç, Gevşek Uç

İkinci tablo aynı işi beş ayrı sınırla koşturuyor ve sınır seçiminin iki ayrı yöne bedel yüklediğini
gösteriyor.

**Sıkı uçta** bedel konteynerin kendi işindedir. 20 pay sınırında gecelik iş 200 dönem boyunca
yalnız 3040 birim bitiriyor — talebin yarısı bile değil; koşum bittiğinde **3052 birim hâlâ
kuyrukta** duruyor ve birim başına bekleme 122,72 döneme çıkıyor. Bu sınırda gecelik iş bitmiyor,
sadece ertesi güne taşıyor. Buna karşılık komşu tamamen dokunulmamış: doğrulayıcının beklemesi
0,00, makine kırpması 0. **Sıkı sınır yalıtımı tam kurar ve faturayı kendi konteynerine keser.**

**Gevşek uçta** bedel yer değiştirir. Sınır 60'ta gecelik iş zaten tamamını bitiriyor: 6092. Sınırı
80'e, sonra 100'e çıkarmak bitirilen işi **hiç artırmıyor** — üç satırda da 6092. Değişen tek şey
beklemedir: 10,54 → 5,48 → 2,49. Yani gevşetmenin kazancı yalnız gecikmedir ve bu kazancın parasını
komşu öder. Doğrulayıcının tamamladığı iş 9012'den 8306'ya iniyor (706 birim eksik) ve beklemesi
0,12'den 10,38 döneme çıkıyor. Makine kırpması 1033'ten 4215 birime tırmanıyor.

Bu, kaynak sınırının en yanlış anlaşılan yanıdır ve tabloda çıplak duruyor: **sınır bir tavandır,
bir garanti değildir.** 50 ve 100 sınırlarının toplamı 150'dir, makine 100 verebilir. Toplam
aşıldığı anda kim ne kadar alacağına karar veren şey konteynerin sınırı değil, dağıtıcıdır — ve o
dağıtıcı konteynerin dışındadır. Yalıtımın delindiği yer burasıdır: ad alanı süreçleri ayırır,
sınır tavanı koyar, ama **makinenin kendisi paylaşılmaya devam eder** ve aşırı taahhütte komşunun
gecikmesi 86 katına çıkabilir.

#### Ortamda Kalan Fark

İkinci ayar kalemi ortam değişkenleridir. İmaj dosya sistemi farkını içine aldı: kurulu paketler,
kütüphane sürümleri, dosya yolları donduruldu. Donmayan şey **aynı imajın farklı yerlerde farklı
davranmasını sağlayan** değerlerdir — veri ucu, parola, tarife tablosu, saat dilimi. Bunlar imajın
dışındadır çünkü imaja girselerdi imaj her yer için ayrı olurdu.

- **CZ6** — Dört servisin ortam değişkeni envanteri kurgudur; adlar ve zorunluluk sınıfları model
  verisidir. Eksik tanım kontrolü gerçekten koşuyor.

```js
// calisma/ortam-degiskenleri.mjs — imaja alinamayan farkin sayimi.
// MODELDIR: dort servisin degisken envanteri kurgudur; eksik tanim kontrolu gercekten kosuyor.
const SERVIS = {                                                        // CZ6: kurgu envanter
  toplayici: { zorunlu: ["SAYAC_AG_UCU", "OKUMA_KUYRUGU", "TOPLAMA_ARALIGI"],
    varsayilanli: { GUNLUK_DUZEYI: "bilgi", ES_ZAMAN: "4" } },
  dogrulayici: { zorunlu: ["OKUMA_KUYRUGU", "KURAL_SURUMU", "VERI_UCU", "VERI_PAROLASI"],
    varsayilanli: { SAPMA_ESIGI: "0.15", GUNLUK_DUZEYI: "bilgi" } },
  faturalama: { zorunlu: ["VERI_UCU", "VERI_PAROLASI", "TARIFE_TABLOSU", "SAAT_DILIMI"],
    varsayilanli: { YUVARLAMA: "2", GUNLUK_DUZEYI: "bilgi" } },
  isemri: { zorunlu: ["VERI_UCU", "VERI_PAROLASI", "SAHA_UCU"],
    varsayilanli: { YENIDEN_DENEME: "3" } },
};

function baslat(ad, ortam) {                       // eksik zorunlu tanim varsa servis kalkmaz
  const s = SERVIS[ad], eksik = s.zorunlu.filter((k) => ortam[k] === undefined);
  if (eksik.length) throw new Error(`${ad}: tanimsiz ${eksik.join(", ")}`);
  return { ...s.varsayilanli, ...Object.fromEntries(s.zorunlu.map((k) => [k, ortam[k]])) };
}

const adlar = Object.keys(SERVIS);
const zorunlu = new Set(adlar.flatMap((a) => SERVIS[a].zorunlu));
const varsayilanli = new Set(adlar.flatMap((a) => Object.keys(SERVIS[a].varsayilanli)));
const tum = new Set([...zorunlu, ...varsayilanli]);
const tanim = adlar.reduce((t, a) => t + SERVIS[a].zorunlu.length + Object.keys(SERVIS[a].varsayilanli).length, 0);
const yalnizVars = [...varsayilanli].filter((k) => !zorunlu.has(k)).length;
console.log(`model: ${adlar.length} servis, ${tum.size} farkli degisken adi, ${tanim} tanim satiri; ` +
  `zorunlu ${zorunlu.size} ad, yalniz varsayilanli ${yalnizVars} ad`);

const dolu = Object.fromEntries([...tum].map((k) => [k, "deger"]));
const sayim = (ortam) => adlar.filter((a) => { try { baslat(a, ortam); return false; } catch { return true; } });
console.log(`bos ortamla kalkmayan servis: ${sayim({}).length}/${adlar.length}; ` +
  `dolu ortamla kalkmayan: ${sayim(dolu).length}/${adlar.length}`);
try { baslat("faturalama", { VERI_UCU: "x", VERI_PAROLASI: "y" }); } catch (e) { console.log(`ornek: ${e.message}`); }

console.log(`\n${"degisken".padEnd(17)}${"kac serviste".padStart(14)}${"cikarilinca dusen".padStart(19)}${"sinif".padStart(15)}`);
let dusen = 0, kirilgan = 0;
for (const k of [...tum].sort()) {
  const kac = adlar.filter((a) => SERVIS[a].zorunlu.includes(k) || k in SERVIS[a].varsayilanli).length;
  const d = sayim(Object.fromEntries(Object.entries(dolu).filter(([x]) => x !== k))).length;
  dusen += d;
  if (d > 0) kirilgan += 1;
  console.log(`${k.padEnd(17)}${String(kac).padStart(14)}${String(d).padStart(19)}${(zorunlu.has(k) ? "zorunlu" : "varsayilanli").padStart(15)}`);
}
console.log(`\ntoplam: ${tum.size} degiskenin ${kirilgan} tanesi tek basina en az bir servisi dusuruyor; ` +
  `tek tek cikarma toplaminda ${dusen} servis-dusmesi. Cikti degismedi, ${tum.size} ad ortamda kaldi.`);
```

```
model: 4 servis, 14 farkli degisken adi, 21 tanim satiri; zorunlu 9 ad, yalniz varsayilanli 5 ad
bos ortamla kalkmayan servis: 4/4; dolu ortamla kalkmayan: 0/4
ornek: faturalama: tanimsiz TARIFE_TABLOSU, SAAT_DILIMI

degisken           kac serviste  cikarilinca dusen          sinif
ES_ZAMAN                      1                  0   varsayilanli
GUNLUK_DUZEYI                 3                  0   varsayilanli
KURAL_SURUMU                  1                  1        zorunlu
OKUMA_KUYRUGU                 2                  2        zorunlu
SAAT_DILIMI                   1                  1        zorunlu
SAHA_UCU                      1                  1        zorunlu
SAPMA_ESIGI                   1                  0   varsayilanli
SAYAC_AG_UCU                  1                  1        zorunlu
TARIFE_TABLOSU                1                  1        zorunlu
TOPLAMA_ARALIGI               1                  1        zorunlu
VERI_PAROLASI                 3                  3        zorunlu
VERI_UCU                      3                  3        zorunlu
YENIDEN_DENEME                1                  0   varsayilanli
YUVARLAMA                     1                  0   varsayilanli

toplam: 14 degiskenin 9 tanesi tek basina en az bir servisi dusuruyor; tek tek cikarma toplaminda 14 servis-dusmesi. Cikti degismedi, 14 ad ortamda kaldi.
```

Ortamdan çıkarılamayan farkın sayısı burada okunuyor: **14 değişken adı**, 21 tanım satırı. Dosya
yolları ve kütüphane sürümleri çıktının içine alındı, bu 14 ad alınamadı. Dokuzu zorunludur ve
tanımsız bırakıldığında servis hiç kalkmaz — boş ortamla dört servisin dördü de düşüyor. Beşinin
varsayılanı vardır ve çıkarıldıklarında hiçbir servis düşmez; onlar farkı taşır ama kırılganlık
üretmez.

Sağ sütun kırılganlığın nerede yoğunlaştığını gösteriyor. `VERI_UCU` ve `VERI_PAROLASI` üç serviste
birden zorunludur; tek bir tanım unutulduğunda üç konteyner birden kalkmaz. Bu ikisi 14 adın
yüzde 14'üdür ama tek tek çıkarma toplamındaki 14 servis-düşmesinin 6'sını, yani yüzde 43'ünü tek
başına üretir. `SAAT_DILIMI` ise bir başka sınıftır: imaj saat dilimi verisini içine aldı ama
**hangi saat diliminde çalışılacağı** kararını alamadı, çünkü o karar imaja değil koşuma aittir.
Faturalama için bu tek değişken bir gün sınırının nereye düştüğünü belirler.

Yalıtımın bedeli burada yazım cinsindendir: 21 tanım satırı, her koşum yerinde bir kez. Bedelin
karşılığı, aynı imajın dört ayrı yerde dört ayrı davranışla koşabilmesidir.

#### Özet

- Kaynak sınırı aşıldığında üç karardan biri verilir ve üçü de aynı 6092 birimlik işi farklı
  yerlere dağıtır: bekletme kaybı 0 tutar ama birim başına 10,54 dönem gecikme yükler; öldürme
  gecikmeyi 2,15 döneme indirir ama 1968 birim kaybettirir ve 15 dönem kapalı geçirir; azaltma
  1106 birim kayba karşılık gecikmeyi 0,33 döneme indirir. Öldürmenin kısa kuyruğu bir kazanç
  değildir: kuyruk kısaldı çünkü içindeki iş silindi ve konteyner 3 kez yeniden başladı.
- Azaltma kararının sonucu iki konteynerde görünür: gecelik iş 1106 birim kaybederken hiç sınıra
  dayanmamış doğrulayıcı da 121 birim kaybediyor, çünkü kırpan şey konteynerin sınırı değil
  makinedir.
- Sıkı sınır faturayı kendi konteynerine keser: 20 pay sınırında gecelik iş 200 dönemde ancak 3040
  birim bitiriyor, 3052 birim kuyrukta kalıyor, bekleme 122,72 döneme çıkıyor; komşunun beklemesi
  0,00'da kalıyor.
- Gevşek sınır faturayı komşuya keser: sınırı 60'tan 100'e çıkarmak gecelik işin bitirdiği 6092
  birimi hiç artırmıyor, yalnız beklemesini 2,49'a indiriyor; karşılığında doğrulayıcı 706 birim
  eksik iş bitiriyor ve beklemesi 0,12'den 10,38 döneme çıkıyor.
- **Yalıtımın delindiği yer:** sınır bir tavandır, bir garanti değildir. Sınırların toplamı makine
  kapasitesini aştığında dağıtım kararı konteynerin dışında verilir; makine kırpması 1033'ten
  4215 birime çıkıyor.
- İmaj dosya sistemi farkını içine aldı, 14 değişken adını alamadı. Dokuzu zorunludur, boş ortamla
  dört servisin dördü de kalkmaz; ikisi üç serviste birden zorunlu olduğu için tek tek çıkarma
  toplamındaki 14 servis-düşmesinin 6'sını üretir.

#### Sonraki Adım

Kaynak sınırı da ortam değişkeni de konteynerin **dışından** verilen ayarlardı; ikisi de konteyner
başlamadan önce belliydi ve konteyner çalışırken değişmedi. Oysa çalışan bir konteyner boş durmaz:
günlük yazar, önbellek doldurur, yarım kalmış toplu işin durumunu bir yere kaydeder, indirdiği
dosyayı bir dizine koyar. Bu veri imajda yoktu, ortamdan da gelmedi — konteynerin kendisi üretti.
Peki nereye yazıldı ve konteyner durduğunda ona ne oluyor? Sonraki ders yazılabilir katmanı gerçek
dizinlerle kurar ve şunu sayar: bir koşum boyunca kaç bayt birikiyor, konteyner silindiğinde kaç
kayıt gidiyor, bir baytlık değişiklik neden bir baytlık yer tutmuyor ve bu kaybın hangi veri
sınıfında kabul edilebilir olduğu.

### Geçici Dosya Sistemi

Source: https://academia.sh/tr/kurslar/konteynerler/gecici-dosya-sistemi

Konteynerin çalışırken ürettiği verinin akıbeti: yazılabilir katmanın gerçek dizinlerle kurulması, otuz gecelik bir koşum boyunca biriken baytın veri sınıflarına dağılımı, tek baytlık bir değişikliğin kopyala-yukarı ile tam dosya kadar yer tutması, konteyner silindiğinde giden kayıt sayısı ve kaybın hangi veri sınıfında kabul edilebilir olduğu.

Önceki ders konteynere **dışarıdan** verilen iki ayarı ölçtü: kaynak sınırı ve ortam değişkenleri.
İkisi de konteyner başlamadan önce belliydi. Oysa çalışan bir konteyner boş durmaz. Doğrulayıcı her
okuma için bir günlük satırı yazar, hesapladığı tarife karşılığını önbelleğe koyar, gecelik koşumun
nereye geldiğini bir yere kaydeder, operatörün elle girdiği düzeltmeyi bir dosyaya ekler. Bu verinin
hiçbiri imajda yoktu ve ortamdan da gelmedi — konteynerin kendisi üretti.

Sorusu şudur: nereye yazıldı, ne kadar yer tuttu ve konteyner ortadan kalktığında ne oldu. Yanıt
yalıtımın en sessiz bedelidir; çünkü hiçbir hata vermez, hiçbir sınır aşılmaz. Veri yazılır,
okunur, bir süre kullanılır, sonra bir gün yoktur.

#### Yazılabilir Katman

İmaj salt okunurdur ve bu bir tercih değil, katman modelinin sonucudur: katmanlar içerik özetiyle
adreslenir, içerik değişirse özet değişir, özet değişirse katman başka bir katman olur. Konteyner
yazmaya ihtiyaç duyduğunda bu yığının üstüne **yazılabilir bir katman** eklenir. Okuma üstten alta
doğru çözülür; yazma her zaman en üste düşer.

Aşağıdaki koşum bu düzeni gerçek dizinlerle kuruyor. Birleşim dosya sistemi bir **modeldir** — okuma
çözümü ve kopyala-yukarı kuralı elle yazılmıştır — ama dizinler, dosyalar ve bayt sayıları
gerçektir; hepsi diske yazılıp `node:fs` ile ölçülüyor.

- **CZ7** — İmajın alt katmanları dört dosyadan oluşuyor: uygulama kodu, kural kümesi, tarife
  tablosu, sayaç listesi. Boyutlar kurgudur, dosyalar gerçekten yazılıyor.
- **CZ8** — Gecelik koşum 480 sayaç okuması işliyor.
- **CZ9** — Her okuma bir günlük satırı; her dördüncü okuma bir önbellek yazımı; her altmışıncı
  okumada ilerleme yazımı; her kırkıncıda bir operatör düzeltmesi üretiyor.
- **CZ10** — Her gecenin sonunda tarife tablosunda tek baytlık bir değişiklik yapılıyor.
- **CZ11** — Konteyner 30 gece boyunca hiç yeniden başlatılmadan koşuyor, sonunda siliniyor. Silme
  yazılabilir katmanı kaldırıyor, alt katmanlara dokunmuyor.

```js
// calisma/gecici-katman.mjs — yazilabilir katmanda birikenin ve konteyner silinince gidenin sayimi.
// MODEL: birlesim dosya sistemi node ile modelleniyor; dizinler, dosyalar ve bayt sayilari gercektir.
import { mkdirSync, writeFileSync, appendFileSync, copyFileSync, existsSync, statSync,
  readdirSync, rmSync } from "node:fs";
import { join, dirname } from "node:path";

const ALT = "kok/alt", UST = "kok/ust";                 // salt okunur imaj katmanlari / yazilabilir katman
const OKUMA = 480, GECE = 30, TOHUM = 20260801;         // CZ8: gecelik kosum, CZ11: konteynerin omru
rmSync("kok", { recursive: true, force: true });

const kur = (kok, yol, icerik) => { mkdirSync(join(kok, dirname(yol)), { recursive: true });
  writeFileSync(join(kok, yol), icerik); };
for (const [yol, n] of [["uygulama/dogrulayici.js", 12288], ["uygulama/kurallar.json", 49152],
  ["ayar/tarife.csv", 98304], ["veri/sayac-listesi.csv", 204800]]) kur(ALT, yol, "x".repeat(n));

let kopyaBayt = 0, kopyaSayi = 0;
function ustEkle(yol, satir) {                          // ust katmanda yoksa ve altta varsa: kopyala-yukari
  const u = join(UST, yol);
  if (!existsSync(u) && existsSync(join(ALT, yol))) {
    mkdirSync(dirname(u), { recursive: true });
    copyFileSync(join(ALT, yol), u);
    kopyaBayt += statSync(u).size; kopyaSayi += 1;
  }
  mkdirSync(dirname(u), { recursive: true });
  appendFileSync(u, satir);
}

let s = TOHUM % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
const sayim = { gunluk: 0, onbellek: 0, durum: 0, kullanici: 0, ayar: 0 };
function gecelikKosum() {
  for (let i = 1; i <= OKUMA; i += 1) {
    const deger = (100 + rast() * 900).toFixed(3);
    ustEkle("gunluk/dogrulama.log", `okuma ${i} sayac ${1000 + i} deger ${deger} durum tamam\n`);
    sayim.gunluk += 1;
    if (i % 4 === 0) { kur(UST, `onbellek/tarife-${i / 4}.json`, `{"sayac":${1000 + i},"birim":"${deger}"}`); sayim.onbellek += 1; }
    if (i % 60 === 0) { kur(UST, "durum/ilerleme.json", `{"islenen":${i},"kalan":${OKUMA - i}}`); sayim.durum += 1; }
    if (i % 40 === 0) { ustEkle("veri/duzeltmeler.csv", `${1000 + i},operator,${deger}\n`); sayim.kullanici += 1; }
  }
  ustEkle("ayar/tarife.csv", "#");                      // tek baytlik degisiklik, tam dosya kopyalaniyor
  sayim.ayar += 1;
}

const olc = (kok, on = "") => { let b = 0, n = 0;
  const gez = (d) => { for (const e of readdirSync(d, { withFileTypes: true })) {
    const p = join(d, e.name);
    if (e.isDirectory()) gez(p); else if (p.includes(on)) { b += statSync(p).size; n += 1; } } };
  if (existsSync(kok)) gez(kok); return { bayt: b, dosya: n }; };

const a = olc(ALT);
const ON = { gunluk: "gunluk/", onbellek: "onbellek/", durum: "durum/", kullanici: "veri/", ayar: "ayar/" };
console.log(`model: ${OKUMA} okuma/gece, ${GECE} gece (tohum ${TOHUM}); alt katmanlar ` +
  `${a.bayt} bayt / ${a.dosya} dosya, hepsi salt okunur`);
console.log(`\n${"gece".padEnd(6)}${"toplam bayt".padStart(12)}${"dosya".padStart(7)}` +
  Object.keys(ON).map((k) => k.padStart(11)).join("") + `${"alt katmana orani".padStart(19)}`);
for (let g = 1; g <= GECE; g += 1) {
  gecelikKosum();
  if (![1, 2, 7, 30].includes(g)) continue;
  const u = olc(UST);
  console.log(`${String(g).padEnd(6)}${String(u.bayt).padStart(12)}${String(u.dosya).padStart(7)}` +
    Object.values(ON).map((o) => String(olc(UST, o).bayt).padStart(11)).join("") +
    `${(u.bayt / a.bayt).toFixed(2).padStart(19)}`);
}

const u = olc(UST);
console.log(`\nkopyala-yukari ${kopyaSayi} dosyada ${kopyaBayt} bayt; katmandaki payi ilk gece ` +
  `%${((100 * (kopyaBayt + 1)) / 124886).toFixed(1)}, otuzuncu gece %${((100 * kopyaBayt) / u.bayt).toFixed(1)}`);
rmSync(UST, { recursive: true, force: true });          // konteyner siliniyor
console.log(`konteyner silindi: kalan ${olc(ALT).bayt} bayt / ${olc(ALT).dosya} dosya (alt katmanlar), ` +
  `giden ${u.bayt} bayt / ${u.dosya} dosya`);
console.log(`giden kayit: ${sayim.gunluk} gunluk satiri, ${sayim.onbellek} onbellek yazimi, ` +
  `${sayim.durum} ilerleme yazimi, ${sayim.kullanici} operator duzeltmesi, ${sayim.ayar} ayar degisikligi`);
console.log(`iki konteyner ayni imajdan kossaydi: paylasimli ${a.bayt + 2 * u.bayt} bayt, ` +
  `paylasimsiz ${2 * (a.bayt + u.bayt)} bayt; alt katman paylasimi ${a.bayt} bayt kazandiriyor`);
rmSync("kok", { recursive: true, force: true });
```

```
model: 480 okuma/gece, 30 gece (tohum 20260801); alt katmanlar 364544 bayt / 4 dosya, hepsi salt okunur

gece   toplam bayt  dosya     gunluk   onbellek      durum  kullanici       ayar  alt katmana orani
1           124886    124      22452       3840         25        264      98305               0.34
2           147603    124      44904       3840         25        528      98306               0.40
7           261188    124     157164       3840         25       1848      98311               0.72
30          783679    124     673560       3840         25       7920      98334               2.15

kopyala-yukari 1 dosyada 98304 bayt; katmandaki payi ilk gece %78.7, otuzuncu gece %12.5
konteyner silindi: kalan 364544 bayt / 4 dosya (alt katmanlar), giden 783679 bayt / 124 dosya
giden kayit: 14400 gunluk satiri, 3600 onbellek yazimi, 240 ilerleme yazimi, 360 operator duzeltmesi, 30 ayar degisikligi
iki konteyner ayni imajdan kossaydi: paylasimli 1931902 bayt, paylasimsiz 2296446 bayt; alt katman paylasimi 364544 bayt kazandiriyor
```

Bu sayılar **ölçüm** sınıfındadır.

#### Kopyala-Yukarı: Bir Baytın 98.304 Bayt Etmesi

Birinci satır ilk bakışta yanlış görünüyor: ilk gecenin `ayar` sütunu 98.305 bayt. Değiştirilen şey
tek bir bayttı.

Nedeni yazma kuralındadır. Yazılabilir katman alt katmandaki dosyayı **değiştiremez**, çünkü alt
katman salt okunurdur ve özetiyle adreslenmiştir. Bir alt katman dosyasına yazılacaksa dosyanın
tamamı önce üst katmana kopyalanır, sonra kopya değiştirilir. Buna **kopyala-yukarı** denir ve
bedeli değişikliğin boyutuyla değil, **dosyanın** boyutuyla ölçülür.

İlk gecenin bileşimi bu yüzden çarpıktır. Koşumun ürettiği gerçek yeni veri 26.582 bayttır —
katmanın toplamından kopyalanan 98.304 bayt düşüldüğünde kalan. Katmanda duran 124.886 bayt, yani
**4,7 katı**. Farkın tamamı tek bir kopyalanmış dosyadır ve o gece katmanın yüzde 78,7'sini kaplar.
Yalıtımın bedeli burada nettir: dosyayı yerinde değiştirme hakkından vazgeçmenin karşılığı, tek
baytlık bir değişiklik için 98.304 bayt yer ve o kadar kopyalama işidir.

Bedel bir kez ödenir. İkinci gecenin `ayar` sütunu 98.306'dır: dosya artık üst katmandadır, ikinci
bayt onun sonuna yazılır ve hiçbir şey kopyalanmaz. Pratik sonuç şudur — yazılabilir katmanda
**hangi** dosyaya ilk kez dokunulduğu, sonradan ne kadar yazıldığından daha pahalıdır.

#### Otuz Gecede Katmanın Şekli Değişiyor

Tablonun satırları aşağı inildikçe katmanın bileşimi tersine dönüyor. Otuzuncu gecede toplam 783.679
bayt; `ayar` sütunu hâlâ 98.334'te duruyor ve payı yüzde 78,7'den yüzde 12,5'e inmiş. Yerini alan
`gunluk` sütunudur: 673.560 bayt, katmanın yüzde 86'sı. Günlük her gece 22.452 bayt eklediği için
doğrusal büyüyor; kopyala-yukarı ise bir kez ödendiği için sabit kalıyor.

Sağ sütun asıl olguyu veriyor. Yazılabilir katmanın alt katmanlara oranı ilk gece 0,34 iken
otuzuncu gecede **2,15**'e çıkıyor. Otuz gece sonunda konteynerin diskte tuttuğu yerin üçte
ikisinden fazlası imajın değil, koşumun ürettiği veridir. "Konteyner hafiftir" cümlesinin karşılığı
burada bir sayıdır ve ilk gün doğru olan sayı otuzuncu gün yanlıştır.

İki sütun hiç büyümüyor ve nedenleri ayrıdır. `onbellek` 3.840 baytta sabit, çünkü aynı 120 tarife
anahtarı her gece üzerine yazılıyor. `durum` 25 baytta sabit, çünkü ilerleme dosyası her yazımda
üzerine yazılıyor — 240 yazımın yalnız sonuncusu duruyor. Dosya sayısının otuz gece boyunca 124'te
kalması da bundandır: koşum yeni dosya açmıyor, var olanları büyütüyor.

#### Konteyner Silindiğinde Giden

Silme işleminden sonra iki sayı kalıyor: alt katmanlarda 364.544 bayt / 4 dosya duruyor, üst
katmandan 783.679 bayt / 124 dosya gitti. Alt katman dokunulmamıştır — imaj neyse odur. Giden şey
konteynerin **kendi ürettiğidir** ve kayıt cinsinden dökümü şudur: 14.400 günlük satırı, 3.600
önbellek yazımı, 240 ilerleme yazımı, 360 operatör düzeltmesi, 30 ayar değişikliği.

Kayıt sayılarıyla bayt sayıları arasındaki uçurum kendi başına bir bilgidir. Önbellek 3.600 kez
yazıldı ama katmanda 120 dosya ve 3.840 bayt bıraktı: yazımların yüzde 97'si kendinden sonraki
yazımla örtüldü. Yazılabilir katman bir günlük değil, bir **anlık görüntüdür** — ne yazıldığını
değil, en son ne yazıldığını tutar. Bu yüzden silinen şey de yazılanların toplamı değil, son
hâlleridir.

İlerleme satırı ayrıca okunmalıdır: katmanda 25 bayt duruyordu ama yazım sayısı 240. Gecelik koşumun
nereye kadar geldiği bilgisi her seferinde üzerine yazıldı ve konteynerle birlikte gitti. Yeni bir
konteyner o geceyi sıfırdan başlatır; 480 okumanın tamamı yeniden işlenir.

Kaybın hangi sınıfta kabul edilebilir olduğu ölçüm ağının işletme kuralıdır (**CZ12**) ve otuz
gecelik sayılarla şu tabloyu verir:

| Veri sınıfı | Kayıt | Bayt | Kayıp kabul edilir mi | Gerekçe |
|---|---|---|---|---|
| Günlük | 14.400 satır | 673.560 | Koşullu | Satırlar dışarı akıtılıyorsa evet; tek kopya konteynerdeyse arıza incelemesi de gider |
| Önbellek | 3.600 yazım | 3.840 | Evet | Yeniden üretilebilir; bedeli ilk isteklerin yavaşlaması |
| İş durumu | 240 yazım | 25 | Hayır | Yarım koşum baştan başlar, 480 okuma yeniden işlenir |
| Kullanıcı verisi | 360 düzeltme | 7.920 | Hayır | Yeniden üretilemez; operatörün elle girdiği tek kaynak |
| Ayar kopyası | 30 değişiklik | 98.334 | Evet | İmaja ya da ayar kaynağına geri döner |

Tablonun okunma yönü terstir: kabul edilemez iki sınıf katmanın yalnız yüzde 1'ini, 7.945 baytı
tutuyor. Kalan yüzde 99 yeniden üretilebilir veridir. Sorun büyüklük değil, **karışım**: 7.945 bayt
kurtarılamaz veri, 775.734 bayt kurtarılabilir verinin içinde duruyor ve ikisi aynı anda siliniyor.

#### Ortamdan Çıkarılan Fark

Bu kaybın bir de karşı tarafı vardır ve kursun ölçüsü onu sormayı gerektiriyor: geçici katman
ortamdan hangi farkı çıkarıyor.

Çıkardığı fark **koşumlar arası artıktır**. Her gece silinip yeniden yaratılan bir konteyner her
gece aynı 364.544 baytlık, dört dosyalık başlangıçtan koşar; yazılabilir katmanı hiçbir zaman
124.886 baytı geçmez ve iki gecenin başlangıç durumu arasındaki fark tam olarak sıfır bayttır. Yarım
kalmış bir günlük, şişmiş bir önbellek, bir önceki gecenin elle düzeltilmiş ayarı sonraki geceye
taşınmaz. Bir konteynerin "durumsuz" sayılması bu ölçüdür: koşumlar arası fark sıfır.

Otuz gece yaşayan konteyner bu farkı çıkarmıyor. Otuzuncu geceye, katmanı alt katmanların 2,15
katına çıkmış olarak giriyor ve o gecenin davranışı önceki yirmi dokuz gecenin artığına bağlı.
Sorunun aranacağı yer artık yalnız imaj değil, o katmanın içeriğidir — yani ortamdan çıkarıldığı
sanılan fark geri gelmiştir.

Bedeli tablodaki iki satırdır. Koşumlar arası farkı sıfıra indiren şey, aynı anda 240 ilerleme
yazımını ve 360 operatör düzeltmesini de siler. **Durumsuzluk bedava değildir: koşum arası artığı
sıfırlamanın karşılığı, artığın içindeki 7.945 baytlık kurtarılamaz veriyi de sıfırlamaktır.**
Sonraki dersin çözdüğü sorun tam olarak budur.

#### Yalıtımın Delindiği Yer

İki delik vardır ve biri kasıtlıdır.

Kasıtlı olan alt katman paylaşımıdır. Aynı imajdan iki konteyner koşarsa toplam disk 2.296.446 bayt
değil, 1.931.902 bayttır; alt katmanların 364.544 baytı bir kez tutulur ve iki konteynere aynı anda
görünür. Yalıtım burada bilerek delinmiştir, çünkü delik olmasa aynı içerik iki kez saklanırdı. Salt
okunur olduğu için paylaşmanın davranışa etkisi yoktur — ama kaynak tektir.

İkinci delik kasıtsızdır ve adı yanlış anlaşılmadan gelir. **Geçicilik bir dosya sistemi özelliği
değil, bir yaşam döngüsü sözüdür.** Yazılabilir katman bellekte değil, ana makinenin diskinde bir
dizindir. Konteyner *durdurulduğunda* o dizin durmayı sürdürür; 783.679 bayt yerinde kalır ve
konteyner yeniden başlatılırsa geri gelir. Veri ancak konteyner **silindiğinde** gider. Bunun iki
sonucu vardır: durdurulmuş konteyner, gittiğini sandığı veriyi hâlâ diskte tutar; ve hiç
silinmeyen bir konteyner, geçici sandığı katmanı süresiz büyütür — otuz gecede 2,15 katına.

#### Özet

- Yazılabilir katman salt okunur alt katmanların üstüne eklenen ayrı bir birimdir; okuma üstten alta
  çözülür, yazma her zaman en üste düşer.
- **Kopyala-yukarı bedeli dosya boyutuyla ölçülür:** tarife tablosundaki tek baytlık değişiklik
  98.304 bayt kopyalattı ve ilk gece katmanın yüzde 78,7'sini kapladı. O gece üretilen gerçek yeni
  veri 26.582 bayt, katmanda duran 124.886 bayt. Bedel bir kez ödenir: ikinci gece kopyalama yok.
- Otuz gecede katmanın şekli dönüyor: toplam 783.679 bayta çıkıyor, günlük payı yüzde 86'ya
  yükselirken kopyala-yukarı payı yüzde 12,5'e iniyor. Yazılabilir katmanın alt katmanlara oranı
  0,34'ten 2,15'e çıkıyor.
- Konteyner silindiğinde 14.400 günlük satırı, 3.600 önbellek yazımı, 240 ilerleme yazımı, 360
  operatör düzeltmesi ve 30 ayar değişikliği gidiyor; alt katmanın 364.544 baytı kalıyor.
- Kaybın kabul edilebilirliği veri sınıfına bağlıdır ve karışım tehlikelidir: kabul edilemez iki
  sınıf katmanın yalnız yüzde 1'ini, 7.945 baytı tutuyor ama kalan 775.734 baytla aynı anda
  siliniyor.
- **Yalıtımın delindiği yer:** alt katman iki konteyner arasında paylaşılır ve 364.544 bayt bir kez
  tutulur; ayrıca geçicilik bir dosya sistemi özelliği değildir — yazılabilir katman ana makinenin
  diskinde bir dizindir ve konteyner durdurulduğunda değil, silindiğinde gider.

#### Sonraki Adım

Kayıp iki sınıfta kabul edilemezdi: iş durumu ve kullanıcı verisi. İkisi toplam 7.945 bayt tutuyor
ve ikisi de yazılabilir katmanın içinde durduğu için siliniyor. Çözüm bellidir — o iki dosyayı
katmanın dışına, konteynerin yaşam döngüsüne bağlı olmayan bir yere koymak. Ama katmanın dışı ana
makinenin dosya sistemidir ve oraya açılan her yol yalıtımdan bir şey götürür. Sonraki ders iki
bağlama biçimini gerçek dizinlerle kurar ve şunu sayar: konteyner silindiğinde kaç bayt kalıyor,
konteyner içindeki kullanıcı kimliği dizinin sahibiyle uyuşmadığında kaç dosya yazılamıyor ve iki
biçimden hangisi makine değiştiğinde kırılıyor.

### Birimler ve Bağlama Takmaları

Source: https://academia.sh/tr/kurslar/konteynerler/birimler-ve-baglama-takmalari

Kalıcı verinin yazılabilir katmanın dışına çıkarılması: yönetilen birim ile doğrudan dizin takmasının gerçek dizinlerle kurulması, konteyner silindiğinde kalan bayt, konteyner içindeki kullanıcı kimliği ana makinedeki dosya sahibiyle uyuşmadığında yazılamayan düğüm sayısı ve mutlak yola bağlı takmanın makine değiştiğinde kırılması.

Önceki ders kaybın iki sınıfta kabul edilemez olduğunu ölçtü: iş durumu ve kullanıcı verisi, toplam
7.945 bayt. İkisi de yazılabilir katmanın içinde durduğu için konteyner silindiğinde gitti. Çözüm
tek cümleyle söylenir — o iki dosyayı katmanın dışına koymak. Ama katmanın dışı ana makinenin dosya
sistemidir ve oraya açılan her yol yalıtım bütçesinden bir kalem harcar.

Bu ders iki bağlama biçimini gerçek dizinlerle kurup üç şeyi ayrı ayrı sayar: konteyner
silindiğinde kaç bayt kalıyor, konteyner içindeki kullanıcı kimliği dizinin sahibiyle uyuşmadığında
kaç düğüm yazılamıyor ve makine değiştiğinde hangi biçim kırılıyor. Üçüncü ölçüm, yalıtımın bu
kursta en açık delindiği yeri de adıyla verir.

#### İki Bağlama Biçimi

Konteynerin gördüğü dosya sistemi bir **takma tablosudur**: konteyner içindeki her yol, ana makinede
bir yere karşılık gelir. Yazılabilir katman da bu tablodaki bir satırdır, tek farkı konteynerle
birlikte silinmesidir. Katmanın dışına çıkmanın iki yolu vardır ve ikisi tabloda **nasıl
anıldıklarıyla** ayrılır.

**Yönetilen birim** bir adla anılır. Çalışma zamanı adı kendi kök dizini altında bir yere çözer;
ayarda yalnız ad durur, yol durmaz. **Bağlama takması** doğrudan bir mutlak yolla anılır; ayarda
ana makinenin dizin yolu yazılıdır ve çözüm diye bir şey yoktur, yol neyse odur.

İkisinin ortak bir yan etkisi vardır: takma noktası imajda dolu bir dizine denk geliyorsa, oradaki
içerik takma sürdüğü sürece **görünmez** olur. Alt katmandaki baytlar durur, silinmez, yalnız
konteynerin gördüğü yolun ucunda artık başka bir dizin vardır. Bu, imajın içine alınmış bir varsayılan
ayarın koşumda sessizce ortadan kalkması demektir; hata verilmez, dosya "yok" görünür.

Hangi verinin dışarı çıkarılacağı da bir bütçe kararıdır. 02. dersteki beş sınıftan yalnız ikisi
takıldı; günlük yazılabilir katmanda bırakıldı ve 673.560 baytı yine gitti. Takılsaydı kaybolmazdı
ama otuz gecede 673.560 bayt kalıcı diskte birikirdi ve onu kim silecek sorusu da eklenirdi. Dışarı
çıkarılan her sınıf kalıcılık kazandırır, karşılığında bir yaşam döngüsü sorumluluğu yükler.

- **CZ13** — Takma tablosu modeldir: üç konteyner içi yol üç ayrı ana makine hedefine karşılık
  geliyor. Dizinler ve baytlar gerçektir.
- **CZ14** — 02. dersteki koşum aynen tekrarlanıyor: 30 gece, gecede 480 okuma, aynı tohum. Değişen
  tek şey iş durumu ile düzeltmelerin nereye yazıldığıdır; günlük yazılabilir katmanda kalıyor.
- **CZ15** — Yönetilen birim çalışma zamanının kök dizini altında `olcum-durum` adıyla, bağlama
  takması ana makinede mutlak bir yolla anılıyor.
- **CZ16** — Konteyner içindeki kullanıcı kimliği ana makinedeki dosya sahibiyle uyuşmuyor; bu
  yüzden uygulanan izin sınıfı "diğer"dir. Mod bitleri gerçek dosyalardan okunuyor.
- **CZ17** — Sekiz düğümün mod dağılımı kurgudur; saha ekibinin bıraktığı düzeni temsil ediyor.
- **CZ18** — Makine değişimi, kök dizinin adının değişmesiyle modelleniyor.

```js
// calisma/baglama.mjs — iki baglama bicimi: kalicilik, sahiplik catismasi, tasinabilirlik.
// MODEL: takma tablosu ve konteyner kullanici kimligi modeldir; dizinler, bayt ve mod bitleri gercektir.
import { mkdirSync, writeFileSync, appendFileSync, chmodSync, statSync, existsSync,
  readdirSync, renameSync, rmSync } from "node:fs";
import { join, dirname, resolve } from "node:path";

const KOK = "kok", CZ = `${KOK}/cz`, UST = `${KOK}/konteyner/ust`, SAHA = `${KOK}/saha`;
const OKUMA = 480, GECE = 30, TOHUM = 20260801;                 // 02. dersle ayni kosum
rmSync(KOK, { recursive: true, force: true }); rmSync("kok-b", { recursive: true, force: true });

const TAKMA = {                                                  // CZ13: konteyner ici yol -> ana makine
  "/gunluk": { tur: "yazilabilir katman", hedef: `${UST}/gunluk` },
  "/veri/durum": { tur: "yonetilen birim", ad: "olcum-durum", hedef: `${CZ}/birimler/olcum-durum` },
  "/veri/duzeltmeler": { tur: "dogrudan takma", yol: resolve(`${SAHA}/duzeltmeler`), hedef: `${SAHA}/duzeltmeler` },
};
const yaz = (ic, dosya, icerik, ek) => {
  const p = join(TAKMA[ic].hedef, dosya);
  mkdirSync(dirname(p), { recursive: true });
  (ek ? appendFileSync : writeFileSync)(p, icerik);
};

let s = TOHUM % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
for (let g = 1; g <= GECE; g += 1)
  for (let i = 1; i <= OKUMA; i += 1) {
    const deger = (100 + rast() * 900).toFixed(3);
    yaz("/gunluk", "dogrulama.log", `okuma ${i} sayac ${1000 + i} deger ${deger} durum tamam\n`, true);
    if (i % 60 === 0) yaz("/veri/durum", "ilerleme.json", `{"islenen":${i},"kalan":${OKUMA - i}}`, false);
    if (i % 40 === 0) yaz("/veri/duzeltmeler", "duzeltmeler.csv", `${1000 + i},operator,${deger}\n`, true);
  }

const olc = (k) => { let b = 0, n = 0;
  const gez = (d) => { for (const e of readdirSync(d, { withFileTypes: true })) {
    const p = join(d, e.name); if (e.isDirectory()) gez(p); else { b += statSync(p).size; n += 1; } } };
  if (existsSync(k)) gez(k); return { bayt: b, dosya: n }; };

const once = Object.fromEntries(Object.entries(TAKMA).map(([ic, t]) => [ic, olc(t.hedef)]));
rmSync(`${KOK}/konteyner`, { recursive: true, force: true });    // konteyner siliniyor
console.log(`${"takma".padEnd(22)}${"bicim".padEnd(21)}${"silmeden once".padStart(14)}${"silmeden sonra".padStart(16)}`);
let kalan = 0, giden = 0;
for (const [ic, t] of Object.entries(TAKMA)) {
  const sonra = olc(t.hedef).bayt;
  kalan += sonra; giden += once[ic].bayt - sonra;
  console.log(`${ic.padEnd(22)}${t.tur.padEnd(21)}${String(once[ic].bayt).padStart(14)}${String(sonra).padStart(16)}`);
}
console.log(`konteyner silindi: giden ${giden} bayt, kalan ${kalan} bayt (02. derste kalan 0 bayt idi)`);

// CZ16: konteyner icindeki kimlik ana makinenin sahibiyle uyusmuyor; uygulanan izin sinifi "diger".
const DUGUM = [[`${CZ}/birimler/olcum-durum`, 0o755, "d"], [`${CZ}/birimler/olcum-durum/ilerleme.json`, 0o644, "f"],
  [`${CZ}/birimler/olcum-durum/kilit`, 0o644, "f"], [`${SAHA}/duzeltmeler`, 0o775, "d"],
  [`${SAHA}/duzeltmeler/duzeltmeler.csv`, 0o664, "f"], [`${SAHA}/arsiv/2026-07.csv`, 0o600, "f"],
  [`${SAHA}/paylasim`, 0o777, "d"], [`${SAHA}/paylasim/aktarim.csv`, 0o666, "f"]];
for (const [p, m, tur] of DUGUM) {
  if (tur === "d") mkdirSync(p, { recursive: true });
  else if (!existsSync(p)) { mkdirSync(dirname(p), { recursive: true }); writeFileSync(p, "x"); }
  chmodSync(p, m);
}
console.log(`\n${"dugum".padEnd(38)}${"mod".padStart(6)}${"sahip yazabilir".padStart(17)}${"diger yazabilir".padStart(17)}`);
let sahipY = 0, digerY = 0;
for (const [p] of DUGUM) {
  const m = statSync(p).mode & 0o777;
  const s1 = (m & 0o200) !== 0, s2 = (m & 0o002) !== 0;
  sahipY += s1 ? 1 : 0; digerY += s2 ? 1 : 0;
  console.log(`${p.replace(`${KOK}/`, "").padEnd(38)}${m.toString(8).padStart(6)}` +
    `${(s1 ? "evet" : "hayir").padStart(17)}${(s2 ? "evet" : "hayir").padStart(17)}`);
}
console.log(`kimlik uyusmadiginda ${DUGUM.length} dugumun ${DUGUM.length - digerY} tanesi yazilamiyor; ` +
  `kimlik esitlendiginde ${DUGUM.length - sahipY} tanesi yazilamiyor`);

renameSync(KOK, "kok-b");                                        // makine degisti: duzen baska
console.log(`\n${"takma".padEnd(22)}${"ayardaki referans".padEnd(30)}${"yeni makinede".padStart(14)}`);
let kirik = 0, mutlak = 0;
for (const [ic, t] of Object.entries(TAKMA)) {
  if (t.tur === "yazilabilir katman") continue;
  const yeni = t.ad ? join("kok-b/cz/birimler", t.ad) : t.yol;
  const cozuldu = existsSync(yeni);
  if (!cozuldu) kirik += 1;
  if (!t.ad) mutlak += 1;
  console.log(`${ic.padEnd(22)}${(t.ad ? `ad: ${t.ad}` : "mutlak yol").padEnd(30)}${(cozuldu ? "cozuldu" : "kirik").padStart(14)}`);
}
console.log(`iki takmanin ${mutlak} tanesi mutlak yola bagli, makine degisince ${kirik} tanesi kirildi`);
rmSync("kok-b", { recursive: true, force: true });
```

```
takma                 bicim                 silmeden once  silmeden sonra
/gunluk               yazilabilir katman           673560               0
/veri/durum           yonetilen birim                  25              25
/veri/duzeltmeler     dogrudan takma                 7920            7920
konteyner silindi: giden 673560 bayt, kalan 7945 bayt (02. derste kalan 0 bayt idi)

dugum                                    mod  sahip yazabilir  diger yazabilir
cz/birimler/olcum-durum                  755             evet            hayir
cz/birimler/olcum-durum/ilerleme.json    644             evet            hayir
cz/birimler/olcum-durum/kilit            644             evet            hayir
saha/duzeltmeler                         775             evet            hayir
saha/duzeltmeler/duzeltmeler.csv         664             evet            hayir
saha/arsiv/2026-07.csv                   600             evet            hayir
saha/paylasim                            777             evet             evet
saha/paylasim/aktarim.csv                666             evet             evet
kimlik uyusmadiginda 8 dugumun 6 tanesi yazilamiyor; kimlik esitlendiginde 0 tanesi yazilamiyor

takma                 ayardaki referans              yeni makinede
/veri/durum           ad: olcum-durum                      cozuldu
/veri/duzeltmeler     mutlak yol                             kirik
iki takmanin 1 tanesi mutlak yola bagli, makine degisince 1 tanesi kirildi
```

Bu sayılar **ölçüm** sınıfındadır.

#### Kalıcılık: Silinen Konteyner, Kalan Bayt

İlk tablo sorunun çözüldüğünü gösteriyor ve çözümün sınırını da veriyor. Konteyner silindikten sonra
yazılabilir katmandaki 673.560 baytlık günlük gitti; yönetilen birimdeki 25 bayt ve bağlama
takmasındaki 7.920 bayt yerinde. Toplam **7.945 bayt kaldı** — önceki dersin kurtarılamaz sayısının
tam olarak kendisi ve o derste bu sayı sıfırdı.

Kalıcılık açısından iki biçim arasında **hiçbir fark yoktur**. İkisi de yazılabilir katmanın dışında
durur, ikisi de konteynerin ömrüne bağlı değildir. Bu ölçü ikisini ayırmaz; ayıran ölçüler
sonrakilerdir.

Yalıtımın bedeli burada koşum cinsinden değil, **yüzey** cinsindendir. Önceki derste konteynerin ana
makineye açılan yazma yüzeyi tek bir dizindi ve o dizin konteynerle birlikte gidiyordu. Şimdi iki
dizin daha eklendi ve ikisi de konteynerden uzun yaşıyor. Konteyner artık başladığı yerden başlamaz;
başlangıç durumu diskte duran 7.945 bayta bağlıdır. Durumsuzluk 02. derste ölçülen anlamıyla
bozuldu — bilerek ve bir karşılık alarak.

Bedelin ikinci kalemi hemen görünmüyor. Kalan 7.945 baytı silen bir yaşam döngüsü artık yoktur.
Konteyner silindi, katman gitti, birim ve takma durdu; onları kaldıracak olan şey konteynerin
ölümü değil, ayrı bir karardır. Yalıtımın dışına çıkarılan her dizin kendi saklama kuralını da
ister — kim, ne zaman, hangi ölçüte göre silecek. Karar verilmezse ölçü şu olur: konteyner sayısı
sabit kalırken diskte duran dizin sayısı artmayı sürdürür, çünkü her yeni ad yeni bir birim yaratır
ve eskisini kimse aramaz.

#### Sahiplik: Yalıtımın Delindiği Yer

İkinci tablo, konteynerlerde yalıtımın en somut şekilde delindiği yeri veriyor.

Konteynerin içinde çalışan kullanıcının bir kimlik numarası vardır. Bağlama takmasıyla açılan dizin
ise ana makinenin dosya sistemindedir ve oradaki dosyaların sahibi ana makinenin bir kullanıcısıdır.
Yazma izni bu iki kimliğin karşılaştırılmasıyla belirlenir. **Ad alanı süreçleri, ağı ve dosya
sistemi görünümünü ayırır; kullanıcı kimliğinin sayı uzayını ayırmaz.** Konteyner içindeki kimlik
ile ana makinedeki sahip aynı sayı uzayında yaşar ve dosya izni yalnız o sayıya bakar.

Sonucu sayıyla: sekiz düğümün **altısı yazılamıyor**. Yazılabilen ikisi, mod bitlerinde "diğer"
sınıfına yazma hakkı tanınmış olanlardır — 777 dizini ve 666 dosyası. Kalan altı düğüm, sahibi
yazabildiği hâlde konteyner için kapalıdır: 755, 644, 644, 775, 664 ve 600. Kimlikler eşitlendiğinde
sayı **sıfıra** iniyor; sekiz düğümün hepsi yazılabilir hâle geliyor, çünkü uygulanan sınıf "diğer"
değil "sahip" oluyor.

İki satır ayrıca okunmalıdır. `saha/duzeltmeler/duzeltmeler.csv` 664 modundadır; sahibi ve **grubu**
yazabilir. Bu genellikle çözüm sanılır — konteyner kullanıcısı doğru gruba alınırsa yazabilir.
Ancak grup da bir sayıdır ve konteyner içindeki grup kimliği ana makinedekiyle uyuşmuyorsa
uygulanan sınıf yine "diğer"dir; tabloda o satırın yanıtı `hayir`. `saha/arsiv/2026-07.csv` ise
600 modundadır ve yalnız sahibine açıktır: geçmiş dönemin arşivi, takılan dizinin içinde durduğu
hâlde konteyner için hem yazılamaz hem okunamaz. Takma, dizinin **görünmesini** sağlar; içindekilere
erişimi sağlamaz.

Bu tablo iki yanlış çözümü de gösteriyor. Birincisi izinleri gevşetmektir: 777 ve 666 satırları
gerçekten yazılabilir, ama o iki düğüm ana makinedeki **her** kullanıcıya da açıktır; konteynerin
yazabilmesi için ödenen bedel, konteynerle ilgisi olmayan herkesin de yazabilmesidir. İkincisi
kimlikleri eşitlemektir: sayı sıfıra iner, ama o zaman konteyner ana makinedeki bir kullanıcı gibi
davranır ve takılan dizinde o kullanıcının yapabildiği her şeyi yapabilir. Yalıtım bütçesi
açısından ikisi de aynı kalemi harcar — ana makinenin dosya sistemine açılan yetki yüzeyini.

Üçüncü bir yol daha vardır ve ölçüsü tabloda dolaylı okunur: takılan dizini konteynerin
yazamayacağı, yalnız okuyacağı bir kaynak saymak. O zaman altı kapalı düğüm sorun olmaktan çıkar,
çünkü zaten yazma istenmiyordur. Ölçüm ağında bu ayrım nettir — tarife tablosu ve sayaç listesi
okunur, iş durumu ve düzeltmeler yazılır. Sekiz düğümün kaçının gerçekten yazılması gerektiği
sorulduğunda sayı ikiye iner ve çakışma da o ikiye daralır.

Yönetilen birim bu çakışmayı ortadan kaldırmaz, yalnız **başlangıç durumunu** kontrol eder: birim
çalışma zamanı tarafından yaratıldığı için sahipliği baştan konteynerin beklediği kimliğe
ayarlanabilir. Bağlama takmasında böyle bir an yoktur; dizin zaten vardır, sahibi zaten bellidir ve
konteyner ona uymak zorundadır.

#### Taşınabilirlik: Mutlak Yolun Bedeli

Üçüncü tablo iki biçimi ayıran ölçüdür. Makine değiştiğinde — koşum burada kök dizinin adını
değiştirerek modelliyor — yönetilen birim **çözüldü**, bağlama takması **kırıldı**.

Nedeni ayardaki referansın türüdür. Birim bir adla anılır ve ad her makinede çalışma zamanının kendi
kökü altında çözülür; kök nerede olursa olsun `olcum-durum` bulunur. Takma bir mutlak yolla anılır ve
o yol bir makinenin dizin düzenine aittir. İki takmanın biri mutlak yola bağlıdır, makine değişince
**biri kırılmıştır** — yani taşınabilirlik oranı bire iki.

Ortamdan çıkarılan farkın burada bir sayısı vardır ve tabloda iki yönü birden okunur. Konteynerin
**içinden** bakıldığında fark tümüyle çıkarılmıştır: uygulama her makinede `/veri/durum` ve
`/veri/duzeltmeler` yollarına yazar, iki yol da her yerde aynıdır, ikisi de koda gömülüdür ve
makine değiştiğinde değişmez. Fark ana makine tarafına, takma tablosunun sağ sütununa itilmiştir ve
orada iki referanstan biri makineye bağlıdır. Yani çıkarılan fark yok olmadı, **tek bir satıra
toplandı** — ve o satır artık ayarın kendisidir, uygulamanın değil.

Bu, bağlama takmasının kusuru değil tanımıdır: amacı zaten ana makinedeki **belirli** bir dizini
konteynere açmaktır. Kaynak kodun bulunduğu dizini konteynere takan bir geliştirme kurulumu bunu
bilerek yapar; makineye bağlı olması istenen davranıştır. Sorun, aynı takmanın koşum ortamına
taşınmasıdır. Sayı şunu söyler: makineye bağlı olmayan bir ayar isteniyorsa iki biçimden yalnız
biri o sözü verir.

#### Özet

- İki bağlama biçimi de yazılabilir katmanın dışındadır: konteyner silindikten sonra yönetilen
  birimde 25, bağlama takmasında 7.920 bayt kalıyor; toplam 7.945 bayt, önceki derste sıfırdı.
  Yazılabilir katmandaki 673.560 baytlık günlük yine gitti.
- Kalıcılık ölçüsü iki biçimi ayırmıyor; ayıran ölçüler sahiplik ve taşınabilirliktir.
- **Yalıtımın delindiği yer:** ad alanı kullanıcı kimliğinin sayı uzayını ayırmaz. Konteyner
  içindeki kimlik ana makinedeki dosya sahibiyle uyuşmadığında sekiz düğümün altısı yazılamıyor;
  kimlikler eşitlendiğinde sayı sıfıra iniyor.
- İki yanlış çözümün de bedeli aynı kalemdedir: izinleri gevşetmek (777, 666) düğümü ana makinedeki
  herkese açar, kimlikleri eşitlemek konteynere ana makine kullanıcısının yetkisini verir.
- Yönetilen birim çakışmayı kaldırmaz, başlangıç durumunu kontrol eder: birim yaratıldığı anda
  sahipliği ayarlanabilir, bağlama takmasında böyle bir an yoktur.
- Taşınabilirlikte iki biçim ayrışıyor: iki takmanın biri mutlak yola bağlı ve makine değişince o
  biri kırıldı; adla anılan birim çözüldü.

#### Sonraki Adım

Kalıcı veri yerine oturdu: iş durumu ve düzeltmeler artık konteynerden uzun yaşıyor ve bunun bedeli
ana makinenin dosya sistemine açılan iki dizinle ödendi. Ölçülen her şey burada tek bir konteynerin
kendi sınırları içindeydi — kaynağı, dosya sistemi, kimliği. Oysa ölçüm ağı tek bir program değil:
okuma toplayıcı doğrulayıcıya, doğrulayıcı faturalamaya, faturalama iş emri servisine veri veriyor
ve dördü ayrı konteynerde koşuyor. Dosya sistemi yalıtıldığında bu dördü birbirini nasıl buluyor,
hangi adla çağırıyor ve biri ötekine ulaşamasın istendiğinde ne yapılıyor? Sonraki ders bir ad
çözümleme ve erişilebilirlik matrisi kurar ve şunu sayar: dört servis iki ağa bölündüğünde kaç yol
kapanıyor ve dışarıya açılan yüzey kaç uçtan kaç uca iniyor.

### Konteyner Ağı

Source: https://academia.sh/tr/kurslar/konteynerler/konteyner-agi

Konteynerlerin birbirini bulması ve bulamaması: servis adının hangi sınıra bağlı çözüldüğü, dört servisin iki ağa bölünmesiyle kapanan yol sayısı, yayımlanan kapı ile yalnız ağ içi erişimin ayrılması, dışarı açılan yüzeyin sekiz uçtan bir uca inmesi ve ağ üyeliğinin bir yetkilendirme olmaması.

Buraya kadar ölçülen her şey tek bir konteynerin kendi sınırları içindeydi: kaynağı, ortam ayarı,
yazılabilir katmanı, taktığı dizinler. Oysa ölçüm ağı tek bir program değil. Okuma toplayıcı
doğrulayıcıya veri veriyor, doğrulayıcı faturalamayı besliyor, faturalama ile iş emri servisi aynı
veriye bakıyor ve dördü ayrı konteynerde koşuyor. Dosya sistemi yalıtıldığında bu dördü birbirini
nasıl buluyor.

Sorunun ilginç yanı şudur: yalıtım burada iki yönlü çalışır. Bir konteynerin ötekine
**ulaşabilmesi** gerekir, ama üçüncüsüne ulaşamaması da gerekir. Bu ders erişilebilirliği bir matris
olarak kurup iki sayıyı ayrı ayrı ölçer: ağ ayrımı kaç yolu kapatıyor ve dışarıya açılan yüzey kaç
uçtan kaç uca iniyor.

#### Adres Yerine Ad

Bir servisin ötekine ulaşması için adresini bilmesi gerekir. Adres bir makine adı ve bir kapı
numarasıdır; makineye bağlıdır, taşınmaz ve 01. derste sayılan **ortam farkının** bir parçasıdır —
o dersteki 14 değişkenden dördü (`SAYAC_AG_UCU`, `OKUMA_KUYRUGU`, `VERI_UCU`, `SAHA_UCU`) tam olarak
bunu taşıyordu.

Konteyner ağının çıkardığı fark budur. Ağa katılan her konteyner bir **servis adıyla** anılır ve o
ad, aynı ağdaki başka bir konteynerin içinden çözülebilir. Ayarda artık bir makine adı durmaz,
servisin adı durur; ad her makinede aynıdır. Fark ortamdan çıkmıştır — ama tümüyle değil, çünkü
adın **çözülüp çözülmeyeceği** ağ üyeliğine bağlıdır ve üyelik ortamda tanımlanır.

- **CZ19** — Dört servis var, her biri iki kapı dinliyor: bir arayüz kapısı ve bir ölçüm kapısı.
- **CZ20** — Erişim kuralı tektir: iki uç ancak ortak bir ağa üye olduğunda birbirine ulaşır.
- **CZ21** — Ad çözümü erişimle aynı sınıra bağlıdır; servis adı yalnız ortak ağda çözülür.
- **CZ22** — Bölünmüş düzende ön ağ toplayıcı ile doğrulayıcıyı, arka ağ doğrulayıcı, faturalama ve
  iş emri servisini içeriyor. Doğrulayıcı ikisine birden üye.
- **CZ23** — Yayımlanan kapı ana makinenin ağ arayüzünde bir uç açıyor; yayımlanmayan kapıya yalnız
  ağ içinden erişiliyor.
- **CZ24** — Ağ sürücüsü sınıfları birer üyelik düzeni olarak modelleniyor: makineyle ortak düzende
  bütün servisler tek bir kümededir ve kapıları doğrudan ana makinenin arayüzündedir; ağsız düzende
  her servis yalnız kendi kümesindedir.

```js
// calisma/ag-matrisi.mjs — ad cozumleme, erisilebilirlik matrisi ve disari acilan yuzey.
// MODELDIR: servisler, aglar ve kapilar kurgu; erisim kurali "ortak ag" olarak modelde tanimli.
const SERVIS = {                                          // CZ19: dort servis, iki kapi (arayuz + olcum)
  toplayici: { kapi: [8081, 9101], ag: ["on"] },
  dogrulayici: { kapi: [8082, 9102], ag: ["on", "arka"] },
  faturalama: { kapi: [8083, 9103], ag: ["arka"] },
  isemri: { kapi: [8084, 9104], ag: ["arka"] },
};
const AD = Object.keys(SERVIS), TUM = AD.flatMap((a) => SERVIS[a].kapi), VARSAYILAN = 8080;
const DUZEN = {                                           // CZ22: uyelik kumesi duzene gore degisiyor
  "tek ag": () => ["ortak"],
  "iki ag": (a) => SERVIS[a].ag,
  "makineyle ortak": () => ["makine"],
  "agsiz": (a) => [a],
};
const DISARI = { "tek ag": 1, "iki ag": 1, "makineyle ortak": TUM.length, "agsiz": 0 };  // CZ23
const ortak = (a, b, d) => DUZEN[d](a).some((x) => DUZEN[d](b).includes(x));             // CZ20

function say(d) {                                         // yonlu ciftler uzerinde sayim
  let acik = 0, kapali = 0;
  for (const a of AD) for (const b of AD) {
    if (a === b) continue;
    if (ortak(a, b, d)) acik += 1; else kapali += 1;
  }
  return { acik, kapali, ad: acik, uc: acik * 2 };         // CZ21: ad cozumu erisimle ayni sinira bagli
}

console.log(`model: ${AD.length} servis, servis basina ${SERVIS.toplayici.kapi.length} kapi, toplam ` +
  `${TUM.length} uc; iki ag duzeninde on = toplayici + dogrulayici, arka = dogrulayici + faturalama + isemri`);
console.log(`\n${"kaynak \\ hedef".padEnd(15)}${AD.map((x) => x.padStart(13)).join("")}${"cozulen ad".padStart(12)}`);
for (const a of AD) {
  const satir = AD.map((b) => (a === b ? "-" : ortak(a, b, "iki ag") ? "cozulur" : "kapali").padStart(13)).join("");
  console.log(`${a.padEnd(15)}${satir}` +
    `${String(AD.filter((b) => b !== a && ortak(a, b, "iki ag")).length).padStart(12)}`);
}

console.log(`\n${"duzen".padEnd(18)}${"acik yol".padStart(10)}${"kapali yol".padStart(12)}` +
  `${"cozulen ad".padStart(12)}${"ag ici uc".padStart(11)}${"disaridan uc".padStart(14)}`);
const r = {};
for (const d of Object.keys(DUZEN)) {
  const k = say(d); r[d] = k;
  console.log(`${d.padEnd(18)}${String(k.acik).padStart(10)}${String(k.kapali).padStart(12)}` +
    `${String(k.ad).padStart(12)}${String(k.uc).padStart(11)}${String(DISARI[d]).padStart(14)}`);
}
const t = r["tek ag"], i = r["iki ag"];
console.log(`ag ayrimi ${t.acik - i.acik} yolu kapatiyor (%${((100 * (t.acik - i.acik)) / t.acik).toFixed(1)}); ` +
  `kapanan ciftler: ${AD.flatMap((a) => AD.filter((b) => a !== b && !ortak(a, b, "iki ag")).map((b) => `${a}->${b}`)).join(", ")}`);
console.log(`dort servis de cerceve varsayilani ${VARSAYILAN} dinleseydi: makineyle ortak duzende ` +
  `${AD.length - new Set(AD.map(() => VARSAYILAN)).size} carpisma, ayri ad alanli duzende 0 carpisma`);

const YAYIM = { "hepsi yayimli": TUM, "yalniz saha arayuzu": [8084], "hicbiri": [] };
console.log(`\n${"yayim secenegi".padEnd(22)}${"disaridan uc".padStart(14)}${"ag icinden uc".padStart(15)}` +
  `${"disa acik oran".padStart(16)}`);
for (const [etiket, y] of Object.entries(YAYIM))
  console.log(`${etiket.padEnd(22)}${String(y.length).padStart(14)}${String(i.uc).padStart(15)}` +
    `${`${y.length}/${TUM.length}`.padStart(16)}`);
console.log(`disari acilan yuzey ${TUM.length} uctan ${YAYIM["yalniz saha arayuzu"].length} uca iniyor; ` +
  `ag icinden erisilebilir uc sayisi ucunde de ${i.uc}, hicbiri kimlik sormuyor`);
```

```
model: 4 servis, servis basina 2 kapi, toplam 8 uc; iki ag duzeninde on = toplayici + dogrulayici, arka = dogrulayici + faturalama + isemri

kaynak \ hedef     toplayici  dogrulayici   faturalama       isemri  cozulen ad
toplayici                  -      cozulur       kapali       kapali           1
dogrulayici          cozulur            -      cozulur      cozulur           3
faturalama            kapali      cozulur            -      cozulur           2
isemri                kapali      cozulur      cozulur            -           2

duzen               acik yol  kapali yol  cozulen ad  ag ici uc  disaridan uc
tek ag                    12           0          12         24             1
iki ag                     8           4           8         16             1
makineyle ortak           12           0          12         24             8
agsiz                      0          12           0          0             0
ag ayrimi 4 yolu kapatiyor (%33.3); kapanan ciftler: toplayici->faturalama, toplayici->isemri, faturalama->toplayici, isemri->toplayici
dort servis de cerceve varsayilani 8080 dinleseydi: makineyle ortak duzende 3 carpisma, ayri ad alanli duzende 0 carpisma

yayim secenegi          disaridan uc  ag icinden uc  disa acik oran
hepsi yayimli                      8             16             8/8
yalniz saha arayuzu                1             16             1/8
hicbiri                            0             16             0/8
disari acilan yuzey 8 uctan 1 uca iniyor; ag icinden erisilebilir uc sayisi ucunde de 16, hicbiri kimlik sormuyor
```

Bu sayılar **ölçüm** sınıfındadır.

#### Matrisin Okunması

Birinci tablo, bölünmüş düzende her servisin neyi görebildiğini veriyor. En sağdaki sütun her
servisin kaç adı çözebildiğini sayıyor ve dört servis dört ayrı sayı veriyor: toplayıcı 1,
doğrulayıcı 3, faturalama 2, iş emri 2.

Toplayıcının satırı en dar olanıdır ve tasarımın kendisidir. Toplayıcı sahadaki sayaçlardan gelen
okumaları alır; ölçüm ağının en dışa dönük parçasıdır ve bu yüzden en az şeyi görmelidir. Matriste
faturalama ve iş emri sütunları `kapali` görünüyor — toplayıcının içinden bu iki adın çözülmesi bile
mümkün değil. Ad çözülmediği için bağlantı denemesi bir adres bulamadan biter; yani engel bir
reddetme değil, bir **bilmeme** durumudur.

Doğrulayıcının satırı en geniş olanıdır ve o da tasarımdır: iki ağın kesişiminde durur, ön ağdan
okuma alır, arka ağa doğrulanmış veri verir. Üç adı da çözer.

Bu ayrım ad çözümlemesinin nasıl çalıştığını da söylüyor. Ad, uygulamanın içinde sabit bir dizgedir
ve her makinede aynıdır; onu bir adrese çeviren şey konteynerin bağlı olduğu ağdır. Aynı kod, aynı
imaj ve aynı ad, iki ayrı ağ üyeliğiyle iki ayrı sonuç verir — birinde çözülür, ötekinde çözülmez.
Ayarda değişen tek şey üyelik satırıdır ve matrisin bütün hücreleri o satırdan türüyor.

Faturalama ile iş emri satırları birbirinin aynısıdır. İkisi de arka ağdadır, birbirini ve
doğrulayıcıyı görür, toplayıcıyı görmez. Bu simetri, ağ üyeliğinin bir **küme** olduğunu gösteriyor:
aynı kümedeki iki uç arasında yön ayrımı yoktur, ikisi de birbirini görür.

#### Ağ Ayrımı Dört Yolu Kapatıyor

İkinci tablonun ilk iki satırı ayrımın kazancını veriyor. Tek ağda dört servis arasında 12 yönlü yol
vardır ve hepsi açıktır; 12 adın hepsi çözülür, erişilebilir uç sayısı 24'tür (12 yol çarpı servis
başına 2 kapı). İki ağa bölündüğünde açık yol 8'e, çözülen ad 8'e, erişilebilir uç 16'ya iniyor.
**Ağ ayrımı 12 yolun 4'ünü, yani üçte birini kapatıyor.**

Kapanan dört yol da simetrik iki çifttir: toplayıcı ile faturalama, toplayıcı ile iş emri. Bunlar
zaten hiç konuşmayan çiftlerdir; ayrımın yaptığı şey konuşmayanların **konuşamamasını** sağlamaktır.
Bu ayrım bedava değildir ve bedeli yazımdadır: tek ağ düzeninde dört üyelik satırı yazılır, iki ağ
düzeninde beş — doğrulayıcı iki kez sayılır. Bir ağ tanımı ve bir üyelik satırı karşılığında dört
yol kapanıyor.

Bedelin ikinci kalemi tasarım tarafındadır. Ayrımı yapabilmek için ağların hangi servisleri
kapsayacağının önceden bilinmesi gerekir ve bu bilgi uygulamanın içinde değil, koşum ayarındadır.
Yeni bir servis eklendiğinde hangi ağa gireceği ayrı bir karardır; unutulursa servis kalkar,
çalışır ve hiçbir hata vermeden hiç kimseyi bulamaz.

#### Üç Sürücü Sınıfı, Üç Ayrı Satır

Konteynerin ağa nasıl bağlandığı bir **sürücü** seçimidir ve tablonun son iki satırı bu seçimin
uçlarını gösteriyor. Sürücüler burada tür adıyla anılıyor; her biri bir üyelik düzenine karşılık
geliyor.

**Ayrı ad alanlı sürücü** — ilk iki satır. Her konteynerin kendi ağ ad alanı ve kendi adresi vardır;
servis adları çözülür ve dışarıya yalnız yayımlanan uçlar açılır: 1.

**Makineyle ortak sürücü** — üçüncü satır. Ayrı bir ağ ad alanı verilmez; konteynerler doğrudan
makinenin adres uzayında dinler. Sonuçları tabloda üç sütunda birden görünüyor: açık yol 12'ye geri
çıkıyor, ağ içi uç 24'e dönüyor ve dışarıdan erişilebilir uç 1'den **8'e** fırlıyor. Yayım diye bir
karar da kalmıyor, çünkü yayımlanacak bir sınır yoktur — sekiz kapının sekizi zaten makinenin
arayüzündedir.

Bu sürücünün ikinci bedeli son satırda hesaplanıyor. Ayrı ad alanı olmadığında kapı numaraları
paylaşılan tek bir uzaydan gelir. Dört servis de çerçevesinin varsayılanı olan 8080'i dinleseydi
makineyle ortak düzende **3 çarpışma** olurdu: biri kapıyı alır, üçü kalkamaz. Ayrı ad alanlı
düzende çarpışma sayısı 0'dır, çünkü her konteynerin 8080'i kendi uzayındadır. Ad alanı yalıtımının
en somut kazancı budur — servislerin birbirinin kapı numarasını bilmek zorunda olmaması.

**Ağsız sürücü** — dördüncü satır. Hiçbir üyelik yoktur: 0 açık yol, 0 çözülen ad, 0 uç, dışarıdan 0.
Bütün ölçümlerin sıfır olduğu tek satır budur ve kullanışsız değildir. Gecelik toplu iş gibi yalnız
takılı bir dizinden okuyup yazan, kimseyi çağırmayan bir konteyner için yalıtım bütçesinin en ucuz
noktasıdır: ağ yüzeyi tümüyle kapalıdır ve kapatmanın bedeli sıfırdır, çünkü hiç kullanılmıyordu.

#### Yayımlanan Kapı ile Ağ İçi Erişim

Üçüncü tablo bambaşka bir sınırı ölçüyor. İlk iki tablo konteynerlerin **birbirine** erişimini
sayıyordu; bu tablo ana makinenin ağ arayüzünden, yani konteyner ağının tümüyle dışından gelen
erişimi sayıyor.

Dört servis toplam 8 kapı dinliyor. Hepsi yayımlanırsa ana makinenin arayüzünde 8 uç açılır; yalnız
saha ekiplerinin kullandığı arayüz kapısı yayımlanırsa 1 uç açılır; hiçbiri yayımlanmazsa 0.
**Dışarı açılan yüzey 8 uçtan 1 uca iniyor** ve bu iniş sırasında ağ içi erişilebilir uç sayısı
sabit kalıyor: üç seçeneğin üçünde de 16.

Tablonun asıl söylediği budur. Yayım kararı ile ağ içi erişim **birbirinden bağımsızdır**. Bir servis
yayımlanmadan da kendi ağındaki herkes tarafından tam olarak eskisi kadar erişilebilir olmayı
sürdürür; yayım yalnız ana makinenin arayüzüne bir uç ekler. Ölçüm kapıları buna iyi bir örnektir:
dört ölçüm kapısı hiçbir seçenekte yayımlanmıyor ama ağ içinden hepsi erişilebilir.

Ayrım pratikte şuraya oturur: ağ içi erişim servislerin birbiriyle konuşması için, yayım ise
ağın dışındaki bir kullanıcı ya da aracın konuşması içindir. İkisini aynı kararla yönetmek, sekiz
ucun sekizini de açık bırakmakla biter.

Yüzeyin 8'den 1'e inmesi bir güvenlik kazancıdır ve karşılığı işletme tarafında ödenir. Yayımlanmayan
bir kapıya dışarıdan bakılamaz; bir sorunun nedenini anlamak için o kapıya soru sormak gerekiyorsa,
soruyu soran şeyin de ağın içinde olması gerekir.

#### Yalıtımın Delindiği Yer

Üç delik vardır ve üçü de sayıyla görünür.

**Birincisi:** ağ üyeliği bir yetkilendirme değildir. Bölünmüş düzende ağ içinden erişilebilir 16 uç
var ve hiçbiri kimlik sormuyor. Aynı ağdaki bir konteyner, komşusunun yalnız arayüz kapısına değil,
ölçüm kapısına da ulaşır. Ağ ayrımı **kimin** ulaşabileceğini kabaca belirler, ulaşan uç ne
isteyebileceğini hiç sınırlamaz.

**İkincisi:** kapanan dört yol yalnız **doğrudan** yollardır. Doğrulayıcı iki ağın da üyesidir; ön
ağdan gelen bir istek doğrulayıcıya ulaşır ve doğrulayıcı arka ağdaki her şeye ulaşır. Yani
toplayıcı ile faturalama arasında kapatılan yol, iki adımda hâlâ kat edilebilir. Ağ ayrımının
kapattığı sayı 4'tür; kesişimde duran servis üzerinden dolaşılabilen sayı da 4'tür.

**Üçüncüsü:** ağ ad alanı ayrıdır ama ana makinenin ağ yığını paylaşılır. Yayımlanan her kapı ana
makinenin arayüzünde gerçek bir uç açar ve o uç artık konteyner ağının değil, makinenin
kuralları altındadır. Konteynerin içindeki 8084 ile makinenin arayüzündeki uç aynı şey değildir;
ikincisi yalıtımın dışındadır.

#### Özet

- Konteyner ağı adresi ortamdan çıkarır: ayarda makine adı yerine servis adı durur ve ad her
  makinede aynıdır. 01. dersteki 14 değişkenden dördü tam olarak bu farkı taşıyordu.
- Ad çözümü erişimle aynı sınıra bağlıdır: bölünmüş düzende toplayıcı 1, doğrulayıcı 3, faturalama
  2, iş emri 2 ad çözüyor. Engel bir reddetme değil, adın çözülmemesidir.
- **Ağ ayrımının kazancı:** tek ağda 12 yönlü yol, 12 çözülen ad ve 24 erişilebilir uç varken iki
  ağda bunlar 8, 8 ve 16'ya iniyor; 4 yol, yani üçte biri kapanıyor. Bedeli bir ağ tanımı ile bir
  fazladan üyelik satırıdır — dört yerine beş.
- Üç sürücü sınıfı üç ayrı satırdır: ayrı ad alanlı düzende dışarıdan 1 uç ve 0 kapı çarpışması;
  makineyle ortak düzende dışarıdan 8 uç, 12 açık yol ve dört servis de 8080 dinleseydi 3 çarpışma;
  ağsız düzende her ölçüm sıfır.
- **Yayımlanan kapı ile ağ içi erişim bağımsızdır:** dışarı açılan yüzey 8 uçtan 1 uca inerken ağ
  içinden erişilebilir uç sayısı üç seçenekte de 16'da kalıyor.
- **Yalıtımın delindiği yer:** ağ üyeliği yetkilendirme değildir — 16 ucun hiçbiri kimlik sormaz;
  kapanan 4 doğrudan yol, iki ağa birden üye olan doğrulayıcı üzerinden iki adımda kat edilebilir;
  ve yayımlanan kapı ana makinenin arayüzünde yalıtımın dışında bir uç açar.

#### Sonraki Adım

Ağ matrisi bir soruyu yanıtladı, bir başkasını açıkta bıraktı. Matris **hangi konteynerin** hangi uca
ulaşabildiğini söylüyor; ulaşan isteğin arkasında **kimin** durduğunu söylemiyor. Aynı boşluk önceki
derste de görülmüştü: takılan dizinde yazma hakkını belirleyen şey konteynerin adı değil, içinde
çalışan kullanıcının kimlik numarasıydı ve sekiz düğümün altısı o yüzden kapalıydı. İki ölçüm aynı
yere bakıyor. Konteynerin içinde çalışan süreç hangi kullanıcıdır, o kullanıcı ana makinede neye
karşılık gelir ve varsayılan bırakıldığında hangi dosyalara erişir? Sonraki ders bu kimliği ölçer:
ayrıcalıklı kimlikle kaç dosya açık kalıyor, kimlik değiştirildiğinde hangi işlemler düşüyor ve en
az yetkiye geçmenin bedeli kaç girdide sahiplik düzeltmesi ister. Kimliğin taşıdığı **yetenekler**
ise konunun sonuna bırakılır; orada ayrı bir sayı kümesi vardır.

### Kullanıcılar ve İzinler

Source: https://academia.sh/tr/kurslar/konteynerler/kullanicilar-ve-izinler

Köksüz çalışmanın üç ölçüsü: ayrıcalıklı kimliğe on beş girdinin kaçının açık kaldığı, köksüz kimliğe geçişin kaç girdide sahiplik düzeltmesi istediği ve düzeltmeden sonra bağlama takmasında kaç yolun ortamdan gelen sahiplikle kapalı kaldığı.

Önceki ders ağ ayrımını kurdu: hangi adın hangi adrese çözüldüğü, hangi bağlantı noktasının dışarı
açıldığı ve açılmayanın erişilemez kaldığı sayıldı. O ölçümlerin hepsi sürecin **ne yaptığını**
sayıyordu. İçeride **kimin** çalıştığı hiç sorulmadı ve sorulmadığı için varsayılan yürürlükte
kaldı: bir imaj derlenirken içine yazılan her dosyanın sahibi derlemeyi yürüten ayrıcalıklı
kimliktir, süreç de aynı kimlikle başlar. Bölgesel ölçüm ağının okuma toplayıcı servisi — sayaç
verisini toplayan, doğrulayan ve faturalamaya devreden kurgu yazılım — bu dersin ölçüm nesnesidir.

Uygulama düzeyindeki yetki soruları bu dersin dışındadır. Gizli değerin nerede durduğu, kaç yerde
tekrarlandığı ve döndürülürken ne kırdığı Gizli Değer Yönetimi dersinde ölçülmüştü. Buradaki soru
bir sınır aşağısındadır: o değerin **dosyasına** hangi kimliğin ulaşabildiği ve bu ulaşmanın
imaj tanımından mı yoksa ortamdan mı geldiği.

**CZ25 — servisin imajı on beş girdiden oluşur** (dosya ve dizin) ve servis çalışırken bunların
üzerinde on iki işlem yapar: okuma, yazma, çalıştırma. **CZ26 — derleme sırasında yazılan her girdi
ayrıcalıklı kimliğin (`0:0`) sahipliğine geçer.** **CZ27 — köksüz düzende süreç kimliği
`10001:10001`'dir.** **CZ28 — bağlanan konak dizini içeride `501:20` sahipliğiyle görünür**; bu sahiplik imajdan
değil, takmanın geldiği yerden okunur. **CZ29 — ayrıcalıklı kimlik okuma ve yazmada isteğe bağlı
erişim denetimini atlar**; çalıştırma için yine de kipte bir `x` biti arar. **CZ30 — kullanıcı
ad alanı eşlemesi yoksa konteyner içindeki sayısal kimlik konaktaki aynı sayıdır.**

#### İzin Matrisi

Aşağıdaki koşum dosyaları gerçekten diske yazar, kip bitlerini `chmod` ile kurar ve `statSync` ile
geri okur; tablodaki kip sütunu diskten gelir. Sahiplik ise bir **modeldir**: sahiplik değiştirmek
ayrıcalık istediğinden sahip ve grup numaraları tabloda tutulur, erişim kararı bu numaralarla
gerçek kip bitleri üzerinden hesaplanır. Bir yola erişimin bütün ata dizinlerinde çalıştırma biti
istemesi de hesaba katılır.

```js
// agac.mjs — kurgu okuma toplayici servisinin dosya agaci.
// Dosyalar ve kip bitleri GERCEK: diske yazilir, chmod ile kurulur, statSync ile geri okunur.
// Sahiplik bir MODELDIR: chown ayricalik ister, bu yuzden sahip/grup tabloda tutulur.
import { mkdirSync, writeFileSync, chmodSync, statSync, rmSync } from 'node:fs';

export const AYRICALIKLI = { ad: 'ayricalikli', uid: 0, gid: 0 };      // CZ26
export const KOKSUZ = { ad: 'koksuz', uid: 10001, gid: 10001 };        // CZ27
export const KONAK = { uid: 501, gid: 20 };                            // CZ28

// [yol, tur, kip, sahip, grup] — bagli/ altindakiler konak sahipligiyle gorunur.
export const IMAJ = [
  ['uygulama', 'd', 0o755, 0, 0],
  ['uygulama/baslat.mjs', 'f', 0o755, 0, 0],
  ['uygulama/toplayici.mjs', 'f', 0o644, 0, 0],
  ['uygulama/dogrulayici.mjs', 'f', 0o644, 0, 0],
  ['ayar', 'd', 0o755, 0, 0],
  ['ayar/servis.json', 'f', 0o644, 0, 0],
  ['ayar/imza-anahtari', 'f', 0o600, 0, 0],
  ['durum', 'd', 0o755, 0, 0],
  ['durum/kuyruk.veri', 'f', 0o644, 0, 0],
  ['durum/kilit', 'f', 0o644, 0, 0],
  ['gecici', 'd', 0o777, 0, 0],
  ['bagli', 'd', 0o755, KONAK.uid, KONAK.gid],
  ['bagli/olcum-01.csv', 'f', 0o644, KONAK.uid, KONAK.gid],
  ['bagli/olcum-02.csv', 'f', 0o640, KONAK.uid, KONAK.gid],
  ['bagli/cikti', 'd', 0o755, KONAK.uid, KONAK.gid],
];

export function kur(kok, dugumler) {
  rmSync(kok, { recursive: true, force: true });
  mkdirSync(kok, { recursive: true });
  for (const [yol, tur, kip] of dugumler) {
    const tam = `${kok}/${yol}`;
    if (tur === 'd') mkdirSync(tam); else writeFileSync(tam, `# ${yol}\n`);
    chmodSync(tam, kip);
  }
  return dugumler.map(([yol, tur, , uid, gid]) => ({
    yol, tur, uid, gid, kip: statSync(`${kok}/${yol}`).mode & 0o777,
  }));
}

// Istege bagli erisim denetimi: sahip / grup / diger uclusunden biri secilir.
// CZ29: ayricalikli kimlik ucluyu atlar; calistirma icin yine de bir x biti arar.
export function izin(dugum, kimlik) {
  const { kip } = dugum;
  if (kimlik.uid === 0) return { r: true, w: true, x: (kip & 0o111) !== 0 };
  const u = kimlik.uid === dugum.uid ? kip >> 6 : kimlik.gid === dugum.gid ? kip >> 3 : kip;
  return { r: (u & 4) !== 0, w: (u & 2) !== 0, x: (u & 1) !== 0 };
}

// Bir yola erisim, butun ata dizinlerinde x bitini de ister.
export function erisim(agac, yol, kimlik) {
  const p = yol.split('/');
  for (let i = 1; i < p.length; i += 1) {
    const ata = agac.find((d) => d.yol === p.slice(0, i).join('/'));
    if (ata && !izin(ata, kimlik).x) return { r: false, w: false, x: false };
  }
  return izin(agac.find((d) => d.yol === yol), kimlik);
}

export const bicim = (e) => `${e.r ? 'r' : '-'}${e.w ? 'w' : '-'}${e.x ? 'x' : '-'}`;

if (import.meta.url === `file://${process.argv[1]}`) {
  const agac = kur('./kok', IMAJ);
  const s = (x, n) => String(x).padStart(n);
  console.log(`${'yol'.padEnd(24)}${s('kip', 5)}${s('sahip', 8)}${s('ayricalikli', 13)}${s('koksuz', 9)}`);
  for (const d of agac) {
    console.log(`${(d.tur === 'd' ? `${d.yol}/` : d.yol).padEnd(24)}${s(d.kip.toString(8), 5)}`
      + `${s(`${d.uid}:${d.gid}`, 8)}${s(bicim(erisim(agac, d.yol, AYRICALIKLI)), 13)}`
      + `${s(bicim(erisim(agac, d.yol, KOKSUZ)), 9)}`);
  }
  for (const k of [AYRICALIKLI, KOKSUZ]) {
    const e = agac.map((d) => erisim(agac, d.yol, k));
    console.log(`${k.ad.padEnd(13)} okunabilir ${s(e.filter((x) => x.r).length, 2)}/${agac.length}`
      + `  yazilabilir ${s(e.filter((x) => x.w).length, 2)}/${agac.length}`
      + `  calistirilabilir ${s(e.filter((x) => x.x).length, 2)}/${agac.length}`);
  }
}
```

```
yol                       kip   sahip  ayricalikli   koksuz
uygulama/                 755     0:0          rwx      r-x
uygulama/baslat.mjs       755     0:0          rwx      r-x
uygulama/toplayici.mjs    644     0:0          rw-      r--
uygulama/dogrulayici.mjs  644     0:0          rw-      r--
ayar/                     755     0:0          rwx      r-x
ayar/servis.json          644     0:0          rw-      r--
ayar/imza-anahtari        600     0:0          rw-      ---
durum/                    755     0:0          rwx      r-x
durum/kuyruk.veri         644     0:0          rw-      r--
durum/kilit               644     0:0          rw-      r--
gecici/                   777     0:0          rwx      rwx
bagli/                    755  501:20          rwx      r-x
bagli/olcum-01.csv        644  501:20          rw-      r--
bagli/olcum-02.csv        640  501:20          rw-      ---
bagli/cikti/              755  501:20          rwx      r-x
ayricalikli   okunabilir 15/15  yazilabilir 15/15  calistirilabilir  7/15
koksuz        okunabilir 13/15  yazilabilir  1/15  calistirilabilir  7/15
```

İki sütun arasındaki fark, kip bitlerinin ne kadarının karar taşıdığını gösteriyor. Ayrıcalıklı
kimlik için on beş girdinin on beşi okunabilir ve on beşi yazılabilir; `600` kipiyle korunan imza
anahtarı da, konak sahipliğiyle gelen ölçüm dosyası da açıktır. O sütunda kip bitleri **karar
taşımaz**, yalnızca kayıt tutar. Köksüz sütununda ise aynı bitler kararı verir: okunabilir on üç,
yazılabilir bir. Yazılabilir tek girdi `gecici/` dizinidir ve nedeni kipinin `777` olmasıdır.

Çalıştırma sütununun iki kimlikte de yedi çıkması ayrı bir ayrımdır. Ayrıcalıklı kimlik izin
denetimini atlar ama olmayan bir çalıştırma bitini uyduramaz; dosyanın çalıştırılabilir olması
yetkiden değil, kipten gelir. Bu dersin bütün sayıları içinde ayrıcalığın değiştirmediği tek sütun
budur.

#### Köksüz Çalışmaya Geçişin Bedeli

Matris tek başına bir karar vermez; hangi erişimin **gerektiği** bilinmeden "kapalı" bir sayı
değildir. İkinci koşum servisin gerçekten yaptığı on iki işlemi listeler, iki kimlikle koşturur ve
düşen işlemleri ayırır: imaj içinde düzeltilebilenler ile takmadan gelenler.

```js
// matris.mjs — koksuz calismaya gecisin bedeli ve konak tarafinda birakilan yuzey.
import { kur, izin, erisim, bicim, IMAJ, AYRICALIKLI, KOKSUZ } from './agac.mjs';

// Servisin gercekten yaptigi islemler: [yol, islem]
const GEREKLI = [
  ['uygulama/baslat.mjs', 'x'], ['uygulama/toplayici.mjs', 'r'],
  ['uygulama/dogrulayici.mjs', 'r'], ['ayar/servis.json', 'r'],
  ['ayar/imza-anahtari', 'r'], ['durum', 'w'], ['durum/kuyruk.veri', 'w'],
  ['durum/kilit', 'w'], ['gecici', 'w'], ['bagli/olcum-01.csv', 'r'],
  ['bagli/olcum-02.csv', 'r'], ['bagli/cikti', 'w'],
];

const dusen = (agac, kimlik) => GEREKLI.filter(([y, i]) => !erisim(agac, y, kimlik)[i]);
const yuzey = (agac, kimlik) => agac.filter((d) => izin(d, kimlik).w).length;

const agac = kur('./kok', IMAJ);
const s = (x, n) => String(x).padStart(n);
console.log(`ayricalikli: dusen islem ${dusen(agac, AYRICALIKLI).length}/${GEREKLI.length}`
  + `, yazilabilir girdi ${yuzey(agac, AYRICALIKLI)}/${agac.length}`);
console.log(`koksuz     : dusen islem ${dusen(agac, KOKSUZ).length}/${GEREKLI.length}`
  + `, yazilabilir girdi ${yuzey(agac, KOKSUZ)}/${agac.length}`);
for (const [y, i] of dusen(agac, KOKSUZ)) {
  const d = agac.find((x) => x.yol === y);
  console.log(`  ${y.padEnd(22)} istenen ${i}  kip ${d.kip.toString(8)}  sahip ${d.uid}:${d.gid}`
    + `  ${d.yol.startsWith('bagli/') ? 'baglama takmasi — imajda duzeltilemez' : 'imaj icinde'}`);
}

// Iki duzeltme bicimi: sahipligi tasimak, ya da kip bitlerini gevsetmek.
// YABANCI: ayni bitlerden yararlanan ucuncu bir kimlik (baska bir surec ya da esleme yokken konak).
const YABANCI = { uid: 20002, gid: 20002 };
function duzelt(bicimAdi) {
  const t = kur('./kok', IMAJ);
  let dokunulan = 0;
  for (const [y] of dusen(t, KOKSUZ)) {
    const d = t.find((x) => x.yol === y);
    if (y.startsWith('bagli/')) continue;
    if (bicimAdi === 'sahiplik') { d.uid = KOKSUZ.uid; d.gid = KOKSUZ.gid; }
    else d.kip |= d.tur === 'd' ? 0o077 : 0o066;
    dokunulan += 1;
  }
  return { t, dokunulan, kalan: dusen(t, KOKSUZ) };
}
console.log(`\n${'duzeltme'.padEnd(16)}${s('dokunulan', 11)}${s('kalan dusen', 13)}`
  + `${s('koksuz yazar', 14)}${s('yabanci yazar', 15)}${s('yabanci okur', 14)}`);
let kalan = [];
for (const b of ['sahiplik', 'kip gevsetme']) {
  const r = duzelt(b);
  kalan = r.kalan;
  console.log(`${b.padEnd(16)}${s(r.dokunulan, 11)}${s(r.kalan.length, 13)}`
    + `${s(`${yuzey(r.t, KOKSUZ)}/${r.t.length}`, 14)}${s(`${yuzey(r.t, YABANCI)}/${r.t.length}`, 15)}`
    + `${s(`${r.t.filter((d) => izin(d, YABANCI).r).length}/${r.t.length}`, 14)}`);
}

// Konak yuzeyi: ayni sayisal kimligin konteyner disinda dokunabildigi girdiler (CZ30).
const KONAK_AGACI = [
  ['veri', 'd', 0o755, 0, 0], ['veri/olcum-arsiv', 'f', 0o644, 10001, 10001],
  ['veri/gunluk.log', 'f', 0o664, 0, 10001], ['ayar', 'd', 0o755, 0, 0],
  ['ayar/ag.json', 'f', 0o644, 0, 0], ['ev', 'd', 0o755, 10001, 10001],
  ['ev/anahtar', 'f', 0o600, 10001, 10001], ['ev/toplayici.mjs', 'f', 0o755, 10001, 10001],
];
const konak = kur('./konak', KONAK_AGACI);
const ESLEME = 100000;  // kullanici ad alani kaydirmasi
console.log(`\n${'konak kimligi'.padEnd(16)}${s('okunabilir', 12)}${s('yazilabilir', 13)}${s('ev/anahtar', 12)}`);
for (const k of [{ ad: 'eslemesiz 10001', uid: KOKSUZ.uid, gid: KOKSUZ.gid },
  { ad: `esleme +${ESLEME}`, uid: KOKSUZ.uid + ESLEME, gid: KOKSUZ.gid + ESLEME }]) {
  const e = konak.map((d) => erisim(konak, d.yol, k));
  console.log(`${k.ad.padEnd(16)}${s(`${e.filter((x) => x.r).length}/${konak.length}`, 12)}`
    + `${s(`${e.filter((x) => x.w).length}/${konak.length}`, 13)}`
    + `${s(bicim(erisim(konak, 'ev/anahtar', k)), 12)}`);
}
console.log(`baglama takmasindaki ${kalan.length} yol konak tarafinda duzeltilmeli;`
  + ` esleme acikken hedef kimlik ${KOKSUZ.uid} degil ${KOKSUZ.uid + ESLEME} olur`);
```

```
ayricalikli: dusen islem 0/12, yazilabilir girdi 15/15
koksuz     : dusen islem 6/12, yazilabilir girdi 1/15
  ayar/imza-anahtari     istenen r  kip 600  sahip 0:0  imaj icinde
  durum                  istenen w  kip 755  sahip 0:0  imaj icinde
  durum/kuyruk.veri      istenen w  kip 644  sahip 0:0  imaj icinde
  durum/kilit            istenen w  kip 644  sahip 0:0  imaj icinde
  bagli/olcum-02.csv     istenen r  kip 640  sahip 501:20  baglama takmasi — imajda duzeltilemez
  bagli/cikti            istenen w  kip 755  sahip 501:20  baglama takmasi — imajda duzeltilemez

duzeltme          dokunulan  kalan dusen  koksuz yazar  yabanci yazar  yabanci okur
sahiplik                  4            2          5/15           1/15         13/15
kip gevsetme              4            2          5/15           5/15         14/15

konak kimligi     okunabilir  yazilabilir  ev/anahtar
eslemesiz 10001          8/8          5/8         rw-
esleme +100000           7/8          0/8         ---
baglama takmasindaki 2 yol konak tarafinda duzeltilmeli; esleme acikken hedef kimlik 10001 degil 110001 olur
```

Ayrıcalıklı kimlikle on iki işlemin sıfırı düşer. Bu, ayrıcalıklı çalışmanın neden sürdüğünü tek
satırda açıklıyor: hiçbir şey kırılmaz, dolayısıyla hiçbir şey sorulmaz. Köksüz kimliğe geçildiğinde
on iki işlemin altısı düşer ve düşenlerin dağılımı asıl bilgidir — dördü imaj içindedir, ikisi
takmadan gelir.

İmaj içindeki dört düşük tek bir düzeltmeyle kapanır: o dört girdinin sahipliğinin köksüz kimliğe
geçirilmesi. Yalıtımın bu dersteki bedeli bu sayıdır — **dört girdide sahiplik düzeltmesi**, yani
imaj tanımına yazılacak dört yol. Bu yolların kod okunarak bulunamaması bedeli asıl ağırlaştıran
şeydir: hangi dosyanın yazıldığı ancak servis köksüz koşup düştüğünde ortaya çıkar ve düşen her
işlem ayrı bir kırılmadır. Düzeltmeden sonra yazılabilir girdi sayısı ondan beşe değil, **on beşten
beşe** iner: ayrıcalıklı kimliğin yazabildiği on girdi kapanır. Kapanan on girdinin içinde bir yapılandırma dosyası (`ayar/servis.json`) ve uygulama kodunun
kendisi — üç `.mjs` dosyası — vardır; ele geçirilmiş bir sürecin kendi kodunu değiştirip kalıcı
olması bu on girdinin kapanmasıyla ortadan kalkar.

Aynı dört girdi iki ayrı yolla açılabilir ve tablo ikisini yan yana koyuyor. Sahipliği taşımak da
kip bitlerini gevşetmek de dört girdiye dokunur, ikisi de kalan düşük sayısını ikiye indirir ve
ikisinde de köksüz kimliğin yazabildiği girdi beştir. Servis her iki düzeltmeyle de aynı biçimde
ayağa kalkar. Fark tablonun son iki sütunundadır: sahiplik taşındığında üçüncü bir kimlik on beş
girdinin yalnız birine yazabilir, kip gevşetildiğinde beşine. Okuma sütunu farkı daha keskin
söylüyor — gevşetme imza anahtarının `600` kipini `666` yapar ve yabancı kimliğin okuyabildiği
girdi on üçten on dörde çıkar. İki düzeltme aynı hatayı susturur, imaj tanımına aynı sayıda satır
yazdırır ve **farklı bir yüzey bırakır**; hangisinin seçildiği ne hata iletisinden ne de servisin
davranışından okunur.

Kalan iki işlem imaj tanımına yazılarak kapanmaz. `bagli/olcum-02.csv` ve `bagli/cikti` takmadan
gelir; sahiplikleri imajın değil, takmanın geldiği yerin kaydındadır. **Yalıtımın delindiği birinci
yer budur:** on beş girdinin on birinin sahipliği çıktının içine alınabilir, dördününki çalışma
anında dışarıdan gelir ve o dördü için doğru sayı ancak iki tarafın anlaşmasıyla belirlenir.

#### Sayısal Kimliğin Paylaşılması

Çıktının alt yarısı ikinci deliği ölçüyor. Köksüz kimlik `10001` bir addan değil bir sayıdan
ibarettir ve o sayı çekirdeğin tek bir tablosundan okunur. Konak tarafında sekiz girdilik kurgu bir
kümede aynı sayı beş girdiyi yazabilir; `ev/anahtar` dosyası `600` kipindedir, yani içeriden
erişilemeyen imza anahtarının konak eşdeğeri o kimliğe `rw-` açıktır. Köksüz çalışmak "yetkisiz
çalışmak" değildir; **başka bir sayının yetkisiyle çalışmaktır** ve o sayının konakta ne anlama
geldiği imajda yazmaz.

Kullanıcı ad alanı eşlemesi bu deliği kapatır: yüz bin kaydırmayla içerideki `10001` konakta
`110001` olur, hiçbir konak girdisinin sahibi ya da grubu değildir, yazılabilir sayısı beşten
**sıfıra** iner ve `ev/anahtar` okunamaz hâle gelir. Bedeli son satırdadır. Takmadaki iki yol için
konak tarafında yapılacak sahiplik düzeltmesinin hedefi artık `10001` değil `110001`'dir; içeriden
yazılan her yeni dosya da konakta o kimlikle görünür. Eşleme, dışarı taşan yetkiyi kapatırken
takmanın iki ucu arasındaki kimlik uyuşmazlığını büyütür. Yalıtım burada da tam olmaz, yalnızca
bedeli başka bir kaleme yazılır.

#### Özet

- İzin matrisi ayrıcalıklı kimlik için karar taşımaz: on beş girdinin on beşi okunabilir, on beşi
  yazılabilir; `600` kipli imza anahtarı da açıktır. Kipin değiştirmediği tek sütun çalıştırmadır —
  iki kimlikte de yedi.
- Köksüz kimlikle on iki işlemin altısı düşer; dördü imaj içinde, ikisi bağlama takmasındadır.
- Geçişin bedeli dört girdide sahiplik düzeltmesidir ve bu dört yol koddan okunarak değil, servis
  düşerek bulunur; karşılığında yazılabilir girdi sayısı on beşten beşe iner. Aynı dördü kip
  gevşeterek açmak da servisi yürütür, ama yabancı bir kimliğe yazılabilir girdiyi birden beşe,
  okunabiliri on üçten on dörde çıkarır.
- On beş girdinin on birinin sahipliği çıktının içine alınabilir; dördü çalışma anında ortamdan
  gelir ve imaj tanımıyla düzeltilemez.
- Köksüz kimlik konakla aynı sayıdır: kurgu konak kümesinde sekiz girdinin beşini yazabilir ve
  `600` kipli konak anahtarını okuyabilir.
- Kullanıcı ad alanı eşlemesi konakta yazılabilir sayısını beşten sıfıra indirir; bedeli takmanın
  iki ucundaki kimlik uyuşmazlığının derinleşmesi, düzeltme hedefinin `110001`'e kaymasıdır.

#### Sonraki Adım

Buraya kadarki bütün ölçümler tek bir konteyner üzerindeydi: bir dosya ağacı, bir kimlik, bir izin
matrisi. Oysa bölgesel ölçüm ağı tek süreç değildir — okuma toplayıcının yazdığı kuyruğu bir
doğrulayıcı okur, doğrulanan kayıt faturalamaya gider ve hepsinin altında bir kayıt deposu durur.
Bu servisleri elle başlatmak, her birinin ağını, birimini ve sırasını ayrı kararlarla kurmak
demektir; bir tanesi yeniden başladığında hangilerinin etkilendiği de o kararların içinde saklıdır.
Sonraki ders bu kurulumu bildirimsel bir bileşim dosyasına taşır ve iki biçimi sayıyla karşılaştırır:
dört servisli bir kurguda başlatma sırasının bağımlılık grafiğinden nasıl türetildiğini, hazır olma
denetimi olmadan kaç yarışın doğduğunu ve elle başlatmaya göre kaç komut ile kaç kararın ortadan
kalktığını ölçer.

### Çok Konteynerli Yerel Ortam

Source: https://academia.sh/tr/kurslar/konteynerler/cok-konteynerli-yerel-ortam

Bildirimsel bir bileşim dosyasının üç ölçüsü: başlatma sırasının bağımlılık grafiğinden türetilmesi, hazır olma denetimi olmadan doğan yarış sayısı ile denetimin ayağa kalkmaya eklediği adım ve elle kuruluma göre ortadan kalkan komut, bayrak ve diziliş kararı.

Önceki ders tek bir konteynerin içine baktı: on beş girdilik bir ağaç, iki kimlik, bir izin matrisi.
Bölgesel ölçüm ağı — sayaç okumalarını toplayan, doğrulayan ve faturaya çeviren kurgu yazılım — tek
süreç değildir. Okuma toplayıcının yazdığı kaydı bir doğrulayıcı okur, doğrulanan kayıt faturalamaya
gider ve hepsinin altında bir kayıt deposu durur. Bu dördünü yerelde ayağa kaldırmak, dört ayrı
başlatma komutu yazmak değildir yalnızca; hangi ağın önce kurulacağına, hangi birimin nereye
bağlanacağına ve hangi servisin hangisinden sonra geleceğine karar vermektir. Bu kararlar bir yere
yazılmadıkça **ortamda** kalır: kuran kişinin kabuğunda, geçmiş komutlarında ya da aklında.

Bir **bileşim dosyası** bu kararları bir tür olarak dosyaya alır. Bu derste böyle bir dosyanın küçük
bir şeması tanımlanır ve bir çözümleyici ile ölçülür; şema da çözümleyici de burada yazılmıştır,
gerçek bir çalışma zamanı koşturulmaz.

**CZ31 — yerel ortamda dört servis vardır:** kayıt deposu, okuma toplayıcı, doğrulayıcı, faturalama.
**CZ32 — bileşim dosyasının şeması altı alandır:** imaj, bağımlı, ağ, birim, kapı, hazır. **CZ33 —
bir servisin süreci bir adımda ayağa kalkar, hizmet vermeye başlaması `hazır` alanı kadar adım daha
sürer.** **CZ34 — hazır olma denetimi yokken bağımlı servis, bağımlısının süreci başlar başlamaz
bağlanmayı dener.** **CZ35 — elle kurulumda ağ ve birim ayrı komutlarla oluşturulur, her servis
kendi komutu ve kendi bayraklarıyla başlatılır.** **CZ36 — bir servisin yeniden başlatılması, ona
geçişli olarak bağımlı bütün servislerin bağlantısını düşürür.**

#### Şema ve Çözümleyici

Şemanın kendisi ölçünün bir parçasıdır: alanlar adlandırıldığı anda, dosyada yazılı olan her şey
başlatmadan önce denetlenebilir hâle gelir. Aşağıdaki çözümleyici üç şey yapar — şemaya uymayan
alanları ve çözülmeyen gönderileri toplar, bağımlılık grafiğinde döngü arar, sıralamayı dalgalar
hâlinde türetir.

```js
// bilesim.mjs — bildirimsel bir bilesim dosyasinin kucuk semasi ve cozumleyicisi (MODEL).
// Sema burada tanimlidir: alanlar, tipleri ve gonderme yaptiklari kumeler.

export const SEMA = { imaj: 'metin', bagimli: 'liste', ag: 'liste', birim: 'liste', kapi: 'liste', hazir: 'sayi' };

// CZ31: dort servisli kurgu yerel ortam. CZ32: hazir = ayaga kalktiktan sonra kac adimda hizmet verir.
export const BILESIM = {
  aglar: ['olcum-agi'],
  birimler: ['kayit-verisi'],
  servisler: {
    'kayit-deposu': { imaj: 'kayit:3', bagimli: [], ag: ['olcum-agi'],
      birim: ['kayit-verisi:/veri'], kapi: [], hazir: 3 },
    'okuma-toplayici': { imaj: 'toplayici:7', bagimli: ['kayit-deposu'], ag: ['olcum-agi'],
      birim: [], kapi: ['8081'], hazir: 1 },
    'dogrulayici': { imaj: 'dogrulama:4', bagimli: ['kayit-deposu'], ag: ['olcum-agi'],
      birim: [], kapi: [], hazir: 2 },
    'faturalama': { imaj: 'fatura:2', bagimli: ['okuma-toplayici', 'dogrulayici'], ag: ['olcum-agi'],
      birim: ['kayit-verisi:/arsiv'], kapi: ['8082'], hazir: 1 },
  },
};

export function dogrula(b) {
  const hata = [];
  const adlar = Object.keys(b.servisler);
  for (const [ad, s] of Object.entries(b.servisler)) {
    for (const alan of Object.keys(SEMA)) if (!(alan in s)) hata.push(`${ad}: eksik alan '${alan}'`);
    for (const alan of Object.keys(s)) if (!(alan in SEMA)) hata.push(`${ad}: taninmayan alan '${alan}'`);
    for (const a of s.ag ?? []) if (!b.aglar.includes(a)) hata.push(`${ad}: taninmayan ag '${a}'`);
    for (const v of s.birim ?? []) {
      if (!b.birimler.includes(v.split(':')[0])) hata.push(`${ad}: taninmayan birim '${v}'`);
    }
    for (const d of s.bagimli ?? []) {
      if (d === ad) hata.push(`${ad}: kendine bagimli`);
      else if (!adlar.includes(d)) hata.push(`${ad}: taninmayan bagimlilik '${d}'`);
    }
  }
  return hata;
}

// Dongu: derinlemesine gezinti; gri bir dugume donus donguyu verir.
export function dongu(b) {
  const renk = {}, yol = [];
  let bulunan = null;
  const git = (ad) => {
    if (renk[ad] === 'gri') { bulunan ??= [...yol.slice(yol.indexOf(ad)), ad]; return; }
    if (renk[ad] === 'siyah') return;
    renk[ad] = 'gri'; yol.push(ad);
    for (const d of b.servisler[ad].bagimli) if (b.servisler[d]) git(d);
    yol.pop(); renk[ad] = 'siyah';
  };
  for (const ad of Object.keys(b.servisler)) git(ad);
  return bulunan;
}

// Baslatma sirasi grafikten turetilir; ayni dalgadakiler arasinda bagimlilik yoktur.
export function sira(b) {
  const kalan = new Map(Object.entries(b.servisler).map(([a, s]) => [a, new Set(s.bagimli)]));
  const dalgalar = [];
  while (kalan.size) {
    const acik = [...kalan].filter(([, d]) => d.size === 0).map(([a]) => a);
    if (!acik.length) return null;                       // dongu var: sira turetilemez
    dalgalar.push(acik);
    for (const a of acik) kalan.delete(a);
    for (const d of kalan.values()) for (const a of acik) d.delete(a);
  }
  return dalgalar;
}

// Elle baslatan kisinin icinden secmek zorunda oldugu dizilis sayisi.
export function dizilis(b) {
  const adlar = Object.keys(b.servisler);
  const perm = (a) => (a.length <= 1 ? [a]
    : a.flatMap((x, i) => perm([...a.slice(0, i), ...a.slice(i + 1)]).map((r) => [x, ...r])));
  const tum = perm(adlar);
  const gecerli = tum.filter((p) => p.every((ad, i) => b.servisler[ad].bagimli.every((d) => p.indexOf(d) < i)));
  return { tum: tum.length, gecerli: gecerli.length };
}

if (import.meta.url === `file://${process.argv[1]}`) {
  console.log(`bilesim dogrulamasi: ${dogrula(BILESIM).length} hata`);

  const BOZUK = structuredClone(BILESIM);
  BOZUK.servisler['dogrulayici'].ag.push('rapor-agi');
  BOZUK.servisler['faturalama'].bagimli.push('arsivleyici');
  BOZUK.servisler['faturalama'].birim.push('gecici-veri:/tmp');
  BOZUK.servisler['okuma-toplayici'].yenidenBaslat = 'her-zaman';
  delete BOZUK.servisler['kayit-deposu'].hazir;
  const hatalar = dogrula(BOZUK);
  console.log(`bozuk bilesim: ${hatalar.length} hata, hepsi baslatmadan once`);
  for (const h of hatalar) console.log(`  ${h}`);

  const DONGULU = structuredClone(BILESIM);
  DONGULU.servisler['kayit-deposu'].bagimli = ['faturalama'];
  console.log(`\ndongu: ${dongu(DONGULU).join(' -> ')}`);
  console.log(`donguluden sira turetilebilir mi: ${sira(DONGULU) === null ? 'hayir' : 'evet'}`);

  const d = dizilis(BILESIM);
  console.log(`\nbaslatma dalgalari: ${sira(BILESIM).map((w) => `[${w.join(' ')}]`).join(' -> ')}`);
  console.log(`${Object.keys(BILESIM.servisler).length} servisin ${d.tum} dizilisinden ${d.gecerli} tanesi gecerli`);
}
```

```
bilesim dogrulamasi: 0 hata
bozuk bilesim: 5 hata, hepsi baslatmadan once
  kayit-deposu: eksik alan 'hazir'
  okuma-toplayici: taninmayan alan 'yenidenBaslat'
  dogrulayici: taninmayan ag 'rapor-agi'
  faturalama: taninmayan birim 'gecici-veri:/tmp'
  faturalama: taninmayan bagimlilik 'arsivleyici'

dongu: kayit-deposu -> faturalama -> okuma-toplayici -> kayit-deposu
donguluden sira turetilebilir mi: hayir

baslatma dalgalari: [kayit-deposu] -> [okuma-toplayici dogrulayici] -> [faturalama]
4 servisin 24 dizilisinden 2 tanesi gecerli
```

Beş hatanın ortak özelliği, hepsinin **başlatmadan önce** bulunmasıdır. Elle kurulumda bu beşi
bulmanın tek yolu beş ayrı koşumdur: tanınmayan ağ adı ilk servis başlatılırken, çözülmeyen birim
adı üçüncü komutta, eksik bağımlılık ise ancak faturalama bağlanmaya çalıştığında görünür. Bildirim
biçimi bunu bir okumaya indirir çünkü **dosya, kurulumun tamamını aynı anda görür**. Kendine bağımlı
servis denetimi de aynı yerdedir; bir servisin kendini beklemesi çalışma anında sonsuz bir bekleme,
dosyada bir satırdır.

Döngü çıktısı bu iki biçimin en keskin ayrımıdır. `kayit-deposu` faturalamaya bağımlı yapıldığında
üç kenarlık bir çevrim doğar ve sıralama türetilemez. Elle başlatan kişi bu durumu bir hata olarak
görmez; birinci servisin ayağa kalkmasını bekler, kalkmadığını görür, ikinciyi dener ve iki servis
birbirini beklerken hangisinin önce gelmesi gerektiğine dair bir yanıt bulamaz. Grafik, yanıtın
**olmadığını** üç adımda söyler.

Son satır kararların sayısını veriyor. Dört servisin yirmi dört dizilişinden yalnız ikisi bütün
bağımlılıkları sağlar. Sıra bir tercih değil, grafikten çıkan bir sonuçtur ve dalgalar bunu görünür
kılıyor: kayıt deposu yalnız başına, sonra aralarında bağımlılık olmayan iki servis, sonra
faturalama.

#### Yarışlar ve Hazır Olma Denetimi

Sıranın doğru olması yetmez. Bir servisin süreci başlamış olması, hizmet vermeye hazır olduğu
anlamına gelmez ve aradaki fark bağımlı servisin bağlanma denemesine düşer. İkinci koşum aynı
bileşimi iki kez ayağa kaldırır — hazır olma denetimi olmadan ve onunla.

```js
// kosum.mjs — hazir olma denetiminin bedeli, yeniden baslatmanin yayilimi, komut ve karar sayisi.
import { BILESIM, SEMA, sira, dizilis } from './bilesim.mjs';

// CZ33: surec 1 adimda ayaga kalkar, hizmet vermesi 'hazir' adim daha surer.
// CZ34: denetim yokken bagimli servis, bagimlisinin sureci basar basmaz baglanmayi dener.
function baslat(b, denetim) {
  const S = b.servisler;
  const bas = {}, hazirAn = {};
  for (const dalga of sira(b)) {
    for (const ad of dalga) {
      const d = S[ad].bagimli;
      bas[ad] = d.length === 0 ? 0 : Math.max(...d.map((x) => (denetim ? hazirAn[x] : bas[x] + 1)));
      hazirAn[ad] = bas[ad] + S[ad].hazir;
    }
  }
  const yaris = [];
  for (const [ad, s] of Object.entries(S)) {
    for (const d of s.bagimli) if (bas[ad] < hazirAn[d]) yaris.push([ad, d, hazirAn[d] - bas[ad]]);
  }
  return { bas, yaris, toplam: Math.max(...Object.values(hazirAn)) };
}

// Bir servisin yeniden baslatilmasindan gecisli olarak etkilenenler.
function etkilenen(b, ad) {
  const ters = {};
  for (const [a, s] of Object.entries(b.servisler)) for (const d of s.bagimli) (ters[d] ??= []).push(a);
  const kume = new Set(), yigit = [ad];
  while (yigit.length) {
    for (const x of ters[yigit.pop()] ?? []) if (!kume.has(x)) { kume.add(x); yigit.push(x); }
  }
  return [...kume];
}

const s = (x, n) => String(x).padStart(n);
const kenar = Object.values(BILESIM.servisler).reduce((a, x) => a + x.bagimli.length, 0);
console.log(`${'denetim'.padEnd(12)}${s('yaris', 7)}${s('kenar', 7)}${s('gizli bekleme', 15)}${s('ayaga kalkma', 14)}`);
for (const denetim of [false, true]) {
  const r = baslat(BILESIM, denetim);
  const bekleme = r.yaris.reduce((a, [, , g]) => a + g, 0);
  console.log(`${(denetim ? 'hazir olma' : 'yok').padEnd(12)}${s(r.yaris.length, 7)}${s(kenar, 7)}`
    + `${s(`${bekleme} adim`, 15)}${s(`${r.toplam} adim`, 14)}`);
}
for (const [ad, d, g] of baslat(BILESIM, false).yaris) {
  console.log(`  yaris: ${ad.padEnd(17)} -> ${d.padEnd(17)} ${g} adim erken baglanir`);
}

console.log(`\n${'yeniden baslayan'.padEnd(18)}${s('etkilenen', 10)}   ${'kim'}`);
for (const ad of Object.keys(BILESIM.servisler)) {
  const e = etkilenen(BILESIM, ad);
  console.log(`${ad.padEnd(18)}${s(e.length, 10)}   ${e.join(', ') || '-'}`);
}

// Elle kurulum ile bildirimsel dosyanin yazim bedeli.
const servisler = Object.values(BILESIM.servisler);
const bayrak = servisler.reduce((a, x) => a + x.ag.length + x.birim.length + x.kapi.length, 0);
const komut = BILESIM.aglar.length + BILESIM.birimler.length + servisler.length;
const alan = servisler.length * Object.keys(SEMA).length + 2;
const d = dizilis(BILESIM);
console.log(`\nelle       : ${komut} komut, ${bayrak} bayrak, ${d.tum} dizilisten ${d.tum - d.gecerli}'si`
  + ` en az bir bagimliligi bozar`);
console.log(`bildirimsel: 1 komut, ${alan} alanlik dosya, sira turetilir (0 dizilis karari)`);
console.log(`yeniden baslatmada elle: en kotu ${Math.max(...Object.keys(BILESIM.servisler)
  .map((a) => etkilenen(BILESIM, a).length))} servis daha elle baslatilir`);
```

```
denetim       yaris  kenar  gizli bekleme  ayaga kalkma
yok               3      4         5 adim        3 adim
hazir olma        0      4         0 adim        6 adim
  yaris: okuma-toplayici   -> kayit-deposu      2 adim erken baglanir
  yaris: dogrulayici       -> kayit-deposu      2 adim erken baglanir
  yaris: faturalama        -> dogrulayici       1 adim erken baglanir

yeniden baslayan   etkilenen   kim
kayit-deposu               3   okuma-toplayici, dogrulayici, faturalama
okuma-toplayici            1   faturalama
dogrulayici                1   faturalama
faturalama                 0   -

elle       : 6 komut, 8 bayrak, 24 dizilisten 22'si en az bir bagimliligi bozar
bildirimsel: 1 komut, 26 alanlik dosya, sira turetilir (0 dizilis karari)
yeniden baslatmada elle: en kotu 3 servis daha elle baslatilir
```

Denetimsiz düzende sıra doğrudur ve yine de dört bağımlılık kenarının üçü yarış doğurur. Doğru sıra
yalnızca sürecin **başlamış** olmasını güvenceye alır; kayıt deposunun hizmet vermesi üç adım sürer,
ona bağlı iki servis birinci adımda bağlanmayı dener ve ikişer adım erken gelir. Faturalama,
doğrulayıcıya bir adım erken bağlanır. Toplam beş adımlık bu erkenlik hiçbir yerde bir hata olarak
görünmez; yeniden deneme varsa gecikmeye, yoksa başlangıçta düşen bir servise dönüşür.

Denetimin bedeli aynı tablonun son sütununda: ayağa kalkma üç adımdan altı adıma çıkar, **iki kat**.
Bu, bildirimsel biçimin bu derste ödediği en açık bedeldir ve her yerel koşumda ödenir. Karşılığı
üç yarışın sıfırlanmasıdır. Sayıların ikisi de aynı dosyadan okunur, bu yüzden takas dosyada
görülebilir bir seçim hâline gelir — elle kurulumda ise aynı takas, komutlar arasına serpiştirilen
bekleme sürelerinin içinde saklıdır.

Yeniden başlatma tablosu ilk defa **tek servisin ötesini** ölçüyor. Kayıt deposu yeniden
başladığında üç servis etkilenir; okuma toplayıcı ya da doğrulayıcı yeniden başladığında birer
servis; faturalama yeniden başladığında hiçbiri. Elle kurulumda bu sayı elle uygulanır: kayıt
deposunu yeniden başlatan kişi, geri kalan üçünü de kendisi başlatmak zorundadır ve hangilerinin
gerektiği bağımlılık listesinden değil, hatalardan öğrenilir.

#### Ortamdan Çıkan ve Çıkmayan

Son üç satır bu dersin yalıtım hesabıdır. Elle kurulum altı komut, sekiz bayrak ve yirmi dört
dizilişten birini seçme kararı ister; yirmi dördün yirmi ikisi en az bir bağımlılığı bozar.
Bildirimsel biçimde bunların yerine yirmi altı alanlık bir dosya ve tek bir komut vardır, diziliş
kararı sıfırdır. **Ortamdan çıkarılan fark budur:** başlatma sırası, ağ ve birim adları, bayrak
kümesi ve yeniden başlatmanın yayılımı, kuran kişinin kabuk geçmişinden çıkıp çıktının yanındaki
bir dosyaya girer. Bedel iki kalemdir: yazılan yirmi altı alan ve hazır olma denetiminin eklediği
üç adım.

Bu sayıları okurken bir ayrım korunmalıdır: altı komut ile sekiz bayrak **ortadan kalkmaz**, yer
değiştirir. Ağın kurulması, birimin oluşturulması ve dört sürecin başlatılması yine olur; yalnız
bunları hangi sırayla ve hangi bayraklarla yapacağına karar veren, kabuğun başındaki kişi değil
dosyadır. Kazanç komut sayısında değil, aynı kurulumu ikinci kez kuranın aynı yirmi iki yanlış
dizilişten kaçınmasının **kanıtlanabilir** olmasındadır. Bileşim dosyası bir kurulumu kısaltmaz,
tekrarlanabilir kılar; kısalan tek şey karar sayısıdır.

**Yalıtımın delindiği yer** aynı dosyada yazılıdır. Dört servis tek bir ağı paylaşır; bileşim
dosyası servisleri birbirine bağlamak için konteyner ağı dersinde kurulan ayrımı **gevşetir**,
birbirini görmesi gereken dördü aynı ad çözümleme uzayına koyar. `kayit-verisi` birimi iki servise
takılıdır — kayıt deposunda `/veri`, faturalamada `/arsiv` yolunda. Aynı bayt kümesi iki ayrı
konteynerde, iki ayrı kimlikle yazılabilir; önceki dersin izin matrisi burada tek bir ağaca değil,
iki ağacın kesiştiği bir noktaya bakar. Hazır olma denetiminin kendisi de bir kanıt değildir: bu modelde
`hazır` alanı bir sayıdır ve sayı yanlışsa yarışlar geri gelir, yalnız görünmez olurlar.

#### Özet

- Bileşim dosyasına bir şema tanımlamak, kurulum hatalarını başlatmadan önce görünür kılar: bozuk
  kurguda beş hatanın beşi tek okumada bulundu, elle kurulumda her biri ayrı bir koşum olurdu.
- Başlatma sırası grafikten türetilir; dört servisin yirmi dört dizilişinden yalnız ikisi geçerlidir
  ve döngü eklendiğinde sıralamanın **olmadığı** üç kenarlık çevrim adıyla basılır.
- Doğru sıra yarışları bitirmez: hazır olma denetimi olmadan dört bağımlılık kenarının üçü erken
  bağlanır, toplam beş adımlık gizli bekleme doğar.
- Hazır olma denetimi yarışı üçten sıfıra indirir, ayağa kalkmayı üç adımdan altı adıma çıkarır;
  takas dosyada yazılı bir seçim hâline gelir.
- Kayıt deposunun yeniden başlatılması üç servisi etkiler, faturalamanınki hiçbirini; elle
  kurulumda bu sayı hatalardan öğrenilir, dosyada bağımlılık listesinden okunur.
- Ortamdan çıkan fark altı komut, sekiz bayrak ve yirmi iki hatalı dizilişin dışlanmasıdır; bedeli
  yirmi altı alanlık dosya ile üç ek adımdır. Delik ortak ağ ile iki servise takılı tek birimdir.

#### Sonraki Adım

Bu ders bileşimi bir kez ayağa kaldırdı ve orada bıraktı. Oysa yerel ortamın asıl kullanımı tek bir
başlatma değil, gün boyunca yüzlerce kez tekrarlanan bir döngüdür: bir satır değiştirilir, sonucu
görülür, bir satır daha değiştirilir. O döngüde imajın yeniden üretilmesi ile kaynağın konteynere
bağlanması iki ayrı biçimdir ve ikisi de bedava değildir. Sonraki ders bu iki biçimi değişiklik
başına sayarak karşılaştırır — kopyalanan bayt, geçersizleşen katman ve geri bildirim adımı — ve
ikinci biçimin ödediği bedeli adıyla ölçer: bağlanan kaynağın ve atlanan derleme adımının çalışan
ortamı üretimden ne kadar uzaklaştırdığı, bu uzaklığın hangi hata sınıfını gizlediği.

### Geliştirici Deneyimi

Source: https://academia.sh/tr/kurslar/konteynerler/gelistirici-deneyimi

İki geri bildirim döngüsünün değişiklik başına karşılaştırması: imajı yeniden üretmenin geçersizleştirdiği katman, kopyaladığı bayt ve adım sayısı ile kaynağı bağlayıp süreci yeniden yüklemenin sıfırladığı bu üç sayı ve bağlamanın bedeli olan ortam uzaklığı.

Önceki ders dört servisi bir kez ayağa kaldırdı ve orada bıraktı. Yerel ortamın asıl kullanımı ise
tek bir başlatma değil, gün boyunca tekrarlanan bir döngüdür: bir satır değiştirilir, sonucu
görülür, bir satır daha değiştirilir. **Geri bildirim döngüsü** bu müfredatın ilk kursunda ölçülmüş
bir niceliktir; o sayım burada tekrarlanmaz. Buradaki soru döngünün süresi değil, konteynerli bir
düzende döngünün **iki ayrı biçimi** olması ve ikisinin farklı şeyler ödemesidir. Birinci biçim
imajı yeniden üretip konteyneri yeniden başlatır. İkinci biçim kaynak dizinini konteynere bağlar ve
süreci yerinde yeniden yükler. Ölçülen şey, değişiklik başına her birinin ne kadar bayt kopyaladığı,
kaç katmanı geçersizleştirdiği, kaç adım sürdüğü — ve ikincinin bunun karşılığında neyi bozduğudur.

Ölçüm nesnesi bölgesel ölçüm ağının okuma toplayıcı servisidir; kurgu bir yazılımdır, kaynak ağacı
gerçekten diske yazılır ve bayt boyutları ile içerik özetleri gerçek dosyalardan okunur.

**CZ37 — imaj tanımı beş adımdan oluşur:** temel katman, bağımlılık bildirimi, bağımlılık kurulumu,
kaynak kopyalama, derleme. **CZ38 — temel katman 41.943.040, bağımlılık kurulumu 18.874.368
bayttır**; bu iki sayı modeldir, geri kalan bayt gerçek dosyalardan gelir. **CZ39 — bir adımın
girdisi değişirse o adım ve kendinden sonraki bütün adımlar geçersizleşir.** **CZ40 — yok sayma
kuralları beş kalıptır** ve bunlara uyan dosya imaja girmez. **CZ41 — bağlama biçiminde kaynak
dizininin tamamı takılır**, yok sayma kuralları uygulanmaz ve kopyalama olmaz. **CZ42 — geri
bildirim adımı, dosya yazıldıktan sonra sonucun görülmesine kadarki ayrık adım sayısıdır.**

#### Katman Zinciri ve İki Küme

İlk koşum ağacı kurar, hangi dosyanın imaja girdiğini yok sayma kurallarıyla belirler ve beş adımın
zincirini hesaplar. Her adımın özeti bir önceki özete ve kendi girdilerinin içeriğine bağlıdır;
zincir bu yüzden bir yerden koptuğunda arkasındaki her şey kopar.

```js
// kaynak.mjs — gercek kaynak agaci, gercek bayt boyutlari, gercek icerik ozetleri.
// Dosyalar ve boyutlari gercektir; imaj adimlari ile temel katman/kurulum bayti MODELDIR.
import { mkdirSync, writeFileSync, rmSync, statSync, readFileSync } from 'node:fs';
import { createHash } from 'node:crypto';

export const KOK = './kaynak';

export const DOSYALAR = {
  'bagimlilik.json': '{"ad":"okuma-toplayici","surum":"7.2.0",'
    + '"bagimlilik":{"cozumleyici":"2.4.1","kayit-istemci":"1.9.3","zaman":"4.0.2"}}\n',
  'uygulama/baslat.mjs': "import { topla } from './toplayici.mjs';\n"
    + "import { AYAR } from './ayar.local.mjs';\ntopla(AYAR.kaynak);\n",
  'uygulama/toplayici.mjs': "import { bicimle } from './bicim.mjs';\n"
    + 'export const topla = (k) => bicimle(k);\n',
  'uygulama/dogrulayici.mjs': "import { bicimle } from './bicim.mjs';\n"
    + 'export const dogrula = (o) => bicimle(o).length > 0;\n',
  'uygulama/bicim.mjs': 'export const bicimle = (x) => String(x).trim();\n',
  'uygulama/ayar.local.mjs': "export const AYAR = { kaynak: 'yerel-sayac', gunluk: 'ayrintili' };\n",
  'uygulama/ornek-olcum.json': '[{"sayac":"S-001","deger":1420},{"sayac":"S-002","deger":980}]\n',
  'sinama/toplayici.sinama.mjs': "import { topla } from '../uygulama/toplayici.mjs';\n"
    + "console.log(topla(' 12 '));\n",
  'notlar.md': '# yerel notlar\n- sayac verisi ornek dosyadan okunuyor\n',
  '.gizli-ayar': 'depo-belirteci=yerel-gelistirme\n',
};

// CZ40: imaja alinmayanlar. Baglama bicimi bu kurallari uygulamaz, dizinin tamamini takar.
export const YOK_SAY = [/^sinama\//, /\.local\./, /(^|\/)ornek-/, /^notlar\.md$/, /(^|\/)\.gizli-/];
export const imajda = (yol) => !YOK_SAY.some((r) => r.test(yol));

// CZ37/CZ38: bes adim. cikti verilmisse MODEL bayt, verilmemisse girdilerin gercek bayti.
export const ADIMLAR = [
  { ad: 'temel katman', onek: [], cikti: 41943040 },
  { ad: 'bagimlilik bildirimi', onek: ['bagimlilik.json'] },
  { ad: 'bagimlilik kurulumu', onek: ['bagimlilik.json'], cikti: 18874368 },
  { ad: 'kaynak kopyalama', onek: ['uygulama/'] },
  { ad: 'derleme', onek: ['uygulama/'], derle: true },
];

export function kur() {
  rmSync(KOK, { recursive: true, force: true });
  for (const [yol, icerik] of Object.entries(DOSYALAR)) {
    const dizin = yol.includes('/') ? `${KOK}/${yol.slice(0, yol.lastIndexOf('/'))}` : KOK;
    mkdirSync(dizin, { recursive: true });
    writeFileSync(`${KOK}/${yol}`, icerik);
  }
  return Object.keys(DOSYALAR).map((yol) => ({ yol, bayt: statSync(`${KOK}/${yol}`).size }));
}

const ozetle = (m) => createHash('sha256').update(m).digest('hex');
const girdileri = (adim, agac) => agac.filter((d) => imajda(d.yol)
  && adim.onek.some((o) => d.yol === o || d.yol.startsWith(o)));

// Derleme adimi gercekten kosar: imaja giren kaynak modulleri tek cikti dosyasinda birlestirilir.
export function derle(agac) {
  const parcalar = girdileri(ADIMLAR[4], agac).filter((d) => d.yol.endsWith('.mjs'))
    .map((d) => readFileSync(`${KOK}/${d.yol}`, 'utf8'));
  mkdirSync(`${KOK}/dagitim`, { recursive: true });
  writeFileSync(`${KOK}/dagitim/paket.mjs`, parcalar.join('\n'));
  return statSync(`${KOK}/dagitim/paket.mjs`).size;
}

// Katman zinciri: her adimin ozeti bir onceki ozete ve kendi girdilerinin icerigine baglidir.
export function zincir(agac) {
  let onceki = '0'.repeat(64);
  return ADIMLAR.map((adim) => {
    const girdi = girdileri(adim, agac);
    const icerik = girdi.map((d) => ozetle(readFileSync(`${KOK}/${d.yol}`))).join('');
    onceki = ozetle(onceki + adim.ad + icerik);
    const bayt = adim.cikti ?? (adim.derle ? derle(agac) : girdi.reduce((a, d) => a + d.bayt, 0));
    return { ad: adim.ad, girdi: girdi.length, bayt, ozet: onceki.slice(0, 12) };
  });
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const agac = kur();
  const s = (x, n) => String(x).padStart(n);
  const t = (x) => x.toLocaleString('tr-TR');
  console.log(`${'dosya'.padEnd(28)}${s('bayt', 6)}${s('imajda', 8)}`);
  for (const d of agac) console.log(`${d.yol.padEnd(28)}${s(d.bayt, 6)}${s(imajda(d.yol) ? 'evet' : 'hayir', 8)}`);
  const imaj = agac.filter((d) => imajda(d.yol));
  console.log(`baglanan kume ${agac.length} dosya / ${t(agac.reduce((a, d) => a + d.bayt, 0))} bayt`
    + `   imaj kumesi ${imaj.length} dosya / ${t(imaj.reduce((a, d) => a + d.bayt, 0))} bayt`);

  console.log(`\n${'adim'.padEnd(22)}${s('girdi', 6)}${s('cikti bayt', 12)}${s('ozet', 15)}`);
  for (const k of zincir(agac)) {
    console.log(`${k.ad.padEnd(22)}${s(k.girdi, 6)}${s(t(k.bayt), 12)}${s(k.ozet, 15)}`);
  }
}
```

```
dosya                         bayt  imajda
bagimlilik.json                118    evet
uygulama/baslat.mjs            102    evet
uygulama/toplayici.mjs          79    evet
uygulama/dogrulayici.mjs        92    evet
uygulama/bicim.mjs              48    evet
uygulama/ayar.local.mjs         68   hayir
uygulama/ornek-olcum.json       63   hayir
sinama/toplayici.sinama.mjs     79   hayir
notlar.md                       54   hayir
.gizli-ayar                     32   hayir
baglanan kume 10 dosya / 735 bayt   imaj kumesi 5 dosya / 439 bayt

adim                   girdi  cikti bayt           ozet
temel katman               0  41.943.040   40f844bcb047
bagimlilik bildirimi       1         118   f52d32c295f8
bagimlilik kurulumu        1  18.874.368   a884990dc6c3
kaynak kopyalama           4         321   e7d548395511
derleme                    4         324   c4729347b567
```

Tablonun iki yarısı arasındaki oran bu dersin bütün ölçüsünü belirliyor. Kaynak dört dosya ve 321
bayttır; onun altındaki iki adım 60.817.408 bayttır, yani kaynağın yaklaşık yüz doksan bin katı.
Değişikliklerin çoğu kaynağa gelir, bedelin çoğu ise kaynağın altında durur. Katman zincirinin
bütün anlamı budur: değişen adımın **üstündekiler** yeniden üretilmez.

#### Değişiklik Başına İki Döngü

İkinci koşum üç değişiklik sınıfını gerçekten dosyaya yazar, zinciri yeniden hesaplar ve hangi
adımların özetinin değiştiğini karşılaştırır. Her sınıf iki biçimde ölçülür.

```js
// dongu.mjs — iki geri bildirim dongusu: imaji yeniden uretmek ile kaynagi baglayip yeniden yuklemek.
import { readFileSync, writeFileSync, statSync } from 'node:fs';
import { KOK, DOSYALAR, ADIMLAR, imajda, kur, zincir, derle } from './kaynak.mjs';

const agacOku = () => Object.keys(DOSYALAR).map((yol) => ({ yol, bayt: statSync(`${KOK}/${yol}`).size }));

const DEGISIKLIK = {
  'uygulama/bicim.mjs': DOSYALAR['uygulama/bicim.mjs'].replace('trim()', 'trim().slice(0, 64)'),
  'bagimlilik.json': DOSYALAR['bagimlilik.json'].replace('2.4.1', '2.5.0'),
  'notlar.md': `${DOSYALAR['notlar.md']}- yok sayma listesi gozden gecirilecek\n`,
};

// CZ42: geri bildirim adimi = dosya yazildiktan sonra sonucun gorulmesine kadarki ayrik adim.
const uretimAdimi = (g) => 1 + (g === 0 ? 1 : g + 3);   // yaz + adimlar + durdur + baslat + hazir ol
const BAGLAMA_ADIMI = 2;                                 // yaz + surec yeniden yuklemesi

const temel = zincir(kur());
const s = (x, n) => String(x).padStart(n);
const t = (x) => x.toLocaleString('tr-TR');
console.log(`${'degisiklik'.padEnd(22)}${'bicim'.padEnd(23)}${s('gecersiz katman', 16)}`
  + `${s('kopyalanan bayt', 17)}${s('adim', 6)}`);
for (const [yol, yeniIcerik] of Object.entries(DEGISIKLIK)) {
  kur();
  writeFileSync(`${KOK}/${yol}`, yeniIcerik);
  const yeni = zincir(agacOku());
  const gecersiz = yeni.filter((k, i) => k.ozet !== temel[i].ozet);
  const bayt = gecersiz.reduce((a, k) => a + k.bayt, 0);
  const bagliBicim = yol === 'bagimlilik.json' ? 'yeniden uretime duser' : 'bagla + yeniden yukle';
  console.log(`${yol.padEnd(22)}${'yeniden uret'.padEnd(23)}${s(`${gecersiz.length}/${ADIMLAR.length}`, 16)}`
    + `${s(t(bayt), 17)}${s(uretimAdimi(gecersiz.length), 6)}`);
  console.log(`${''.padEnd(22)}${bagliBicim.padEnd(23)}`
    + `${s(yol === 'bagimlilik.json' ? '-' : `0/${ADIMLAR.length}`, 16)}`
    + `${s(yol === 'bagimlilik.json' ? '-' : '0', 17)}`
    + `${s(yol === 'bagimlilik.json' ? '-' : BAGLAMA_ADIMI, 6)}`);
  if (gecersiz.length) console.log(`${''.padEnd(22)}gecersizlesen: ${gecersiz.map((k) => k.ad).join(', ')}`);
}

// Baglama biciminin bedeli: calisan ortamin uretimden uzakligi.
const agac = kur();
const bagli = agac;                                   // CZ41: dizinin tamami takilir
const imaj = agac.filter((d) => imajda(d.yol));
const ICE = /from\s+'(\.[^']+)'/g;
function cozulmeyen(kume) {
  const yollar = new Set(kume.map((d) => d.yol));
  const eksik = [];
  for (const d of kume.filter((x) => x.yol.endsWith('.mjs'))) {
    const taban = d.yol.slice(0, d.yol.lastIndexOf('/'));
    for (const [, hedef] of readFileSync(`${KOK}/${d.yol}`, 'utf8').matchAll(ICE)) {
      const coz = new URL(hedef, `file:///${taban}/`).pathname.slice(1);
      if (!yollar.has(coz)) eksik.push(`${d.yol} -> ${hedef}`);
    }
  }
  return eksik;
}
const derlenmis = derle(agac);
console.log(`\n${'ortam'.padEnd(22)}${s('dosya', 7)}${s('bayt', 7)}${s('cozulmeyen ice aktarma', 24)}`);
for (const [ad, kume] of [['baglanan kaynak', bagli], ['uretim imaji', imaj]]) {
  console.log(`${ad.padEnd(22)}${s(kume.length, 7)}${s(kume.reduce((a, d) => a + d.bayt, 0), 7)}`
    + `${s(cozulmeyen(kume).length, 24)}`);
}
for (const e of cozulmeyen(imaj)) console.log(`  yalniz imajda kirilan: ${e}`);
console.log(`imajda olmayip baglanan ${bagli.length - imaj.length} dosya`
  + ` (${bagli.filter((d) => !imajda(d.yol)).map((d) => d.yol).join(', ')})`);
console.log(`baglama biciminde hic kosmayan derleme adimi: ${imaj.filter((d) => d.yol.endsWith('.mjs')).length}`
  + ` kaynaktan ${derlenmis} baytlik tek cikti`);
```

```
degisiklik            bicim                   gecersiz katman  kopyalanan bayt  adim
uygulama/bicim.mjs    yeniden uret                        2/5              671     6
                      bagla + yeniden yukle               0/5                0     2
                      gecersizlesen: kaynak kopyalama, derleme
bagimlilik.json       yeniden uret                        4/5       18.875.131     8
                      yeniden uretime duser                 -                -     -
                      gecersizlesen: bagimlilik bildirimi, bagimlilik kurulumu, kaynak kopyalama, derleme
notlar.md             yeniden uret                        0/5                0     2
                      bagla + yeniden yukle               0/5                0     2

ortam                   dosya   bayt  cozulmeyen ice aktarma
baglanan kaynak            10    735                       0
uretim imaji                5    439                       1
  yalniz imajda kirilan: uygulama/baslat.mjs -> ./ayar.local.mjs
imajda olmayip baglanan 5 dosya (uygulama/ayar.local.mjs, uygulama/ornek-olcum.json, sinama/toplayici.sinama.mjs, notlar.md, .gizli-ayar)
baglama biciminde hic kosmayan derleme adimi: 4 kaynaktan 324 baytlik tek cikti
```

Kaynak satırı değişikliği en sık gelen sınıftır ve iki biçim arasındaki fark burada en açık
görünür: beş adımın ikisi geçersizleşir, 671 bayt kopyalanır ve altı adım geçer; bağlama biçiminde
sıfır katman, sıfır bayt ve iki adım. Adım sayısındaki üç kat, bayt sayısındaki farktan daha
anlamlıdır çünkü bayt sayısı kaynak ağacının büyüklüğüyle ölçeklenir, adım sayısı ölçeklenmez.
Buradaki 671 bayt on dosyalık bir ağaçtan gelir; ağaç bin dosya olsaydı bayt yüz binlere çıkardı,
adım sayısı yine altı kalırdı.

Altı adımın nelerden oluştuğu da sayının bir parçasıdır: dosya yazılır, geçersizleşen iki adım
yeniden yürütülür, konteyner durdurulur, yenisiyle başlatılır ve hazır olması beklenir. Bunların
hiçbiri değişikliğin içeriğiyle ilgili değildir; hepsi çıktının yeniden üretilmesinin ve çalışan
sürecin değiştirilmesinin bedelidir. Günde kırk kaynak değişikliği yapan bir kişi için iki biçim
arasındaki fark dört ek adımın kırk katıdır — yüz altmış durak ve her durak sonucun görülmesinden
önce beklenen bir yerdir. Bağlama biçiminin satın aldığı şey kopyalanmayan 671 bayt değil, bu
duraklardır.

İkinci satır bağlama biçiminin sınırını çiziyor. Bağımlılık bildirimi değiştiğinde beş adımın dördü
geçersizleşir, kopyalanan bayt 18.875.131'e — kaynak değişikliğinin yaklaşık yirmi sekiz bin katına
— çıkar ve döngü sekiz adım sürer. Bağlama bu sınıfa dokunamaz: bağımlılıklar kaynak dizininde
değil, kurulum adımının çıktısındadır, dolayısıyla döngü yeniden üretime düşer. Bağlamanın kazancı
**tek bir değişiklik sınıfında** geçerlidir ve o sınıfın dışına çıkıldığında birinci biçimin bütün
bedeli geri gelir.

Üçüncü satır sessiz olanı gösteriyor. Yok sayılan bir dosya değiştiğinde imaj tarafında hiçbir
katman geçersizleşmez; bu doğru davranıştır. Ama aynı değişiklik bağlama biçiminde **görünür**,
çünkü dosya takılan dizinin içindedir ve süreç onu okuyabilir. İki biçim aynı sayıları verir, farklı
şeyler yapar. Bu satır bir sonraki bölümün konusudur.

#### Bağlamanın Bedeli: Uzaklaşan Ortam

Çıktının ikinci yarısı bağlama biçiminin ödediği bedeli sayıyor. Yerelde çalışan ağaç on dosya ve
735 bayttır; üretime giden imaj beş dosya ve 439 bayt. Yerelde koşan sürecin gördüğü dosyaların
**yarısı** üretimde yoktur. Bu fark bir kaza değil, yok sayma kurallarının kasıtlı sonucudur — ve
bağlama biçimi tam olarak o kuralları atlayarak çalışır.

Farkın somut sonucu bir satır aşağıdadır. `baslat.mjs` dosyası `./ayar.local.mjs` dosyasını içe
aktarır. Bağlanan kümede bu içe aktarma çözülür ve süreç sorunsuz kalkar; üretim imajında dosya
bulunmadığı için çözülmez. Ölçüm bunu sayıyor: bağlanan kümede sıfır, imajda bir çözülmeyen içe
aktarma. **Yerel döngü bu hatayı hiçbir koşumda gösteremez**, çünkü hatanın koşulu tam da yerel
döngünün ortadan kaldırdığı şeydir. Bu, bağlama biçiminin gizlediği hata sınıfının adıdır: yok
sayma kuralının imajdan çıkardığı bir dosyaya kalan bağımlılık.

İkinci gizli sınıf son satırdadır. Derleme adımı bağlama biçiminde hiç koşmaz; dört kaynaktan
üretilen 324 baytlık çıktı yerelde hiçbir zaman çalıştırılmaz. Yerelde çalışan şey kaynağın
kendisidir, üretimde çalışan şey derlemenin ürünüdür ve ikisinin aynı davrandığı hiçbir yerde
sınanmaz. Bu iki sınıf birlikte, geliştirici deneyiminin gerçek fiyatını verir: döngü altı adımdan
iki adıma inerken, yerel ortam üretimden beş dosya, bir çözülmeyen bağımlılık ve bir hiç koşmayan
adım kadar uzaklaşır.

**Yalıtımın delindiği yer** bu derste bir kusur değil, aracın kendisidir. Bağlama takması imajın
kapattığı sınırı bilerek deler; kaynak dizini artık çıktının içinde değil, ortamdadır. Önceki iki
dersin ölçüsü buraya doğrudan bağlanır: takılan dizinin dosya sahipliği içerideki kimlikle
uyuşmayabilir ve bileşim dosyası bu takmayı her servis için ayrı ayrı bildirir. Deliğin ölçüsü
yukarıdaki üç sayıdır ve ölçülebilir olması, deliğin kapatılmasını değil, **ne zaman kapatılacağının
bilinmesini** sağlar.

#### Özet

- Kaynak dört dosya ve 321 bayt, altındaki iki adım 60.817.408 bayttır; değişikliklerin çoğu üstte,
  bedelin çoğu alttadır ve katman zinciri değişen adımın üstündekileri yeniden üretmez.
- Kaynak satırı değişikliğinde yeniden üretim beş katmanın ikisini geçersizleştirir, 671 bayt
  kopyalar ve altı adım sürer; bağlama biçiminde sıfır katman, sıfır bayt, iki adım.
- Bayt farkı ağaç büyüdükçe büyür, adım farkı büyümez: üç katlık adım oranı bu ölçümün ağaçtan
  bağımsız kısmıdır.
- Bağımlılık bildirimi değiştiğinde dört katman geçersizleşir ve 18.875.131 bayt kopyalanır; bağlama
  biçimi bu sınıfa dokunamaz, döngü yeniden üretime düşer.
- Bağlanan ağaç on dosya ve 735 bayt, üretim imajı beş dosya ve 439 bayttır; yerelde çözülen bir içe
  aktarma imajda çözülmez ve bu hatayı yerel döngü hiçbir koşumda gösteremez.
- Derleme adımı bağlama biçiminde hiç koşmaz: dört kaynaktan üretilen 324 baytlık çıktı yerelde
  çalıştırılmaz. Döngü altı adımdan iki adıma inerken ortam beş dosya, bir çözülmeyen bağımlılık ve
  bir koşmayan adım kadar üretimden uzaklaşır.

#### Sonraki Adım

Bu dersin sonunda yalıtım bilerek delinmiş durumda: bir dizin dışarıdan takılı, kaynak imajın içinde
değil, derleme adımı atlanmış. Üç ders boyunca ölçülen şey hep aynı eksenin bir parçasıydı — kimin
çalıştığı, kaç servisin birbirini gördüğü, hangi dosyanın nereden geldiği. Bu delikler yerel ortamda
bilinçli bir seçimdir. Aynı seçimler üretimde de yapılabilir ve orada bedelleri başkadır: bir
konteynerin çekirdekten isteyebileceği ayrıcalıklar, dosya sisteminin yazılabilir kalması, kaldırılan
her kısıtın açtığı yol. Sonraki ders çalışma zamanı güvenliğini bu eksende ele alır ve yalıtımın
isteğe bağlı olan kısımlarını tek tek sayar: hangisi varsayılan olarak açık, kapatıldığında ne
kırılıyor, açık bırakıldığında hangi yol açılıyor.

### Çalışma Zamanı Güvenliği

Source: https://academia.sh/tr/kurslar/konteynerler/calisma-zamani-guvenligi

Koşan konteynerin yetkisi iki kısıtlamayla ölçülür: on dört öntanımlı yeteneğin kaçının meşru bir işlemi olduğu ve düşürülenlerin yerine ne konduğu, salt okunur kökün kaç yazma girişimini düşürdüğü, yazılabilir bırakılan yolların açtığı yüzey ve on iki kötüye kullanım girişiminin kaçının kapandığı.

Önceki ders geliştirme döngüsünü altı adımdan iki adıma indirdi ve bunu **yalıtımı bilerek
delerek** yaptı: kaynak takıldı, derleme adımı atlandı, ortam üretimden uzaklaştı. O ölçümün
tamamı hızla ilgiliydi. Üretimde koşan sürecin **ne yapabildiği** — çekirdekten hangi ayrıcalığı
isteyebildiği, hangi dosyaya yazabildiği — hiç sorulmadı.

İki kısıtlama ölçülür: **yetenek kısıtlama** ve **salt okunur kök dosya sistemi**. İkisi de
öntanımlı açık olanı kapatır ve bedelleri aynı kalemdedir: kapatılanın meşru bir kullanıcısı varsa,
yerine bir düzenleme yazılır. Ölçüm nesnesi bölgesel ölçüm ağının okuma toplayıcı servisidir;
kurgudur, kip bitleri ve yazma denemeleri gerçektir.

**CZ43 — çalışma zamanı konteynere öntanımlı olarak on dört yetenek açık verir**; adlar genel
karşılıklarıyla yazılır. **CZ44 — servisin meşru işlemleri ve her yeteneğin istediği işlem kurgu
envanterdir.** **CZ45 — kötüye kullanım kümesi on iki girişimdir**; her girişim ya bir yetenek ya
bir yazma yolu ister, biri hiçbirini. **CZ46 — salt okunur kök gerçek kip bitleriyle kurulur.**
**CZ47 — çalıştırma yasağı modeldir: yazılabilir yollarda çalıştırma biti yoktur.** **CZ48 — yanlış
alarm, kurala uygun bir işlemin engellenmesidir.**

#### Yetenek Kümesi

Bir yetenek, ayrıcalıklı kimliğin yapabildiği işlerden birini tek başına adlandırır. Öntanımlı küme
bir karar değil, geniş tutulmuş bir varsayılandır; ölçülen şey ne kadarının kullanıldığıdır.

```js
// yetenek.mjs — yetenek kumesi ve bilinen kotuye kullanim kumesi (MODEL).
// Yetenekler genel adlariyla anilir; hicbir cekirdek sabiti yazilmaz.

// [yetenek, isteyen mesru islem (yoksa null), dusurulunce yerine konan]
export const YETENEK = [
  ['sahiplik degistirme', 'veri dizini sahipligi', 'sahiplik derleme aninda kurulur'],
  ['kimlik degistirme', 'baslangicta kimlik dusurme', 'surec dogrudan koksuz baslar'],
  ['ham yuva acma', 'ag tanilama', 'tanilama yardimci konteynerde'],
  ['surec oldurme', 'gecelik isi durdurma', 'ayni kimlikte yetenek gerekmez'],
  ['dosya sahipligini yoksayma', 'ortak gunluge ekleme', 'ortak grup ve kip biti'],
  ['dosya izinlerini yoksayma', null, '-'],
  ['ayricalikli kapi baglama', null, 'yuksek kapi zaten kullaniliyor'],
  ['surec izleme', null, 'dokum disaridan tetiklenir'],
  ['zaman degistirme', null, 'saat konaktan gelir'],
  ['cihaz dugumu olusturma', null, '-'],
  ['cekirdek modulu yukleme', null, '-'],
  ['sistem gunlugu okuma', null, '-'],
  ['kaynak sinirini asma', null, '-'],
  ['takma yapma', null, 'takmalar baslamadan kurulur'],
];

// Kotuye kullanim kumesi (kurgu): [girisim, istedigi yetenek, istedigi yazma yolu]
export const KOTUYE = [
  ['saati geri alma', 'zaman degistirme', null],
  ['modul yukleme', 'cekirdek modulu yukleme', null],
  ['baska kimligin dosyasini okuma', 'dosya izinlerini yoksayma', null],
  ['ham paket dinleme', 'ham yuva acma', null],
  ['komsu surece baglanma', 'surec izleme', null],
  ['ham diske erisim', 'cihaz dugumu olusturma', null],
  ['yeni takma noktasi acma', 'takma yapma', null],
  ['uygulama kodunu degistirme', null, 'uygulama/toplayici.mjs'],
  ['baslangic betigine kanca', null, 'uygulama/baslat.sh'],
  ['ayardaki ucu tasima', null, 'ayar/servis.json'],
  ['gecici dizine ikili yazip calistirma', null, 'gecici/yuk.bin'],
  ['takili dizinden okuyup gonderme', null, null],
];

// Servisin bir gecelik kosumda gercekten yazdigi yollar.
export const MESRU_YAZMA = ['gunluk/dogrulama.log', 'onbellek/tarife-1.json',
  'veri/durum/ilerleme.json', 'veri/duzeltmeler/duzeltmeler.csv', 'gecici/ara-0001.tmp'];

export const gereksiz = YETENEK.filter(([, i]) => i === null).map(([y]) => y);
export const kullanilan = YETENEK.filter(([, i]) => i !== null);

if (import.meta.url === `file://${process.argv[1]}`) {
  const s = (x, n) => String(x).padStart(n);
  console.log(`${'mesru islemi olan yetenek'.padEnd(28)}${'islem'.padEnd(28)}dusurulunce yerine`);
  for (const [y, i, yerine] of kullanilan) console.log(`${y.padEnd(28)}${i.padEnd(28)}${yerine}`);
  console.log(`\nontanimli acik ${YETENEK.length}, mesru islemi olan ${kullanilan.length}`
    + `, gereksiz acik ${gereksiz.length}`);

  const kapanan = (d) => KOTUYE.filter(([, y]) => y && d.includes(y)).length;
  console.log(`\n${'kisitlama'.padEnd(30)}${s('kapanan', 9)}${s('kacan', 7)}${s('dusen mesru islem', 19)}`);
  for (const [ad, d] of [['gereksiz yetenekler dusuk', gereksiz],
    ['butun yetenekler dusuk', YETENEK.map(([y]) => y)]]) {
    const k = kapanan(d);
    console.log(`${ad.padEnd(30)}${s(`${k}/${KOTUYE.length}`, 9)}${s(KOTUYE.length - k, 7)}`
      + `${s(YETENEK.filter(([y, i]) => i && d.includes(y)).length, 19)}`);
  }
  console.log(`yetenekle kapanmayan ${KOTUYE.filter(([, y]) => !y).length} girisimin `
    + `${KOTUYE.filter(([, y, p]) => !y && p).length} tanesi bir yazma yolu ister`);
}
```

```
mesru islemi olan yetenek   islem                       dusurulunce yerine
sahiplik degistirme         veri dizini sahipligi       sahiplik derleme aninda kurulur
kimlik degistirme           baslangicta kimlik dusurme  surec dogrudan koksuz baslar
ham yuva acma               ag tanilama                 tanilama yardimci konteynerde
surec oldurme               gecelik isi durdurma        ayni kimlikte yetenek gerekmez
dosya sahipligini yoksayma  ortak gunluge ekleme        ortak grup ve kip biti

ontanimli acik 14, mesru islemi olan 5, gereksiz acik 9

kisitlama                       kapanan  kacan  dusen mesru islem
gereksiz yetenekler dusuk          6/12      6                  0
butun yetenekler dusuk             7/12      5                  5
yetenekle kapanmayan 5 girisimin 4 tanesi bir yazma yolu ister
```

On dört yeteneğin yalnız **beşinin** meşru bir işlemi vardır. Kalan dokuz, yalıtım bütçesinde
bedeli sıfır olan tek kalemdir: düşürüldüğünde on iki girişimin **altısı** kapanır ve düşen meşru
işlem **sıfırdır**. Kullanılmayan bir yetkiyi kapatmak bir takas değildir — bu kursta bedelsiz bir
kazancın görüldüğü tek yer burasıdır.

Beşi de düşürüldüğünde bir girişim daha kapanır ve beş meşru işlem düşer. Sağ sütun yerine ne
konduğunu yazıyor: sahiplik derleme anında kurulur, süreç doğrudan köksüz başlar, tanılama yardımcı
bir konteynere taşınır. Düşen beş işlem vazgeçilen bir yetenek değil, **başka bir yere taşınan bir
iştir**; bedel bayt değil yazımdır. Son satır sınırı çiziyor: beş girişim yetenek düşürmeyle
kapanmaz, dördü bir dosyaya yazmak ister.

#### Salt Okunur Kök

İkinci kısıtlama o dört girişimi hedefler. Aşağıdaki koşum kökü gerçek kip bitleriyle kurar — salt
okunur dizinler `0555`, yazılabilir bırakılanlar `0755` — ve dokuz yazma denemesini gerçekten yapar.

```js
// salt-okunur.mjs — salt okunur kok gercek dizinlerde: gercek kip bitleri, gercek yazma denemeleri.
import { mkdirSync, writeFileSync, chmodSync, rmSync } from 'node:fs';
import { YETENEK, KOTUYE, MESRU_YAZMA } from './yetenek.mjs';

const KOK = './kok';
const DIZIN = ['uygulama', 'ayar', 'veri', 'veri/durum', 'veri/duzeltmeler', 'gunluk', 'onbellek', 'gecici'];
const YAZILABILIR = ['veri/durum', 'veri/duzeltmeler', 'gunluk', 'onbellek', 'gecici'];

const HEDEF = [...MESRU_YAZMA.map((y) => [y, 'mesru', null]),
  ...KOTUYE.filter(([, , y]) => y).map(([ad, , y]) => [y, 'kotuye', ad])];

const serbest = () => {                     // silmeden once kip bitleri geri acilir
  for (const d of [...DIZIN].reverse()) { try { chmodSync(`${KOK}/${d}`, 0o755); } catch { /* yok */ } }
};
const kur = (kip) => {
  serbest();
  rmSync(KOK, { recursive: true, force: true });
  for (const d of DIZIN) mkdirSync(`${KOK}/${d}`, { recursive: true });
  for (const d of DIZIN) chmodSync(`${KOK}/${d}`, kip(d));   // kip bitleri gercekten kuruluyor
};
const dene = (y) => { try { writeFileSync(`${KOK}/${y}`, 'x'); return true; } catch { return false; } };

const s = (x, n) => String(x).padStart(n);
console.log(`${'duzen'.padEnd(30)}${s('mesru', 7)}${s('kotuye', 8)}${s('kapanan', 9)}${s('yanlis alarm', 14)}`);
let acik = [];
for (const [ad, kip] of [['kok yazilabilir', () => 0o755], ['kok salt okunur', () => 0o555],
  ['salt okunur + yazilabilir yol', (d) => (YAZILABILIR.includes(d) ? 0o755 : 0o555)]]) {
  kur(kip);
  const r = HEDEF.map(([yol, tur, kotu]) => [tur, dene(yol), kotu]);
  const m = r.filter(([t]) => t === 'mesru'), k = r.filter(([t]) => t === 'kotuye');
  acik = k.filter(([, ok]) => ok).map(([, , kotu]) => kotu);
  console.log(`${ad.padEnd(30)}${s(`${m.filter(([, o]) => o).length}/${m.length}`, 7)}`
    + `${s(`${acik.length}/${k.length}`, 8)}${s(k.length - acik.length, 9)}`
    + `${s(m.filter(([, o]) => !o).length, 14)}`);
}
console.log(`yazilabilir birakilan ${YAZILABILIR.length}/${DIZIN.length} yol; acik kalan: ${acik.join(', ')}`);
console.log(`calistirma yasagi (model): yazilabilir yollarin 0'inda x biti -> ${acik.length} girisim daha kapanir`);

// Birikimli: butun yetenekler dusuk + salt okunur kok + yazilabilir yollar + calistirma yasagi.
kur((d) => (YAZILABILIR.includes(d) ? 0o755 : 0o555));
const kacan = KOTUYE.filter(([, y, p]) => !y && !p).map(([ad]) => ad);
const mesru = YETENEK.filter(([, i]) => i).length;
console.log(`\nbirikimli: kapanan ${KOTUYE.length - kacan.length}/${KOTUYE.length}, kacan `
  + `${kacan.length} (${kacan.join(', ')})`);
console.log(`kalan yuzey: ${YAZILABILIR.length} yazilabilir yol; acik yetenek ${YETENEK.length} -> 0;`
  + ` dusen ${mesru} mesru isleme ${mesru} karsilik yazildi`);
console.log(`uygulama hala kosuyor mu: ${MESRU_YAZMA.filter((y) => dene(y)).length}/${MESRU_YAZMA.length}`
  + ' mesru yazma basarili');
serbest();
rmSync(KOK, { recursive: true, force: true });
```

```
duzen                           mesru  kotuye  kapanan  yanlis alarm
kok yazilabilir                   5/5     4/4        0             0
kok salt okunur                   0/5     0/4        4             5
salt okunur + yazilabilir yol     5/5     1/4        3             0
yazilabilir birakilan 5/8 yol; acik kalan: gecici dizine ikili yazip calistirma
calistirma yasagi (model): yazilabilir yollarin 0'inda x biti -> 1 girisim daha kapanir

birikimli: kapanan 11/12, kacan 1 (takili dizinden okuyup gonderme)
kalan yuzey: 5 yazilabilir yol; acik yetenek 14 -> 0; dusen 5 mesru isleme 5 karsilik yazildi
uygulama hala kosuyor mu: 5/5 mesru yazma basarili
```

İkinci satır bu dersin en öğretici satırıdır. Kök tümüyle salt okunur yapıldığında dört yazma
girişiminin dördü kapanır — kapanma sayısı en yüksek olan düzen budur — ama **beş meşru yazmanın
beşi de düşer** ve servis hiç koşmaz. Bir kısıtlamayı yalnız kapattığı girişim sayısıyla ölçmek,
uygulamanın çalışmadığı düzeni en iyi düzen olarak raporlar.

Üçüncü satır dengeyi kurar: beş yol yazılabilir bırakıldığında meşru yazmalar geri gelir, yanlış
alarm sıfırlanır, kapanan sayısı dörtten üçe iner. Kaybedilen girişim `gecici` dizinine ikili yazma
girişimidir. **Yalıtımın delindiği yer budur:** salt okunur kökün açtığı delik, geriye bırakılan
yolların kümesidir ve o küme uygulamanın ihtiyacı kadar büyüktür — sekiz dizinin beşi. Çalıştırma
yasağı bu girişimi de kapatır, çünkü o beş yolun hiçbirinde çalıştırma biti yoktur.

Birikimli satır ikisini üst üste koyar: on iki girişimin **on biri** kapanır, biri kaçar, uygulama
beş meşru yazmasının beşini de yapar. Kaçan girişim ne yetenek ne yazma ister; okuma izni birimler
dersinde verilmişti, ağ içi on altı uç ağ dersinde ölçüldüğü gibi kimlik sormuyordu. **Kısıtlamalar
istenebilecek yetkiyi daraltır, verilmiş olanı geri almaz.**

#### Özet

- On dört öntanımlı yeteneğin beşinin meşru bir işlemi vardır. Kalan dokuzu düşürmek on iki
  girişimin altısını kapatır ve sıfır meşru işlem düşürür — bu kurstaki tek bedelsiz kalem.
- Beşi de düşürüldüğünde bir girişim daha kapanır ve beş meşru işlem düşer; her birinin yerine bir
  düzenleme yazılır, yani bedel bayt değil yazımdır.
- Kök tümüyle salt okunur yapıldığında dört yazma girişimi de kapanır ama beş meşru yazma da
  düşer: en çok kapatan düzen, uygulamanın koşmadığı düzendir.
- Sekiz dizinin beşi yazılabilir bırakıldığında meşru yazma 5/5'e döner, kapanan sayısı üçe iner ve
  kalan girişim ancak çalıştırma yasağıyla kapanır.
- Birikimli düzende on iki girişimin on biri kapanır ve uygulama koşar; kaçan girişime okuma izni
  ile ağ erişimi zaten verilmiştir.

#### Kurs Kapanışı

| Ders | Ortamdan çıkarılan fark | Yalıtımın bedeli | Yalıtımın delindiği yer |
|---|---|---|---|
| Neden Konteyner | 18 farkın 6'sı çıktıda | 84,11 MB; fark başına 14,01 MB | Kaynak sınırı dışarıda; saat ve ad eşleştirme çekirdekte |
| Sunucu, Sanal Makine, Konteyner | 6/18 (sunucu 0, sanal makine 7) | 12 MB bellek, 300 ms, 84 MB disk; yük %6,3 | Çekirdek ortak: saat, sürüm, kusur |
| Alt Yapı Teknolojileri | Yalnız birleşim dosya sistemi; ad alanı 0, denetim grubu 0 | 16 bayt için 4.096 bayt kopya | Numara havuzu ortak; ağırlık tavan değil; silinen 1.024 bayt altta |
| Konteyner Standartları | Çıktı %100, davranış %70 | Doğrulama 83.650.787 bayt okur | Yoruma bırakılan 5 özelliğin 3'ü farklı |
| Konteyner Yaşam Döngüsü | Temizlenirse artık 0 | Durdurulmuş 436 MB, 1 ad, 1 birim; dönemde 13.876 MB | Yalnız süreç kaydı düşer; birim kalır |
| İmaj ve Katman Modeli | Dosya sistemi düzeni 22.526 baytlık depoda | 29.785 → 22.526 bayt; tek satır 8 zincir kimliği | Görünüm dışı 1.559 bayt depoda |
| İmaj Tanım Dosyası | Bağımlılık sürümü tek satıra | Bağlam 15.604 bayt; imaja girmeyen 5.067 | Kopyalananı kaydeder: 7.340 bayt fazla |
| Katman Önbelleği | İş depoya taşındı, ortamdan çıkmadı | Kötü sırada 21 katman, 289.389 bayt (%143,4) | Anahtar metni ölçer: 550 bayt büyüdü, 4 yönerge isabet etti |
| Çok Aşamalı Derleme | Koşumdan 208.222 bayt çıktı (%87,8) | İmaja girmeyen 237.202 bayt | Kopyalama sınırı: geniş yolda 4 dosya döner |
| Temel İmaj Seçimi | 148 / 31 / 6 bileşen içeride | 135,1 / 34,5 / 28,1 MB; 32 ek adım | Üretimi dışarıda: yılda 133 güncelleme |
| Etiketleme Düzeni | İzlenebilirlik 9/24 → 24/24 | İmaj başına 96 bayt, imajın 359 binde biri | Defter depoda: ad taşınmaz |
| İmaj Depoları | 80 referans 27 dosya; 690,0 → 176,2 KB | Yerel 176,2 KB; 27,3 KB silinemez | 7 özel imajın 16 katmanından 9'u genel havuzda |
| İmaj Güvenliği | Envanter ve imza 1.088 bayt | 37 uyarının 8'i yanlış alarm | Silinen anahtar 1.699 bayt altta; 0 uyarı, 1 açık kusur |
| Çalıştırma Seçenekleri | 14 değişken ortamda (9 zorunlu) | 21 tanım satırı; 10,54 dönem gecikme | Sınır tavan: kırpma 4215 birime |
| Geçici Dosya Sistemi | Koşumlar arası artık 0 | 1 bayt → 98.304 bayt; katman 2,15 kat | Alt katman ortak; geçicilik silmeye bağlı |
| Birimler ve Bağlama Takmaları | Yol her makinede aynı | 2 kalıcı dizin; 7.945 bayt kalır | Sayı uzayı ortak: 8 düğümün 6'sı yazılamaz |
| Konteyner Ağı | Makine adı yerine servis adı | 1 ağ tanımı, 1 üyelik satırı (4 → 5) | 16 uç kimlik sormaz; 4 yol iki adımda |
| Kullanıcılar ve İzinler | 15 girdinin 11'i içeride, 4'ü ortamdan | 4 sahiplik düzeltmesi; yazılabilir 15 → 5 | Kimlik konakla ortak: konakta 5/8 yazılır |
| Çok Konteynerli Yerel Ortam | 6 komut, 8 bayrak, 22 diziliş dosyaya | 26 alan; 3 ek adım (3 → 6) | 4 servis 1 ağ, 2 servis 1 birim |
| Geliştirici Deneyimi | İmaj kümesi 10 → 5 dosya | 2 katman, 671 bayt, 6 adım / 0, 0, 2 | Kasten delik: 1 çözülmeyen içe aktarma |
| Çalışma Zamanı Güvenliği | 14 yetenek → 0 | 5 işleme 5 karşılık; 5 yol yazılabilir | 12 girişimin 1'i kapanmaz; 5 yol açık, çekirdek ortak |

Kursun kuralı yirmi bir satırda aynı biçimde okunur: **yalıtım bir bütçedir.** Çıkarılan her fark
bir bayt, bir koşum ya da bir kaynak karşılığında çıkar — 14,01 MB, 4.096 bayt, %143,4 fazla iş,
üç ek adım — ve **hiçbir yalıtım tam değildir.** Son sütunda dört delik tekrar ediyor. Birincisi
**paylaşılan çekirdektir**: saat değeri, sistem çağrısı yüzeyi ve çekirdek kusuru üç ayrı derste
aynı yerden geçiyor. İkincisi **çıktının içine alınanın çıktıdan çıkarılamamasıdır**: görünümden
silinen 1.559 bayt da, katmanda kalan 1.699 baytlık anahtar da depoda duruyor. Üçüncüsü
**paylaşılan sayı uzaylarıdır** — süreç numarası, kullanıcı kimliği, kapı numarası; ad alanı
görünümü ayırır, sayıyı ayırmaz. Dördüncüsü **kuralın yalıtımın dışında durmasıdır**: sınırı
uygulayan dağıtıcı, etiketi çözen defter, kapıyı açan konak arayüzü ve yetenek kümesini veren
çalışma zamanı konteynerin dışındadır.

Bütün bu ölçümlerin ortak bir sınırı vardı: hepsi **tek makinede** yapıldı. Çıktı taşınabilir oldu,
kimliği içeriğinden türedi, yetkisi bir satıra indi. Ama onu kaç kopya, hangi makinede ve ne zaman
çalıştıracağına karar veren hâlâ bir insandır. Bu karar bir sisteme devredilebilir mi? Devredilirse
o sistemin neyi bilmesi gerekir: kaç kopyanın ayakta olduğunu, bir kopyanın gerçekten hizmet verip
vermediğini, hangi makinede ne kadar yer kaldığını, yeni sürüme geçerken kaç kopyayı aynı anda
değiştirmenin güvenli olduğunu. **M22/K03 Konteyner Orkestrasyonu** bu soruyla başlar.

