Ders 16 / 16
Geri Yükleme Tatbikatı
Yedeğin çalıştığının kanıtlanması: gerçek dosyaların yedeklenip silinmesi ve geri yüklenmesi, birebir aynı olduğunun özetle doğrulanması, yalnız tatbikatla görülen beş kusurun sayılması, geri yükleme süresinin beş bileşene ayrılıp kurtarma hedefiyle karşılaştırılması ve tatbikat sıklığının biriken belirsizlikle birlikte taranması.
İçindekiler
Önceki ders yedeğin kapsamını, sıklığını ve saklamasını sayıya bağladı; o sayılar bir yedek kümesinden okundu. Varsayılanın bıraktığı kalem en sessiz olanıdır: yedek alma adımı başarılı döner. Arşiv üretilir, boyutu yazılır, kayıt tutulur — ve o kayıt yedeğin geri yüklenebildiğine dair hiçbir şey söylemez. Ölçü geri döndürülebilirliğin kanıtıdır: üretimdeki bayt ile geri gelen baytı karşılaştıran bir özet.
Tatbikat Gerçekten Koşuyor
- ST31 — Kurgu bölgesel ölçüm ağının üretim ağacı gerçekten diske yazılıyor: beş kalem, içerikleri kendi üretecimizden, tohum 51703; geçici dizin koşum sonunda siliniyor.
- ST32 — Arşiv kabı kendi biçimimizdir; parola ve tuz kurgudur.
- ST33 — Beş kusurun üçü gerçekten enjekte edilir (kapsam dışı kalem, çevrilmiş bayt, başka anahtar); yanlış sıra ile eksik yetki modeldir.
- ST34 — Kurtarma süresi hedefi Dayanıklılık Kalıpları kursundan girdidir: 240 dakika. İndirme süresi önceki dersin 649,8 GiB tam yedeğinden ve 80 MiB/s’lik kurgu bir hattan türer; öteki dört bileşen modeldir.
- ST35 — Üretim kümesi ayda üç yapılandırma değişikliği ve çeyrekte bir kalem alır; bir tatbikat bir kişinin altı saatidir, ayrı bir geri yükleme ortamında koşar.
- ST36 — Süre ölçülür ama basılmaz; yalnız bant denetiminden geçirilir.
// tatbikat/kap.mjs — arsiv kabi kendi bicimimizdir: bildirim + ardisik icerikler. // Parola ve tuz kurgudur, gercek sir yazilmaz. import { readFileSync, writeFileSync, readdirSync, mkdirSync } from "node:fs"; import { join } from "node:path"; import { gzipSync, gunzipSync } from "node:zlib"; import { createCipheriv, createDecipheriv, createHash, scryptSync } from "node:crypto"; export const TOHUM = 51703; // ureteci kendimiz yaziyoruz, tohum gorunur let x = TOHUM >>> 0; export const uret = (n) => { const b = Buffer.allocUnsafe(n); for (let i = 0; i < n; i += 4) { x = (Math.imul(x, 1103515245) + 12345) >>> 0; b.writeUInt32LE(x, i); } return b; }; export const ozet = (b) => createHash("sha256").update(b).digest("hex"); const tuz = Buffer.from("kurgu-tuz-ornek-gecersiz"); export const ANAHTAR = scryptSync("kurgu-yedek-parolasi-ornek-gecersiz", tuz, 32); export const BASKA = scryptSync("kurgu-baska-parola-ornek-gecersiz", tuz, 32); const IV = Buffer.from("kurgu-iv-12b"); // 12 bayt, sabit ve kurgu export function paketle(dizin, atla = []) { // atla: plan disi birakilan kalem const adlar = readdirSync(dizin).sort().filter((a) => !atla.includes(a)); const govde = adlar.map((a) => readFileSync(join(dizin, a))); const bildirim = Buffer.from(JSON.stringify(adlar.map((a, i) => [a, govde[i].length]))); const bas = Buffer.allocUnsafe(4); bas.writeUInt32LE(bildirim.length, 0); return Buffer.concat([bas, bildirim, ...govde]); } export function ac(kap, dizin) { const n = kap.readUInt32LE(0); let p = 4 + n; mkdirSync(dizin, { recursive: true }); for (const [ad, boy] of JSON.parse(kap.subarray(4, 4 + n).toString())) { writeFileSync(join(dizin, ad), kap.subarray(p, p + boy)); p += boy; } } export function sifrele(kap) { const s = createCipheriv("aes-256-gcm", ANAHTAR, IV); return { govde: Buffer.concat([s.update(gzipSync(kap)), s.final()]), etiket: s.getAuthTag() }; } export function coz({ govde, etiket }, anahtar = ANAHTAR) { try { // bozuk arsiv ya da yanlis anahtar duser const d = createDecipheriv("aes-256-gcm", anahtar, IV).setAuthTag(etiket); return gunzipSync(Buffer.concat([d.update(govde), d.final()])); } catch { return null; } }
// tatbikat/tatbikat.mjs — GERCEKTEN kosar: kurgu uretim agaci diske yazilir, arsivlenir, // SILINIR ve geri yuklenir; birebir ayni oldugu ozetle dogrulanir. Sure basilmaz. import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { TOHUM, uret, ozet, paketle, ac, sifrele, coz, BASKA } from "./kap.mjs"; const KALEM = [["olcum.veri", 262144], ["is-emri.veri", 131072], ["tarife.veri", 65536], ["servis.yapilandirma", 2048], ["ad-bolge.kayit", 1024]]; // kurgu olcum agi agaci const ICERIK = KALEM.map(([ad, n]) => [ad, uret(n)]); const kok = mkdtempSync(join(tmpdir(), "tatbikat-")); const uretim = join(kok, "uretim"); const durum = () => readdirSync(uretim).sort().map((a) => [a, ozet(readFileSync(join(uretim, a)))]); function kos({ atla = [], boz = false, anahtar = undefined }) { rmSync(uretim, { recursive: true, force: true }); mkdirSync(uretim, { recursive: true }); for (const [ad, b] of ICERIK) writeFileSync(join(uretim, ad), b); const oncesi = durum(); let a = sifrele(paketle(uretim, atla)); if (boz) a = { ...a, govde: Buffer.concat([a.govde.subarray(0, 64), // tek bayt cevrilir Buffer.from([a.govde[64] ^ 1]), a.govde.subarray(65)]) }; rmSync(uretim, { recursive: true }); // uretim silindi: donus arsivden const kap = coz(a, anahtar); if (kap) ac(kap, uretim); const m = new Map(kap ? durum() : []); return { oncesi, acildi: !!kap, arsiv: a.govde.length, ayni: oncesi.filter(([ad, o]) => m.get(ad) === o).length }; } const t0 = process.hrtime.bigint(); const saglikli = kos({}); const sn = Number(process.hrtime.bigint() - t0) / 1e9; const bayt = ICERIK.reduce((a, [, b]) => a + b.length, 0); const geri = new Map(durum()); console.log(`${KALEM.length} kalem, ${bayt} bayt (tohum ${TOHUM}); arsiv ${saglikli.arsiv} bayt`); console.log("kalem | uretimdeki ozet (ilk 16) | geri gelen ayni"); console.log("--------------------|--------------------------|----------------"); for (const [ad, o] of saglikli.oncesi) console.log(`${ad.padEnd(19)} | ${o.slice(0, 16).padStart(24)} | ` + `${(geri.get(ad) === o ? "evet" : "hayir").padStart(15)}`); // MODEL: sira bozulunca kac adim bos veriyle acilir const BAGLI = { sir: [], veri: ["sir"], yapilandirma: [], servis: ["veri", "yapilandirma"] }; const YANLIS = ["servis", "veri", "yapilandirma", "sir"]; const bos = YANLIS.filter((a, i) => BAGLI[a].some((b) => YANLIS.indexOf(b) > i)).length; // MODEL: geri yukleme kimliginin gereken/verilen yetkileri const GEREKEN = { "yedek-deposu": "oku", "nesne-deposu": "yaz", "sir-deposu": "oku", "servis-yoneticisi": "baslat" }; const VERILEN = { "yedek-deposu": "oku", "sir-deposu": "oku" }; const yetki = Object.keys(GEREKEN).filter((k) => VERILEN[k] !== GEREKEN[k]); console.log("\ndeneme | acildi | birebir | eksik | yedek alma adimi"); console.log("---------------------------|--------|---------|-------|------------------"); let bulunan = 2; // iki MODEL kusuru bastan for (const [ad, secenek] of [["saglikli tatbikat", {}], ["eksik kalem (yapilandirma)", { atla: ["servis.yapilandirma"] }], ["bozuk arsiv (tek bayt)", { boz: true }], ["cozulemeyen sifre", { anahtar: BASKA }]]) { const r = kos(secenek), n = r.oncesi.length; if (r.ayni < n) bulunan += 1; console.log(`${ad.padEnd(26)} | ${(r.acildi ? "evet" : "hayir").padStart(6)} | ` + `${(r.ayni + "/" + n).padStart(7)} | ${String(n - r.ayni).padStart(5)} | ` + `${(r.ayni < n ? "basarili gorundu" : "basarili").padStart(16)}`); } console.log(`yanlis sira (MODEL) | ${bos}/${YANLIS.length} adim bos veriyle aciliyor` + `${" ".repeat(11)}| basarili gorundu`); console.log(`eksik yetki (MODEL) | ${yetki.length} kalem: ${yetki.join(", ")} | basarili gorundu`); console.log(`\ntatbikatin buldugu kusur ${bulunan}; yedek alma adiminda gorulen 0`); const mib = bayt / 1024 / 1024, [ALT, UST] = [0.5, 20000]; // MiB/s bandi; ham olcum basilmaz console.log(`yedekle+sil+geri yukle hizi ${ALT}-${UST} MiB/s bandinda: ` + `${mib / sn >= ALT && mib / sn <= UST ? "evet" : "hayir"}`); rmSync(kok, { recursive: true, force: true }); // gecici agac silinir
5 kalem, 461824 bayt (tohum 51703); arsiv 462113 bayt kalem | uretimdeki ozet (ilk 16) | geri gelen ayni --------------------|--------------------------|---------------- ad-bolge.kayit | b7c0e693cd032524 | evet is-emri.veri | 5c4512d9a8016d51 | evet olcum.veri | 3edf42c01d4f883f | evet servis.yapilandirma | 4ead0d5566a44d75 | evet tarife.veri | 434443d05cecee54 | evet deneme | acildi | birebir | eksik | yedek alma adimi ---------------------------|--------|---------|-------|------------------ saglikli tatbikat | evet | 5/5 | 0 | basarili eksik kalem (yapilandirma) | evet | 4/5 | 1 | basarili gorundu bozuk arsiv (tek bayt) | hayir | 0/5 | 5 | basarili gorundu cozulemeyen sifre | hayir | 0/5 | 5 | basarili gorundu yanlis sira (MODEL) | 2/4 adim bos veriyle aciliyor | basarili gorundu eksik yetki (MODEL) | 2 kalem: nesne-deposu, servis-yoneticisi | basarili gorundu tatbikatin buldugu kusur 5; yedek alma adiminda gorulen 0 yedekle+sil+geri yukle hizi 0.5-20000 MiB/s bandinda: evet
Birinci tablo bu dersin kanıtıdır: üretim ağacı yazıldı, arşivlendi, silindi ve yalnız arşivden geri getirildi; beş kalemin beşinde özetler aynı çıktı. Bir cümle değil, beş karşılaştırma.
İkinci tablonun son sütunu bulgudur. Kapsam dışı bırakılan yapılandırma geri gelmiyor ama arşiv açılıyor; tek baytı çevrilmiş arşiv ile başka anahtarla şifrelenmiş arşiv hiç açılmıyor ve beş kalem birden gidiyor. Yanlış sırada dört adımın ikisi boş veriyle açılıyor, kimlikte iki yetki eksik. Beş kusurun beşi de yedek alma adımında görünmedi.
Süre Bileşenlere Ayrılıyor
// tatbikat/sure.mjs — geri yukleme suresi bilesenlerine ayrilir, hedefle karsilastirilir ve // tatbikat sikligi taranir. MODELDIR; indirme suresi 05'in tam yedek boyutundan turer. const HEDEF = 240; // dakika; onceki kurstan girdi const TAM_GIB = 649.8, HAT_MIBS = 80; // tam yedek boyutu; hat hizi kurgudur const INDIRME = (TAM_GIB * 1024) / HAT_MIBS / 60; const BILESEN = { // [tatbikatsiz, tatbikatli] dakika bulma: [95, 12], indirme: [INDIRME, INDIRME], acma: [65, 38], dogrulama: [40, 15], "servisi acma": [85, 22], }; const g = (x, n, d = 1) => x.toFixed(d).padStart(n); const topla = (i) => Object.values(BILESEN).reduce((a, b) => a + b[i], 0); const PAY = HEDEF / Object.keys(BILESEN).length; // esit pay console.log(`hedef ${HEDEF} dk; tam yedek ${TAM_GIB} GiB, hat ${HAT_MIBS} MiB/s, esit pay ${PAY} dk.`); console.log("\nbilesen | tatbikatsiz | tatbikatli | hedefin yuzdesi"); console.log("-------------|-------------|------------|----------------"); for (const [ad, [y, t]] of Object.entries(BILESEN)) console.log(`${ad.padEnd(12)} | ${g(y, 11)} | ${g(t, 10)} | ${("%" + g((100 * t) / HEDEF, 6)).padStart(15)}`); console.log(`${"toplam".padEnd(12)} | ${g(topla(0), 11)} | ${g(topla(1), 10)} | ` + `${("%" + g((100 * topla(1)) / HEDEF, 6)).padStart(15)}`); const asan = Object.entries(BILESEN).filter(([, [, t]]) => t > PAY).map(([a]) => a); console.log(`tatbikatli surede esit payi asan ${asan.length} bilesen: ${asan.join(", ")}`); const DEGISIM = 3 / 30 + 1 / 90; // gunluk degisim: ayda 3 yapilandirma, ceyrekte 1 kalem const KALEM = 16, SAAT = 6; // envanter; bir tatbikat alti saat console.log("\nsiklik | yilda | yillik saat | dogrulanmamis degisiklik | geri yukleme | hedef"); console.log("----------|-------|-------------|--------------------------|--------------|-------"); for (const gun of [90, 180, 365, null]) { const n = gun ? Math.floor(365 / gun) : 0; const belirsiz = gun ? gun * DEGISIM : 365 * DEGISIM + KALEM; const sure = gun ? topla(1) : topla(0); console.log(`${(gun ? gun + " gunde" : "hic").padEnd(9)} | ${String(n).padStart(5)} | ` + `${String(n * SAAT).padStart(11)} | ${(g(belirsiz, 4) + (gun ? " kalem" : " kalem+kume")).padStart(24)}` + ` | ${g(sure, 12)} | ${(sure > HEDEF ? "asildi" : "tuttu").padStart(6)}`); }
hedef 240 dk; tam yedek 649.8 GiB, hat 80 MiB/s, esit pay 48 dk. bilesen | tatbikatsiz | tatbikatli | hedefin yuzdesi -------------|-------------|------------|---------------- bulma | 95.0 | 12.0 | % 5.0 indirme | 138.6 | 138.6 | % 57.8 acma | 65.0 | 38.0 | % 15.8 dogrulama | 40.0 | 15.0 | % 6.3 servisi acma | 85.0 | 22.0 | % 9.2 toplam | 423.6 | 225.6 | % 94.0 tatbikatli surede esit payi asan 1 bilesen: indirme siklik | yilda | yillik saat | dogrulanmamis degisiklik | geri yukleme | hedef ----------|-------|-------------|--------------------------|--------------|------- 90 gunde | 4 | 24 | 10.0 kalem | 225.6 | tuttu 180 gunde | 2 | 12 | 20.0 kalem | 225.6 | tuttu 365 gunde | 1 | 6 | 40.6 kalem | 225.6 | tuttu hic | 0 | 0 | 56.6 kalem+kume | 423.6 | asildi
Tatbikatsız düzende geri yükleme 423,6 dakika sürüyor, hedefi 183,6 dakika aşıyor; tatbikatlı düzende 225,6 dakikaya iniyor ve hedef 14,4 dakikalık payla tutuyor. Tatbikatın kısalttığı şey bilgidir: doğru noktayı bulma 95 dakikadan 12’ye, servisleri açma 85’ten 22’ye iniyor — prova edilmiş adımlar.
İndirme sütunu hiç değişmiyor: 138,6 dakika, hedefin yüzde 57,8’i ve eşit payı aşan tek bileşen. Bu süre bilgi eksikliğinden değil, 649,8 GiB’i 80 MiB/s’lik bir hattan geçirmekten geliyor; tatbikat ölçer ama kısaltmaz. Hedefi tutturmanın yolu daha çok tatbikattan değil, yedeği geri yükleneceği yere yaklaştırmaktan geçiyor. Sıklık taraması bedeli ve alternatifini yan yana koyuyor: doksan günde bir tatbikat yılda yirmi dört saat ve 10 kalemlik belirsizlik, yılda bir tatbikat altı saat ve 40,6 kalem. Hiç yapılmadığında bedel sıfır, belirsizlik on altı kalemin tamamı ve süre hedefi aşıyor.
Özet
- Yedek alma adımı beş kusurun beşinde de başarılı döndü; hepsi yalnız tatbikatta görüldü: kapsam dışı kalem, bozuk arşiv, çözülemeyen şifre, yanlış sıra, eksik yetki.
- Kanıt bir özet karşılaştırmasıdır: beş kalem yazıldı, arşivlendi, silindi, geri yüklendi ve özetler aynı çıktı.
- Geri yükleme süresi tatbikatsız 423,6, tatbikatlı 225,6 dakikadır; hedef yüzde 94 doluluğla tutar. Tatbikat dört bileşeni kısaltır, indirmeye dokunmaz: 138,6 dakika hedefin yüzde 57,8’i ve eşit payı aşan tek bileşendir.
- Tatbikat sıklığı bir bedel-belirsizlik takasıdır: doksan günde bir yılda 24 saat ve 10 kalem, hiç yapılmadığında 0 saat ve on altı kalemin tamamı.
Kurs Kapanışı
| Ders | Varsayılanın açık bıraktığı | Kapatılan / yanlış kapatılan | Kapatmanın getirdiği yeni yüzey |
|---|---|---|---|
| Web sunucusu rolleri | 18 kalem | 13 kapandı / 4 meşru iş | 3 yeni kalem |
| Ters vekil yapılandırması | 4 kayıp bilgi | 4 çözüldü / 2 kalem gizlendi | sahte başlık, 4 istek |
| Taşıma katmanı sonlandırma | 19 zayıf kalem | 19 kapandı / 65 cihaz | sertifika 6 noktada |
| Sertifika otomasyonu | 1.700 saat pencere | 0 saat / 12 gereksiz yenileme | 4 yeni kalem |
| Önbellek sunucusu | 179 sızan istek | 0 sızıntı / 2 kat yük | kaçan yayın, sessiz pencere |
| Servis ağı | 40 servise 40 dokunuş | 508 iş kalemi / 3 serviste pahalı | 162 yan araba kalemi |
| DNS yönetimi | 4 ölü, 2 devralınabilir kayıt | 5 dakikaya / 288 kat sorgu | bölgeye yazan kimlik |
| Gönderen doğrulama kayıtları | sahipsiz gönderim serbest | 5/6 sahte / 2 meşru düştü | 254 fazla yetkili adres |
| Alan adı politika kaydı | 61 sahte ulaşıyor | 49 engellendi / 43 meşru ayrıldı | 12 uyumlu taklit |
| Posta teslim sorunları | 12 puan tavan | tavan kalktı / 1.078 ileti kayıp | alıcı kararı ölçülemez |
| Güvenlik duvarı yapılandırması | 100 kalem, 107.265 deneme | 10 satır hepsi / 2 akış | dış satır, 20.650 deneme |
| Uzak erişim sertleştirme | 2.340 üçlü, 16.505 deneme | 16.505 kapandı / 0 meşru | 34 anahtar kopyası |
| Sır yönetimi sistemleri | 36 kalem, sonsuz pencere | 2,5 dakika / 9 kalem ömürsüz | tek nokta, 18 örnek |
| Yama yönetimi | 60 kalem, 28,94 gün | 1,22 güne / 4 geri alma, 82 kesinti | 15 kalem, yama ajanı |
| Yedekleme stratejisi | 13 kalem kapsam dışı | 3 kaleme / servis yine 2/4 | okunabilir yüzey iki katı |
| Geri yükleme tatbikatı | 5 kusur görünmez, 423,6 dk | 5 bulundu / 225,6 dk | 24 saat, 2 eksik yetki |
İkinci sütunda hiçbir satırda bir kararın izi yok: varsayılan bir karar değildir, kimsenin vermediği karardır. Üçüncü sütunda yanlış kapatma on altı satırın on dördünde doludur: hiçbir adım bedava değil. Dördüncü sütunda üç yüzey türü tekrar ediyor: yeni bir kimlik ya da yazma yetkisi beş derste, bütün yükü taşıyan tek nokta dört derste, kendisi yapılandırılması gereken yeni bir katman dört derste. Her savunma kendi yüzeyini getirir — kapatılan kalem yok olmaz, biçim değiştirir.
Yedi kurstur her katman ayrı ayrı kuruldu ve sertleştirildi. Tablodaki her satır bir kararlar dizisidir: hangi kapı, hangi eşik, hangi kalem. Ama bunların hepsini her ekibin yeniden kurması mı gerekiyor? Platform Mühendisliği bu soruyla başlıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.