Ders 12 / 16
On İki Etmen Uygulama İlkeleri
Bu bölümde alınan kararlar taşınabilirlik sözleşmesi olarak adlandırılmış on iki ilkeyle eşleştirilir; tek yapının iki ortamda değişmeden çalıştığı dosya özetiyle, hızlı açılış ve zarif kapanma ise uçuştaki isteğin tamamlanmasıyla ölçülür.
İçindekiler
Bu bölümde alınan kararlar birbirinden bağımsız gibi görünür: dizinleri katmanlara bölmek, yapılandırmayı ortamdan okumak, gizli değerleri maskelemek, kütüğü standart çıktıya yazmak, hataları tek bir katmandan geçirmek. Aralarında bir bağ vardır ve bağ tek bir hedefte toplanır: uygulamanın çalıştığı makineden bağımsız olması.
Bu hedefi adlandırılmış kurallar hâline getiren bir yöntem vardır: on iki etmen uygulama ilkeleri (twelve-factor app). İlkeler bir çatı ya da araç değil, bir sözleşmedir; uygulamayı yazan ile onu çalıştıran arasındaki arayüzü tanımlar. Bu ders ilkeleri sayar, bu bölümde hangilerinin karşılandığını ölçümlerle eşleştirir ve karşılanmayanları işaret eder.
Sözleşmenin Konusu
İlkelerin ortak varsayımı şudur: uygulamayı çalıştıran taraf, uygulamanın içini bilmeden onu kurabilmeli, ayarlayabilmeli, çoğaltabilmeli, izleyebilmeli ve durdurabilmelidir. Bu sağlandığında uygulama bir makineye değil, bir sözleşmeye bağlanır.
Kütüphane ödünç servisi açısından bunun karşılığı somuttur. Servisi bir dizüstü bilgisayarda çalıştıran geliştirici ile bir sunucuda çalıştıran işletmeci aynı dosyayı kullanır; aralarındaki tek fark verdikleri ortam değişkenleridir. Kod, hangi makinede olduğunu bilmez.
On iki ilke ve bu bölümdeki karşılıkları aşağıdaki gibidir.
| İlke | İçerik | Bu bölümdeki karşılığı |
|---|---|---|
| Kod tabanı | Tek kod tabanı, çok dağıtım | Sürüm Kontrolüne Giriş kursu |
| Bağımlılıklar | Açıkça bildirilir ve yalıtılır | Modüller, Araçlar ve Ekosistem kursu |
| Yapılandırma | Ortamda saklanır | Yapılandırma Yönetimi dersi |
| Destekleyici servisler | Bağlı kaynak sayılır | Yerel Geliştirme Ortamı dersi |
| Derle, yayımla, çalıştır | Aşamalar kesin ayrılır | Oluşturma Stratejileri ve Altyapı kursu |
| Süreçler | Durumsuz ve paylaşımsız | Uygulama Çalışma Zamanı dersi |
| Bağlantı noktası bağlama | Hizmet kendi bağlar | Web Sunucusu Kavramı dersi |
| Eşzamanlılık | Süreç modeliyle ölçeklenir | Uygulama Çalışma Zamanı dersi |
| Tek kullanımlık olma | Hızlı açılış, zarif kapanma | Bu ders |
| Geliştirme/üretim eşliği | Ortamlar birbirine yakın tutulur | Bu ders |
| Kütükler | Olay akışı sayılır | Günlükleme Kurulumu dersi |
| Yönetim süreçleri | Tek seferlik iş olarak çalıştırılır | Backend Geliştirme müfredatının ileri kursları |
Tabloda dört ilke bu kursun dışına düşer; ikisi bu dersin ölçümleridir.
Ölçüm: Tek Yapı, İki Ortam
Geliştirme/üretim eşliği ilkesi, aynı yapının farklı ortamlarda değişmeden çalışmasını ister. Bunun sınanabilir biçimi şudur: dosyanın özeti değişmeden, davranışı ortam değişkenleriyle değişmelidir.
// kaynak/http/sunucu.mjs — ayni dosya her ortamda; baglanti noktasini kendi baglar, // kutugu standart cikisa yazar, SIGTERM aldiginda ucustaki istegi bitirip iner. import { createServer } from "node:http"; import { setTimeout as bekle } from "node:timers/promises"; const BAGLANTI_NOKTASI = Number(process.env.KUTUPHANE_BAGLANTI_NOKTASI ?? 8440); const ORTAM = process.env.KUTUPHANE_ORTAM_ETIKETI ?? "yerel"; const ODUNC_GUN = Number(process.env.KUTUPHANE_ODUNC_GUN ?? 14); const kutuk = (kayit) => console.log(JSON.stringify({ an: new Date().toISOString(), ...kayit })); let acikIstek = 0; const sunucu = createServer(async (istek, yanit) => { yanit.sendDate = false; acikIstek++; const yol = new URL(istek.url, "http://yerel").pathname; if (yol === "/odunc") await bekle(1500); // suren bir odunc islemi const metin = JSON.stringify({ ortam: ORTAM, oduncGun: ODUNC_GUN, yol }); yanit.writeHead(200, { "content-type": "application/json; charset=utf-8", "content-length": Buffer.byteLength(metin) }); yanit.end(metin); acikIstek--; }); sunucu.listen(BAGLANTI_NOKTASI, "127.0.0.1", () => kutuk({ olay: "acildi", baglantiNoktasi: BAGLANTI_NOKTASI, ortam: ORTAM, oduncGun: ODUNC_GUN, acilisMs: Number(performance.now().toFixed(1)), // surec baslangicindan bu yana })); process.on("SIGTERM", () => { kutuk({ olay: "sigterm_alindi", acikIstek }); sunucu.close(() => { // yeni baglanti yok, acik olanlar biter kutuk({ olay: "kapandi", acikIstek }); process.exit(0); }); sunucu.closeIdleConnections(); // bos bekleyen baglantilar hemen kapanir setTimeout(() => { kutuk({ olay: "zorla_kapandi" }); process.exit(1); }, 5000).unref(); });
#!/usr/bin/env bash # Ayni dosyayi iki ortamla calistirir; dosyanin ozeti degismeden davranisin degistigini gosterir. echo "dosya ozeti: $(shasum -a 256 kaynak/http/sunucu.mjs | cut -c1-16)…" dene() { # $1 = ortam etiketi, $2 = odunc gunu, $3 = baglanti noktasi KUTUPHANE_ORTAM_ETIKETI="$1" KUTUPHANE_ODUNC_GUN="$2" KUTUPHANE_BAGLANTI_NOKTASI="$3" \ node kaynak/http/sunucu.mjs > "kutuk-$1.jsonl" 2>&1 & s=$! sleep 0.7 printf '%-8s -> ' "$1" curl -sS "http://127.0.0.1:$3/kitaplar"; echo kill -TERM "$s"; wait "$s" 2>/dev/null grep -o '"acilisMs":[0-9.]*' "kutuk-$1.jsonl" rm -f "kutuk-$1.jsonl" } dene yerel 14 8441 dene yayin 21 8442
dosya ozeti: cc28586133fd7c6a…
yerel -> {"ortam":"yerel","oduncGun":14,"yol":"/kitaplar"}
"acilisMs":28.5
yayin -> {"ortam":"yayin","oduncGun":21,"yol":"/kitaplar"}
"acilisMs":28.2
Dosya özeti ve açılış süreleri her makinede ve her düzenlemeden sonra değişir; özet, dosyanın iki çalıştırma arasında değişmediğini göstermek için buradadır.
Aynı dosya iki ortamda iki farklı yanıt üretmiştir: ödünç süresi bir ortamda on dört, diğerinde
yirmi bir gündür. Kodda hiçbir dallanma yoktur; ortamın adını taşıyan bir koşul, bir
if (ORTAM === "yayin") satırı bulunmaz. Böyle bir satır eklendiği anda ilke kırılır, çünkü
uygulama artık nerede çalıştığını bilir ve yerelde sınanan yol ile yayında çalışan yol
ayrışır.
Aynı çıktı bir başka ilkeyi de doğrular. Bağlantı noktası bağlama ilkesi, hizmetin dış bir
bileşene ihtiyaç duymadan kendi bağlantı noktasını açmasını ister. curl çağrısı doğrudan
sürece gitmiştir; arada hiçbir bileşen yoktur. Önceki bölümde kurulan ters vekil bir seçenektir,
bir zorunluluk değil — ve tam olarak bu yüzden yerel geliştirme ile yayın aynı kodu çalıştırır.
Ölçüm: Tek Kullanımlık Olma
Tek kullanımlık olma (disposability) ilkesi süreçlerin hızlı açılmasını ve düzgün inmesini ister. İkisi de aynı gerekçeye dayanır: süreçler sık başlatılır ve sık durdurulur. Yayın, ölçek değişikliği, makine değişimi ve çökme sonrası yeniden başlatma bunu zorunlu kılar.
Açılış süresi yukarıdaki çıktıda görülür: süreç başlangıcından dinlemeye geçişe kadar otuz
milisaniyenin altı. Asıl sınanması gereken ise inişin nasıl olduğudur. Aşağıdaki ölçüm,
uçuşta bir istek varken sürece SIGTERM gönderir.
#!/usr/bin/env bash # Ucusta bir istek varken SIGTERM gonderir; istegin bitip yeni baglantinin reddedildigini olcer. node kaynak/http/sunucu.mjs > kutuk.jsonl 2>&1 & surec=$! sleep 0.7 curl -sS -o /dev/null -w 'ucustaki istek : %{http_code} %{time_total} s\n' \ http://127.0.0.1:8440/odunc > ucus.txt & istek=$! sleep 0.4 echo "SIGTERM gonderildi" kill -TERM "$surec" sleep 0.4 printf 'kapanma sirasinda yeni istek : ' if curl -s -o /dev/null --max-time 2 http://127.0.0.1:8440/kitaplar; then echo "yanit alindi" else echo "baglanti kurulamadi (curl cikis kodu=$?)" fi wait "$istek"; cat ucus.txt wait "$surec"; echo "surec cikis kodu=$?" echo "--- kutuk ---" cat kutuk.jsonl rm -f kutuk.jsonl ucus.txt
SIGTERM gonderildi
kapanma sirasinda yeni istek : baglanti kurulamadi (curl cikis kodu=7)
ucustaki istek : 200 1.505712 s
surec cikis kodu=0
--- kutuk ---
{"an":"2026-07-28T20:20:44.905Z","olay":"acildi","baglantiNoktasi":8440,"ortam":"yerel","oduncGun":14,"acilisMs":25.9}
{"an":"2026-07-28T20:20:45.986Z","olay":"sigterm_alindi","acikIstek":1}
{"an":"2026-07-28T20:20:47.101Z","olay":"kapandi","acikIstek":0}
Zaman damgaları ve süreler her çalıştırmada değişir.
Üç gözlem sabittir. Uçuştaki istek tamamlanmıştır: sinyal isteğin ortasında gelmiş,
buna rağmen yanıt 200 olarak dönmüş ve bir buçuk saniyelik iş kesilmemiştir. Kütükteki
sigterm_alindi satırı o anda bir açık istek bulunduğunu, kapandi satırı ise kapanma
tamamlandığında sıfır açık istek kaldığını yazar.
Yeni bağlantı reddedilmiştir. Sinyalden sonra yapılan istek bağlantı bile kuramamıştır;
sunucu.close çağrısı dinleyen soketi kapatmış, ama açık bağlantıları kesmemiştir. Kapanan
bir süreç için doğru davranış budur: yeni iş alma, aldığın işi bitir.
Çıkış kodu sıfırdır. Bir denetçi bu kodu gördüğünde sürecin isteyerek indiğini bilir; sıfırdan farklı bir kod ise beklenmedik bir sonlanmayı bildirir ve farklı bir tepki gerektirir.
Zorla kapanma zamanlayıcısı bu düzenin kaçınılmaz tamamlayıcısıdır. Bitmeyen bir istek
süreci sonsuza kadar bekletebilir; beş saniyelik sınır dolduğunda süreç sıfırdan farklı bir
kodla iner. unref çağrısı bu zamanlayıcının, kapanma erken tamamlandığında sürecin ömrünü
uzatmasını engeller.
Karşılanmayanlar
İlkeler listesinin dürüst okunması, karşılanmamış olanları da saymayı gerektirir.
Destekleyici servisler ilkesi henüz uygulanmadı. Bu bölümdeki depolar süreç belleğindeydi; bir veritabanının ya da dış servisin yalnız bir adresle tanımlanan, değiştirilebilir bir bağlı kaynak olarak ele alınması, bu kursun Yerel Geliştirme Ortamı dersinde kurulur.
Yönetim süreçleri ilkesi de açıktır. Veri göçü, toplu düzeltme, rapor üretimi gibi tek seferlik işlerin uygulamanın kendisiyle aynı kod ve aynı ortamla çalıştırılması gerekir; ayrı bir betikle yapıldığında iki farklı gerçeklik oluşur.
Derle, yayımla, çalıştır ayrımı bu bölümde ölçülmedi; yapı çıktısının ortam değişkenlerinden ayrı tutulması ve her yayının geri alınabilir bir kimlik taşıması Oluşturma Stratejileri ve Altyapı kursunda ele alınmıştı ve sunucu tarafındaki karşılığı ileri kurslardadır.
Bir ilkeyi karşılamamak kusur değildir; karşılamadığını bilmemek kusurdur. Listenin işlevi budur — kararların hangisinin alınmış, hangisinin ertelenmiş olduğunu ada bağlar.
Özet
- On iki etmen ilkeleri bir araç değil, uygulamayı yazan ile çalıştıran arasındaki sözleşmedir; ortak hedefleri uygulamayı çalıştığı makineden bağımsız kılmaktır.
- Geliştirme/üretim eşliği, dosya özeti değişmeden davranışın ortam değişkenleriyle değişmesiyle ölçüldü; kodda ortam adına bakan hiçbir dallanma yoktur.
- Bağlantı noktası bağlama ilkesi gereği hizmet kendi soketini açar; ters vekil bir seçenektir, önkoşul değildir.
- Tek kullanımlık olma ölçümünde
SIGTERMsonrası uçuştaki istek200ile tamamlanmış, yeni bağlantı reddedilmiş ve süreç sıfır çıkış koduyla inmiştir; zorla kapanma zamanlayıcısı bitmeyen isteğe karşı sınırı korur. - Destekleyici servisler, yönetim süreçleri ve derle-yayımla-çalıştır ayrımı bu bölümde karşılanmadı; listenin işlevi ertelenmiş kararları adlandırmaktır.
Sonraki Adım
İskelet artık ayakta: yapılandırma ortamdan okunuyor, gizli değerler ayrılmış, kütük standart çıktıya yazılıyor, hatalar ortak bir biçimde yanıta dönüşüyor ve süreç düzgün inip kalkıyor. Buna karşılık uygulama dışarıya hâlâ hiçbir gerçek içerik vermiyor; verdiği tek şey kendi durumu. Sonraki bölüm bu boşluğu doldurmaya en yalın olandan başlar: diskte duran bir dosyayı ağ üzerinden vermek. Sonraki ders o dosyanın hangi koşulla okunmasına izin verildiğini, alıcının içeriği nasıl yorumlayacağını, aynı dosya ikinci kez istendiğinde ne olacağını ve bu yükün sunucuda kalıp kalmayacağını kurar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.