2011 का The Twelve-Factor App
(12factor.net)- The Twelve-Factor App वेब ऐप्स और SaaS को लंबे समय तक चलाने और स्केल करने की एक methodology है, जो configuration automation, portability, cloud deployment और continuous deployment को साथ में संबोधित करती है
- यह किसी खास programming language या database, queue, memory cache जैसी backing services के संयोजन से बंधी नहीं है, इसलिए इसे तरह-तरह की service-style applications पर लागू किया जा सकता है
- इसका आधार Heroku platform पर सैकड़ों ऐप्स के development और deployment में सीधे शामिल होने, और लाखों ऐप्स के development, operations और scaling को परोक्ष रूप से देखने का अनुभव है
- मुख्य चिंता यह है कि जब ऐप organically grow करते हैं, तब पैदा होने वाली collaboration cost और software erosion को कम करने के लिए एक shared vocabulary दी जाए
- यह service applications बनाने वाले developers के साथ-साथ उन्हें deploy और manage करने वाले operations engineers के लिए भी practical standard के रूप में उपयोगी हो सकता है
SaaS ऐप्स के लिए 12 operational principles
- आधुनिक software अक्सर web apps या SaaS के रूप में दिया जाता है, और Twelve-Factor App ऐसी applications बनाने की methodology है
- लक्ष्य ऐप के development, deployment और operations process को अधिक predictable बनाना है
- नए developer के project में जुड़ने पर लगने वाले समय और लागत को घटाने के लिए declarative configuration automation का उपयोग किया जाता है
- underlying operating system के साथ स्पष्ट contract रखकर execution environments के बीच portability बढ़ाई जाती है
- server और system administration का बोझ घटाकर आधुनिक cloud platforms पर deployment के अनुरूप design किया जाता है
- development environment और production environment के अंतर को घटाकर continuous deployment संभव बनाया जाता है
- tools, architecture और development practices को बड़े पैमाने पर बदले बिना scale करने योग्य बनाया जाता है
- लागू होने का दायरा किसी खास technology stack तक सीमित नहीं है
- किसी भी programming language में लिखे गए ऐप पर लागू हो सकता है
- backing services में databases, queues, memory caches आदि शामिल हैं
Heroku अनुभव से संकलित पृष्ठभूमि
- contributors ने Heroku platform पर सैकड़ों ऐप्स के development और deployment में सीधे हिस्सा लिया, और लाखों ऐप्स के development, operations और scaling को परोक्ष रूप से देखा
- वास्तविक SaaS apps से मिले अनुभव और observations के आधार पर app development की आदर्श practices को व्यवस्थित किया गया
- समय के साथ ऐप के organically grow करने की प्रक्रिया पर ध्यान दिया गया
- एक ही codebase पर कई developers के collaboration के तरीके को संबोधित किया गया
- software erosion cost से बचाव को एक महत्वपूर्ण लक्ष्य माना गया
- format Martin Fowler की Patterns of Enterprise Application Architecture और Refactoring से प्रेरित है
12 factors
- I. Codebase: version control में रखा गया एक codebase और कई deployments रखें
- II. Dependencies: dependencies को स्पष्ट रूप से declare और isolate करें
- III. Config: configuration को environment में store करें
- IV. Backing services: backing services को attached resources की तरह treat करें
- V. Build, release, run: build stage और run stage को कड़ाई से अलग रखें
- VI. Processes: ऐप को एक या अधिक stateless processes के रूप में run करें
- VII. Port binding: port binding के जरिए service को बाहर expose करें
- VIII. Concurrency: process model के जरिए scale out करें
- IX. Disposability: fast startup और graceful shutdown से robustness बढ़ाएं
- X. Dev/prod parity: development, staging और production को यथासंभव समान रखें
- XI. Logs: logs को event streams की तरह treat करें
- XII. Admin processes: administrative tasks को one-off processes के रूप में run करें
1 टिप्पणियां
Hacker News की राय
12-Factor App 2011 में Heroku और उस समय की containerized infrastructure की सीमाओं पर अधिक निर्भर होकर बनाई गई सिफारिशों का सेट है; यह engineering principles पर गहराई से आधारित दस्तावेज़ जैसा नहीं दिखता
उदाहरण के लिए, configuration को environment variables में रखने का दावा इसलिए था क्योंकि लेखक Heroku में काम करते थे, और Heroku web apps के input fields के जरिए environment variables भरता था
अगर आप configuration history को version control से track करना चाहते हैं, GitOps इस्तेमाल करना चाहते हैं, k8s ConfigMap इस्तेमाल करना चाहते हैं, या mounted volume में configuration file रखना चाहते हैं, तो ये सभी मोटे तौर पर ठीक विकल्प हैं। क्योंकि ये configuration state को app deployment state से अलग कर देते हैं
यह दस्तावेज़ जंगल और पेड़ों को गड्ड-मड्ड करता है, और वास्तविक engineering principles के बजाय इसे लिखने वाली कंपनी के product features के हिसाब से recommendations देता है, इसलिए मैं इसे हानिकारक guideline मानता हूं
Kubernetes में सुरक्षित configuration storage चाहिए तो अंततः Secrets इस्तेमाल करने पड़ते हैं, और वे environment variables की तरह key-value रूप में होते हैं। Git में वैसी security चाहिए तो encryption layer चाहिए, और तब diff comparison टूट जाता है व extra tooling चाहिए होती है
आखिरकार बात फिर इस पर लौट आती है कि Heroku जैसे high-level deployment tools क्यों बनाए गए थे
logs को stream की तरह treat करने का principle अब भी सही है। file में नहीं, STDOUT में logs लिखिए, और orchestrator को उन्हें पढ़ने व store करने दीजिए
configuration environment से आती है। app deployment method के हिसाब से local में .env और production में secret store जैसे अलग sources से configuration पढ़ने की ओर झुकता है
port binding भी ऐसा ही है: app port खोलता है, और सामने nginx जैसी चीज़ रखकर reverse proxy configure किया जाता है। K8S Services और Ingress वही भूमिका निभाते हैं
आज 12-Factor पर सबसे बड़ी आलोचना यह हो सकती है कि document असल में अच्छी तरह लिखा नहीं गया था, और यह मान लेता है कि reader को पहले से ठीक-ठीक पता है कि बात किस बारे में है
हालांकि यह credit Heroku को देता हूं कि उसने इस concept को popular किया और इसका उपयोग फैलाया
app शुरू करने से पहले machine में settings.json जोड़ने के बजाय, वही source अगर Azure EU north के AKS cluster में deploy हो तो उस cluster में configured values इस्तेमाल करे, और अगर Ikea frame के अंदर रखे RPi Zero Docker Swarm cluster में deploy हो तो उस cluster की configuration इस्तेमाल करे
उस cluster का नाम Gibson है
मुझे लगता है कि इन points में से हर एक का काफी reasonable counterargument दिया जा सकता है
पहला, एक app के लिए एक repository वाला principle बुनियादी तौर पर गलत नहीं है। ऐसी कई apps को एक repository में develop करने में समस्या नहीं है जो functionally tightly coupled हैं और release cycle share करती हैं, लेकिन अलग processes और independent scaling के फायदे पाने के लिए अलग से deploy की जानी चाहिए। public API और worker process का separation, जैसे Ruby का Sidekiq, Python का Celery, या सामान्य Kafka consumer, याद आते हैं
दूसरा, “12-Factor app system-wide packages की implicit मौजूदगी पर निर्भर नहीं करता” यह बात Nix जैसी चीज़ इस्तेमाल किए बिना व्यवहार में हासिल करना बहुत मुश्किल है। kernel system call API dependencies भी leak हो जाती हैं, और ज्यादातर Rust apps musl को छोड़कर glibc पर implicitly निर्भर होते हैं। Alpine जैसे slim distributions को छोड़ दें तो यह major Linux distributions में मौजूद होता है। मुझे लगता है Docker जैसा सहारा भी इसी समस्या को कम करने के लिए जरूरी है
तीसरा, configuration को environment variables में store करना ज्यादा fragile लगता है। secrets को environment में डालने के लिए मजबूर करने से security कमजोर हो सकती है, और structured file configuration से मिलने वाली type safety, IDE autocomplete, और automatic parsing छोड़नी पड़ती है। environment variables में string के अलावा complex configuration values के लिए parser खुद implement करना पड़ता है। व्यवहार में वे अक्सर .env file के रूप में repository में commit भी हो जाते हैं, इसलिए commit safety वाला तर्क भी अर्थहीन हो जाता है
इसका मतलब glibc समेत operating system पर निर्भर न रहने से नहीं है, बल्कि यह है कि किसी specific language package manager package के machine पर installed होने की जरूरत न बना दें
बाकी points से सहमत हूं। खासकर environment variables वाली बात actual evidence-based advice से ज्यादा उस समय Ruby development में common तरीके को authors द्वारा best मानकर लिख देने जैसी है
इसलिए ऐसी स्थिति के लिए तैयार रहना होगा, और अलग-अलग version combinations को आसानी से test करना हो तो separate repositories बेहतर लगती हैं
ज्यादातर high-level languages किसी specific glibc पर निर्भर नहीं होतीं। अगर उस language runtime ठीक से काम करता है, तो app भी उसके ऊपर काम करता है। बेशक कुछ cases में Docker जैसी चीज़ इस्तेमाल करनी पड़ती है। मुश्किल होने का मतलब यह नहीं कि उसका मूल्य नहीं है
dynamic languages या managed languages अक्सर system package से जुड़ी ज्यादातर झंझटों को ignore कर सकती हैं
कुल मिलाकर मुझे यह पसंद है, लेकिन गैर-तकनीकी या आधे-अधूरे तकनीकी लोगों ने “12 factor” को किसी万能 yellow card की तरह निकालकर releases में देरी करवाई, ऐसा इतनी बार हुआ कि मैंने इसे लगभग पूरी तरह ignore करना शुरू कर दिया
सच कहें तो “agile” के साथ भी कुछ ऐसा ही था। मैं ऐसे guidelines की मंशा समझता हूँ, लेकिन असली value उन लोगों के लिए कहीं ज़्यादा लगती है जो ivory-tower style technical leadership के अलावा कुछ दे ही नहीं पाते
मैंने ऐसे over-enthusiastic junior engineers या architect बनने की चाह रखने वालों को देखा है जो 12-Factor लेख को हर launch की mandatory requirement की तरह इस्तेमाल करते थे
ये pursue करने लायक अच्छे goals हैं, लेकिन हकीकत में release के लिए compromises करने पड़ते हैं और कौन-सी चीज़ slow करनी है या postpone करनी है, यह चुनना पड़ता है—यह बात firm और consistent तरीके से समझानी पड़ती है
छोटी-मोटी deviation के कारण release नहीं रोकेंगे, लेकिन अगर overall fit नहीं बैठता तो कम से कम उसे technical debt की तरह treat करना चाहिए। अगर कोई release किसी item में बड़ा step backward है, तो उसे रोकना, या कम से कम यह enforce करना कि उस trade-off को valuable क्यों माना गया, इसकी ज़्यादा detailed review हो—यह fair है
अगर organization ने 12FA को best practice के रूप में adopt किया है तो उसे पूरा करना चाहिए, लेकिन deployment रोकनी नहीं चाहिए
12FA कोई single checkbox नहीं है जिसे बस tick करना हो। Product mature होते समय हर item को एक-एक करके, ज़रूरत हो तो और छोटे हिस्सों में तोड़कर, product में add करने के तरीके से implement किया जा सकता है, और ज़्यादातर मामलों में ऐसा ही करना चाहिए
अगर शुरुआत में अच्छी engineering की गई हो—यानी proper abstractions और interfaces रखे गए हों और सब कुछ hardcode न किया गया हो—तो यह problem नहीं होनी चाहिए
YAGNI का भी 12FA जितना ही misuse होता है। 12FA में सबसे बड़ी कमी यह है कि junior engineers reference कर सकें ऐसे concrete examples नहीं हैं
ऐसी problems को मैंने good faith में लेना सीख लिया है। देखना चाहिए कि कोई issue क्यों उठा रहा है, existing process unclear है या नहीं, flawed है या नहीं, या उसमें reliability की कमी है या नहीं
अगर reasonable concerns मिलें, तो process बदल दें और documentation भी update कर दें
Twelve-Factor App configuration के लिए environment इस्तेमाल करने को कहता है, और Docker configuration के लिए environment इस्तेमाल न करने को कहता है, क्योंकि यह safe नहीं है
12-Factor के कई patterns मुझे पसंद हैं और मैं उन्हें use करता हूँ, लेकिन कुछ VPS context में लिखे गए थे। उस समय environment stable, safe और ज़्यादा fixed था, लेकिन containers में environment किसी layer में चला भी जा सकता है
container era में यह particular item काफी सोचने वाली बात रहा। Docker secrets भी हमेशा fit नहीं बैठते, इसलिए उन्हें काम कराने के लिए कई तरह की juggling करनी पड़ती है
secrets को environment variables के रूप में app में inject करने का मतलब यह नहीं कि उसके बाहर की handling भी unsafe है। उदाहरण के लिए AWS ECS containers में startup के समय Secret Manager से secrets लाकर environment variables के रूप में pass करने का built-in support है: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/...
container start होते समय secrets fetch होते हैं, और running application के IAM credentials से Secret Manager से लाए जाते हैं। इसलिए उस secret के लिए permissions होनी चाहिए
environment variables इस्तेमाल करने का फायदा आखिरकार पूरी तरह इस पर depend करता दिखता है कि वे set कैसे होते हैं। इस mechanism में मुझे कोई बड़ा downside नहीं दिखता
जो मुख्य downside दिखता है वह यह है कि environment variables dump करने की कोशिश करने वाला सामान्य malware values capture कर सकता है, लेकिन अगर आप secrets को memory में permanently store न करने वाले नहीं हैं, तो उस threat को रोकना कुल मिलाकर obfuscation जैसा ही है
private keys का इस तरह expose होना वाकई avoid करना चाहिए
k8s के साथ filesystem पर secrets mount करने के तरीके में खास problem नहीं है। बेशक यह सब deployment environment पर depend करता है
कभी-कभी environment variables कम खराब option होते हैं। Secrets अपने आप में हमेशा difficult होते हैं
single-tenant होने के कारण input पर blindly trust करेंगे, तो बाद में किसी arbitrary business pivot से conditions बदलने पर ऐसे bugs और लंबी रातें मिलेंगी जिन्हें trace करना मुश्किल होगा
पिछले कुछ वर्षों में 12-Factor ऐप्स पर काफी चर्चा की है और काफी भ्रम भी देखा है। 12-Factor साइट बेहतरीन है, लेकिन मुख्यतः उन लोगों के लिए अच्छी है जो पहले से समझते हैं कि वे बिंदु क्यों महत्वपूर्ण हैं।
जिन्हें नियमों के पीछे की वजह नहीं पता, उनके लिए ज्यादा गहरी व्याख्या की जरूरत थी। इसलिए “What are 12 Factor Apps and Why Should You Care?”[1] वीडियो बनाया, और सुना कि कुछ कंपनियों ने इसे engineers/DevOps के नए कर्मचारियों की training में इस्तेमाल किया और उन्हें काफी मदद मिली।
आप जहां से भी सीखें, 12-Factor ऐप्स पर एक-दो घंटे लगाकर सीखना worthwhile है। ज्यादातर “नियम” ऐसी चीजें हैं जिनके लिए awareness चाहिए, और जब तक आप खुद गलती करके दर्द नहीं झेलते, वे तुरंत स्पष्ट नहीं होते।
[1] https://youtu.be/REbM4BDeua0
“unit test” की अवधारणा ने शायद 90s के आखिर में traction पाना शुरू किया, और राहत मिली कि किसी authoritative व्यक्ति ने इसकी वकालत की।
Kent Beck के JUnit जारी करने से पहले मैंने खुद ऐसा क्यों नहीं किया, इसका कारण यह था कि जिस code पर मैं काम कर रहा था वह दूसरे code द्वारा नियंत्रित होने के लिए अच्छी तरह structured नहीं था। global variables, चारों ओर फैली state, external systems और खास filesystem layout पर निर्भरता, और modularity की अनदेखी के कारण design किए गए context के बाहर कुछ भी run नहीं हो सकता था।
यह सब “bad design” था, लेकिन deadlines पूरी हो जाती थीं, इसलिए सब यही करते थे। उम्मीद थी कि unit tests traction पकड़ेंगे तो programmers monolithic design से बाहर निकलेंगे।
25 साल तक getter/setter के लिए unit tests, और एक विशाल unit test देखा जिसमें app के हर function को सिर्फ run करने के लिए भी live database चाहिए था, इसलिए in-memory database बनाया गया, और आखिर में वह fail होकर comment out कर दिया गया—इसके बाद मेरा यह विश्वास खत्म हो गया कि unit tests meaningless checkbox से ज्यादा कुछ बनेंगे। सब इसे “best practice” होने के कारण टिक करते हैं, लेकिन रुककर यह नहीं सोचते कि कर क्यों रहे हैं।
configuration वाली सलाह से मैं हमेशा सबसे ज्यादा असहमत रहा हूं। configuration अक्सर कई parties define करती हैं और कई बार developers भी define करते हैं, इसलिए application के साथ sensible defaults ship करना और environment-specific files व environment variables से उन्हें overwrite करना अक्सर सबसे अच्छा होता है।
ज्यादातर server-side applications के लिए यह तरीका सबसे flexible है। अक्सर आपको पहले से पता होता है कि configuration क्या होनी चाहिए, और बेहतर है कि वह source control में हो, लेकिन secret values runtime पर inject की जानी चाहिए।
development, test और production में भारी configuration time न लगाना हो तो configuration में hierarchical overrides जरूरी हैं।
अगर वे development के लिए हैं तो आखिरकार production तोड़ेंगे, और production defaults development में शायद बिल्कुल meaningful न हों।
configuration section को सोचने का एक तरीका है: “क्या यह configuration strategy containers के साथ अच्छी तरह fit होती है?” जब आप image build करते हैं, तो static disk state बनती है जिसमें बदलाव तब तक persist नहीं होते जब तक आप नई image न बनाएं।
अगर configuration सिर्फ file-based है, तो test और production behavior switch करने के लिए आपको पूरी तरह नई image build करनी पड़ेगी।
configuration को base disk से स्वतंत्र रूप से बदलने योग्य बनाना changes को isolate करने में मदद करता है। आपको अलग करना होता है कि app इसलिए टूटा क्योंकि deployment, यानी image creation, broken था, या इसलिए कि configuration गलत थी।
image creation और configuration change को अलग कर देने से वह सवाल ही गायब हो जाता है।
उदाहरण के लिए, QA से production job submission queue में auth-denied retries 100,000 बार भेजने जैसी, पर्याप्त रूप से predictable human mistake की अनुमति नहीं होनी चाहिए।
हालांकि बहुत-सी configuration infrastructure के बारे में नहीं होती, बल्कि जैसे हर environment में कौन-सा ResolverStrategy bean wire करना है, ऐसे मुद्दों के बारे में होती है।
configuration version controlled होनी चाहिए, लेकिन source code से अलग managed होनी चाहिए। क्योंकि configuration deployment में इस्तेमाल होने वाली image को नहीं, बल्कि deployment itself को describe करती है।
यह निश्चित रूप से प्रभावशाली engineering norm था। Render या Vercel जैसी hosting की आसान abstractions आज बहुत हैं, लेकिन यह document 2012 में लिखा गया था और उस समय web apps accepted common practices के लिहाज से कहीं ज्यादा Wild West जैसे थे—यह सोचकर थोड़ा अजीब भी लगता है।
इस document में सबसे बड़ी कमी rules की justification है। लगभग सब कुछ बस rules हैं।
यह judge करना मुश्किल है कि rules अच्छे हैं या नहीं, और यह document यह समझने में मदद नहीं करता।
title देखकर मुझे लगा यह two-factor authentication पर commentary है। जैसे कोई app जो एक बार login करने के लिए passport photo, face scan, driving licence, SMS text, Google Authenticator, email link, password और fingerprint—सब मांगता हो।
Docker के शुरुआती दिनों में मैंने WordPress को Twelve-Factor App की तरह behave कराने के लिए काफी काम किया था।
परंपरागत रूप से WordPress ऐसे behave नहीं करता था, और कुछ हद तक यह स्वाभाविक भी है। WordPress उस दुनिया में बड़ा हुआ जहां writable और persistent local disk वाले long-lived servers आम थे।
उसके बाद चीजें काफी बदल गई होंगी। यह लगभग 2016 की बात है, लेकिन वाकई मजेदार challenge था।
मुझे हैरानी हुई कि चीजें कितनी simple हो जाती हैं, और session stickiness की चिंता किए बिना कई nodes पर load balancing संभव हो गई।
बेशक, दूसरी तरह से यह कठिन भी हो गया, जैसे sessions के लिए अलग DB की जरूरत पड़ना।