2011’de The Twelve-Factor App
(12factor.net)- The Twelve-Factor App, web uygulamaları ve SaaS’ı uzun vadede işletmek ve ölçeklendirmek için bir metodolojidir; yapılandırma otomasyonu, taşınabilirlik, bulut dağıtımı ve sürekli dağıtımı birlikte ele alır
- Belirli bir programlama diline ya da veritabanı, kuyruk, bellek önbelleği gibi destekleyici servis kombinasyonlarına bağlı olmadığı için çeşitli hizmet tipi uygulamalara uygulanabilir
- Heroku platformunda yüzlerce uygulamanın geliştirme ve dağıtımına doğrudan katılma, yüz binlerce uygulamanın geliştirme, işletme ve ölçeklendirmesini dolaylı olarak gözlemleme deneyimine dayanır
- Temel sorun bilinci, uygulamalar organik olarak büyüdüğünde ortaya çıkan iş birliği maliyetlerini ve yazılım aşınmasını azaltacak ortak bir söz dağarcığı sunmaktır
- Hizmet uygulamaları geliştiren geliştiricilerin yanı sıra bunları dağıtan ve yöneten operasyon mühendisleri için de pratik bir standart olarak kullanılabilir
SaaS uygulamaları için 12 işletim ilkesi
- Modern yazılım çoğu zaman web uygulaması veya SaaS biçiminde sunulur; Twelve-Factor App, bu tür uygulamaları geliştirmeye yönelik bir metodolojidir
- Amaç, uygulamanın geliştirme, dağıtım ve işletim süreçlerini daha öngörülebilir kılmaktır
- Yeni geliştiricilerin projeye katılırken harcadığı zamanı ve maliyeti azaltmak için bildirimsel yapılandırma otomasyonu kullanır
- Temel işletim sistemiyle açık bir sözleşme kurarak çalışma ortamları arasındaki taşınabilirliği artırır
- Sunucu ve sistem yönetimi yükünü azaltır ve modern bulut platformu dağıtımlarına uygun tasarlanır
- Geliştirme ortamı ile üretim ortamı arasındaki farkları azaltarak sürekli dağıtımı mümkün kılar
- Araçları, mimariyi ve geliştirme pratiklerini büyük ölçüde değiştirmeden ölçeklenebilir olmasını sağlar
- Uygulama kapsamı belirli bir teknoloji yığınıyla sınırlı değildir
- Herhangi bir programlama diliyle yazılmış uygulamalara uygulanabilir
- Destekleyici servisler veritabanı, kuyruk, bellek önbelleği vb. içerir
Heroku deneyiminden derlenen arka plan
- Katkıda bulunanlar, Heroku platformunda yüzlerce uygulamanın geliştirme ve dağıtımına doğrudan katıldı; yüz binlerce uygulamanın geliştirme, işletme ve ölçeklendirmesini dolaylı olarak gözlemledi
- Gerçek SaaS uygulamalarından elde edilen deneyim ve gözlemlere dayanarak uygulama geliştirmenin ideal pratiklerini derler
- Zaman içinde uygulamaların organik olarak büyüme akışına odaklanır
- Birden fazla geliştiricinin aynı kod tabanında nasıl iş birliği yaptığını ele alır
- Yazılım aşınması maliyetinden kaçınmayı önemli bir hedef olarak görür
- Biçim, Martin Fowler’ın Patterns of Enterprise Application Architecture ve Refactoring eserlerinden ilham alır
12 unsur
- I. Codebase: Sürüm kontrolündeki tek bir kod tabanı ve birden çok dağıtım bulunur
- II. Dependencies: Bağımlılıklar açıkça beyan edilir ve izole edilir
- III. Config: Yapılandırma ortamda saklanır
- IV. Backing services: Destekleyici servisler bağlı kaynaklar olarak ele alınır
- V. Build, release, run: Derleme aşaması ile çalıştırma aşaması kesin biçimde ayrılır
- VI. Processes: Uygulama bir veya daha fazla durum bilgisiz süreç olarak çalıştırılır
- VII. Port binding: Servisler port bağlama yoluyla dışarıya sunulur
- VIII. Concurrency: Süreç modeliyle yatay ölçeklendirme yapılır
- IX. Disposability: Hızlı başlatma ve zarif kapatma ile sağlamlık artırılır
- X. Dev/prod parity: Geliştirme, staging ve üretim mümkün olduğunca benzer tutulur
- XI. Logs: Loglar olay akışı olarak ele alınır
- XII. Admin processes: Yönetim işleri tek seferlik süreçler olarak çalıştırılır
1 yorum
Hacker News yorumları
12-Factor App, 2011'de Heroku'ya ve o dönemin konteyner tabanlı altyapısının sınırlamalarına daha çok yaslanarak hazırlanmış bir öneriler dizisi; mühendislik ilkelerine derinden dayanan bir belge gibi görünmüyor
Örneğin yapılandırmayı ortam değişkenlerine koyma iddiası, yazarların Heroku'da çalışması ve Heroku'nun web uygulamalarındaki giriş alanlarını ortam değişkenleriyle dolduran bir yöntem kullanması yüzündendi
Yapılandırma geçmişini sürüm kontrolüyle izlemek, GitOps kullanmak, k8s ConfigMap kullanmak ya da yapılandırma dosyalarını mount edilmiş bir volume'de tutmak istiyorsanız bunların hepsi genel olarak gayet makul seçimlerdir. Çünkü yapılandırma durumunu uygulama dağıtım durumundan ayırırlar
Bu belge ormanı ağaçlarla karıştırıyor ve gerçek mühendislik ilkelerinden çok, belgeyi yazan şirketin ürün özelliklerine göre önerilerde bulunuyor; bu yüzden bunu zararlı bir kılavuz olarak görüyorum
Kubernetes'te güvenli yapılandırma depolaması istiyorsanız sonunda Secrets kullanırsınız; bu da ortam değişkenleri gibi anahtar-değer biçimindedir. Git'te benzer güvenliği elde etmek için bir şifreleme katmanı gerekir; o zaman da diff bozulur ve ek araçlara ihtiyaç duyulur
Sonunda Heroku gibi üst seviye dağıtım araçlarının neden ortaya çıktığı sorusuna geri dönülür
Logları stream olarak ele alma ilkesi hâlâ doğru. Dosyaya değil STDOUT'a log yazarsınız; orchestrator da bunları okur ve saklar
Yapılandırma ortamdan alınır. Uygulamalar, dağıtım biçimine göre yerelde .env, production'da gizli değer deposu gibi farklı kaynaklardan yapılandırma okuma eğilimindedir
Port binding de benzer: uygulama portu açar, önüne nginx gibi bir şey koyup reverse proxy yapılandırırsınız. K8S service ve ingress bu rolü üstlenir
Bugün 12-Factor'a getirilebilecek en büyük eleştiri, belgenin aslında iyi yazılmamış olması ve okuyucunun ne söylendiğini zaten tam olarak bildiğini varsaymasıdır
Yine de Heroku'nun bu kavramı yaygınlaştırıp kullanımını artırmadaki payını teslim ediyorum
Uygulamayı başlatmadan önce makineye settings.json eklemek değil; aynı kaynak Azure EU north'taki bir AKS kümesine dağıtıldığında o kümede ayarlanmış değerleri, Ikea çerçevesinin içindeki RPi Zero Docker Swarm kümesine dağıtıldığında ise o kümenin yapılandırmasını kullanması gibi
O kümenin adı Gibson
Bu maddelerin her birine oldukça makul karşı argümanlar getirilebileceğini düşünüyorum
Birincisi, tek uygulamaya tek depo ilkesi temelden yanlış değil. İşlevsel olarak güçlü biçimde bağlı olan ve release döngüsünü paylaşan, ama ayrı process'ler ve bağımsız ölçeklenmenin avantajlarını elde etmek için ayrı dağıtılması gereken birden çok uygulamanın tek depoda geliştirilmesinde sorun yok. Public API ile worker process'in ayrılması; örneğin Ruby'de Sidekiq, Python'da Celery, sıradan Kafka consumer'ları akla geliyor
İkincisi, “12-Factor uygulama sistem genelindeki paketlerin örtük varlığına dayanmaz” sözünü Nix gibi bir şey kullanmadan gerçekten başarmak çok zor. Kernel system call API bağımlılıkları da dışarı sızar ve çoğu Rust uygulaması, musl hariç, örtük olarak glibc'ye bağımlıdır. Alpine gibi slim dağıtımlar dışında başlıca Linux dağıtımlarında bulunur. Docker dayanağının da bu sorunu hafifletmek için gerekli olduğunu düşünüyorum
Üçüncüsü, yapılandırmayı ortam değişkenlerinde saklamak daha kırılgan görünüyor. Gizli değerleri ortama koymaya zorlayarak güvenliği zayıflatabilir; type safety, IDE autocompletion ve otomatik parsing sağlayan yapılandırılmış dosya ayarlarından vazgeçirir. Ortam değişkenlerinde string olmayan karmaşık yapılandırma değerleri için kendi parser'ınızı yazmanız gerekir. Pratikte bunların çoğu .env dosyası biçiminde depoya commit edildiğinden, commit güvenliği argümanı da anlamsızlaşır
glibc dahil işletim sistemine bağımlı olmayın demek değil; belirli bir dil paket yöneticisi paketinin makineye kurulmuş olmasını zorunlu hale getirmeyin demek
Diğer eleştirilere katılıyorum. Özellikle ortam değişkenleri konusu, gerçek dayanağı olan bir tavsiyeden çok, o dönemde Ruby geliştirmede yaygın olan yöntemi yazarların en iyi yöntem varsayıp yazmasına yakın
Bu yüzden böyle bir duruma hazırlıklı olmak gerekir; farklı sürüm kombinasyonlarını kolayca test etmek için de ayrı depoların daha iyi olduğunu düşünüyorum
Üst seviye dillerin çoğu belirli bir glibc'ye bağımlı değildir. İlgili dil runtime'ı düzgün çalışıyorsa uygulama da onun üzerinde çalışır. Elbette bazı durumlarda Docker gibi şeyler kullanırsınız. Zor olması, değersiz olduğu anlamına gelmez
Dinamik diller veya yönetilen diller, sistem paketleriyle ilgili zahmeti çoğu zaman büyük ölçüde görmezden gelebilir
Genel olarak hoşuma gidiyor, ama teknik olmayan ya da yarı teknik kişilerin “12 factor”ı her derde deva bir sarı kart gibi çıkarıp sürümleri geciktirdiği o kadar çok oldu ki neredeyse tamamen görmezden gelmeye başladım.
Aslında “agile” da benzerdi. Bu tür kılavuzların niyetini anlıyorum, ama pratik değeri, fildişi kule tarzı teknik liderlik dışında pek bir şey sunamayan insanlar için çok daha faydalı görünüyor.
Aşırı hevesli junior mühendislerin ya da mimar adayı kişilerin 12-Factor yazısını her yayının zorunlu gereksinimi gibi kullandığı durumlar yaşadım.
Bunların peşinden gidilmeye değer iyi hedefler olduğunu, ama gerçek hayatta yayın yapmak için taviz vermek ve hangi kısımların yavaşlatılacağına ya da erteleneceğine karar vermek gerektiğini kararlı ve tutarlı biçimde anlatmak gerekiyor.
Küçük bir sapma yüzünden sürümü engellemezsiniz, ama genel olarak uymuyorsa en azından teknik borç olarak ele alınmalı. Bir sürüm bir maddede ciddi biçimde geriye gidiyorsa bunu engellemek ya da en azından o ödünün neden değerli görüldüğüne dair daha ayrıntılı bir incelemeyi zorunlu kılmak adil olur.
Kuruluş 12FA’yı en iyi uygulama olarak benimsediyse karşılanmalıdır, ama dağıtımı engellememelidir.
12FA işaretlenecek tek bir kutu değildir. Ürün olgunlaştıkça her maddeyi tek tek, gerekirse daha küçük parçalara bölerek ürüne ekleyecek şekilde uygulanabilir ve çoğu zaman öyle uygulanmalıdır.
Başta iyi mühendislik yaptıysanız, yani uygun soyutlamalar ve arayüzler koyup her şeyi hardcode etmediyseniz sorun olmamalı.
YAGNI de 12FA kadar kötüye kullanılıyor. 12FA’daki en büyük eksik, junior mühendislerin başvurabileceği somut örnekler.
Bu tür sorunları iyi niyetle ele almayı öğrendim. Neden itiraz edildiğine, mevcut sürecin belirsiz mi, kusurlu mu, yoksa güvenilirliğinin mi düşük olduğuna bakmak gerekiyor.
Makul bir kaygı bulunursa süreç değiştirilir ve dokümantasyon da güncellenir.
Twelve-Factor App yapılandırma için ortamı kullanmayı söyler, Docker ise yapılandırma için ortamı kullanmayın der. Çünkü güvenli değildir.
12-Factor’daki birçok kalıbı seviyor ve kullanıyorum, ama bazıları VPS bağlamında ortaya çıkmıştı. O zamanlar ortam daha istikrarlı, güvenli ve daha sabitti; fakat container’larda ortam herhangi bir katmana taşınmış olabilir.
Container çağında bu özel madde epey düşündürücüydü. Docker secrets da her zaman tam uymuyor; çalıştırmak için çeşitli cambazlıklar gerekebiliyor.
Gizli değerleri ortama değişken olarak uygulamaya enjekte etmek, bunun dışındaki işlenişin de güvensiz olduğu anlamına gelmez. Örneğin AWS ECS container’ları başlangıçta Secret Manager’dan gizli değerleri alıp ortam değişkeni olarak aktarmak için yerleşik desteğe sahip: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/...
Gizli değerler container başlatılırken alınır ve çalışan uygulamanın IAM kimlik bilgileriyle Secret Manager’dan çekilir. Dolayısıyla söz konusu gizli değer için yetkiniz olması gerekir.
Ortam değişkeni kullanmanın avantajı, sonunda nasıl ayarlandığına tamamen bağlı görünüyor. Böyle bir mekanizmada büyük bir dezavantaj pek göremiyorum.
Görebildiğim başlıca dezavantaj, ortam değişkenlerini dump etmeye çalışan genel amaçlı kötü amaçlı yazılımların değeri yakalayabilmesi; ama gizli değeri bellekte kalıcı olarak saklamıyorsanız, bu tehdidi engellemek çoğunlukla obfuscation’a yakın.
Özel anahtarların bu şekilde açığa çıkmasından gerçekten kaçınmak gerekir.
k8s ile gizli değerleri dosya sistemine mount etme yönteminde pek sorun görmüyorum. Elbette bunların hepsi dağıtım ortamına bağlı.
Bazen ortam değişkenleri daha az kötü seçenektir. Çünkü gizli değerler doğası gereği her zaman zordur.
Tek kiracılı olduğu için girdiye körü körüne güvenmek, ileride rastgele bir iş yönü değişikliğiyle koşullar değiştiğinde izlenmesi zor hatalara ve uzun gecelere yol açar.
Son birkaç yılda 12-Factor uygulamaları üzerine çok konuştum ve çok fazla kafa karışıklığı da gördüm. 12-Factor sitesi harika, ama daha çok maddelerin neden önemli olduğunu zaten anlayan kişiler için iyi.
Kuralların arkasındaki nedeni bilmeyenler için daha derin bir açıklama gerekiyordu. Bu yüzden “What are 12 Factor Apps and Why Should You Care?”[1] videosunu hazırladım; bazı şirketlerin bunu mühendis/DevOps yeni işe başlayan eğitimlerinde kullandığını ve çok faydalı bulduklarını duydum.
Nereden öğrenirseniz öğrenin, 12-Factor uygulamaları için bir iki saat ayırıp öğrenmeye değer. “Kuralların” çoğu farkındalık gerektiren şeylerdir; kendiniz hata yapıp acı çekerek öğrenmeden hemen açık seçik görünmezler.
[1] https://youtu.be/REbM4BDeua0
“Birim testi” kavramı 90’ların sonlarına doğru traction kazanmaya başladı ve otorite sahibi birinin bunu savunması rahatlatıcıydı.
Kent Beck JUnit’i yayımlayana kadar bunu kendim yapmamamın nedeni, üzerinde çalıştığım kodun başka kod tarafından kontrol edilmeye uygun bir yapıda olmamasıydı. Global değişkenler, geniş alana yayılmış durum, dış sistemlere ve belirli dosya sistemi yerleşimlerine bağımlılık, modülerliğin göz ardı edilmesi yüzünden tasarlandığı bağlamın dışında hiçbir şey çalıştırılamıyordu.
Bunların hepsi “kötü tasarım”dı ama teslim tarihleri tutuyordu; bu yüzden herkes böyle yapıyordu. Birim testleri traction kazanınca programcıların monolitik tasarımdan uzaklaşmasını ummuştum.
25 yıl boyunca getter/setter için yazılmış birim testleri ve uygulamadaki her fonksiyonun sadece çalıştırılabilmesi için bile canlı veritabanı gerektiğinden bellek içi veritabanı oluşturan devasa tek bir birim testi görüp, sonunda bunun başarısız olup yorum satırına alındığını gördükten sonra, birim testlerinin anlamsız bir onay kutusundan fazlası olacağına dair inancımı kaybettim. Herkes “en iyi uygulama” olduğu için kutuyu işaretliyor, ama neden yaptığını durup düşünmüyor.
Konfigürasyon tavsiyesine her zaman en az katılanlardan oldum. Konfigürasyon birden fazla aktör tarafından tanımlanır ve çoğu zaman geliştiriciler de tanımlar; bu yüzden uygulamayla birlikte makul varsayılanlar göndermek ve bunların ortam bazlı dosyalar ile ortam değişkenleriyle üzerine yazılmasına izin vermek çoğu zaman en iyi yaklaşımdır.
Çoğu sunucu tarafı uygulaması için en esnek yöntem budur. Hangi konfigürasyonun olması gerektiğini çoğu zaman zaten bilirsiniz ve bunun kaynak kontrolünde olması iyidir; ama gizli değerler çalışma zamanında enjekte edilmelidir.
Geliştirme, test ve prod ortamlarında muazzam konfigürasyon süresi harcamamak için konfigürasyonda hiyerarşik üzerine yazma gerekir.
Geliştirme içinse eninde sonunda prod’u bozacaktır; prod varsayılanları ise geliştirmede hiçbir anlam ifade etmeyebilir.
Konfigürasyon bölümünü düşünmenin bir yolu şudur: “Bu konfigürasyon stratejisi container’larla iyi çalışıyor mu?” Bir imaj build ettiğinizde, yeni bir imaj oluşturmadıkça değişikliklerin kalıcı olmadığı statik bir disk durumu ortaya çıkar.
Konfigürasyon yalnızca dosya tabanlıysa, test ve prod davranışını değiştirmek için tamamen yeni bir imaj build etmeniz gerekir.
Konfigürasyonu temel diskten bağımsız olarak değiştirebilmek, değişiklikleri izole etmeye yardımcı olur. Uygulamanın bozulma nedeni dağıtımın, yani imaj oluşturmanın bozulması mı, yoksa konfigürasyonun yanlış olması mı, bunu ayırt etmeniz gerekir.
İmaj oluşturma ile konfigürasyon değişikliğini ayırınca bu soru baştan ortadan kalkar.
Örneğin QA’dan prod iş gönderim kuyruğuna 100 bin kimlik doğrulama reddi yeniden denemesi göndermek gibi yeterince öngörülebilir bir insan hatasına izin verilmemeli.
Öte yandan birçok konfigürasyon altyapıyla ilgili değil; örneğin her ortamda hangi ResolverStrategy bean’inin bağlanacağı gibi meselelerle ilgili oluyor.
Konfigürasyon sürüm kontrolünde olmalı, ancak kaynak koddan ayrı yönetilmelidir. Çünkü konfigürasyon, dağıtımda kullanılan imajı değil, dağıtımın kendisini tanımlar.
Kesinlikle etkili bir mühendislik normuydu. Render veya Vercel gibi hosting hizmetlerinin kolay soyutlamaları bugün bolca var; bu belgenin 2012’de yazıldığını ve o dönemde web uygulamalarının kabul görmüş ortak pratikler açısından çok daha vahşi batı gibi olduğunu düşününce biraz tuhaf hissettiriyor.
Bu belgede büyük ölçüde eksik olan şey, kuralların gerekçelendirilmesi. Neredeyse tamamı sadece kural.
Kuralların iyi olup olmadığını değerlendirmek zor ve bu belge bunu anlamaya yardımcı olmuyor.
Başlığı görünce bunun iki faktörlü kimlik doğrulama hakkında bir yorum olduğunu sandım. Tek bir giriş için pasaport fotoğrafı, yüz taraması, ehliyet, SMS mesajı, Google Authenticator, e-posta bağlantısı, parola ve parmak izi isteyen uygulamalar gibi.
Docker’ın ilk dönemlerinde WordPress’i Twelve-Factor App gibi davranacak hale getirmek için epey çalışma yapmıştım.
Geleneksel olarak WordPress böyle davranmıyordu ve bir ölçüde bu anlaşılır. Çünkü WordPress, yazılabilir ve kalıcı yerel diske sahip uzun ömürlü sunucuların yaygın olduğu bir dünyada büyüdü.
O zamandan beri işler çok değişmiş olmalı. 2016 civarından bahsediyorum ama gerçekten ilginç bir meydan okumaydı.
İşlerin ne kadar basitleştiğine şaşırmıştım; session stickiness ile uğraşmadan birden çok node’u yük dengeleyebilir hale gelmiştik.
Elbette oturumlar için ayrı bir DB gerekmesi gibi başka açılardan işler zorlaşmıştı.