- Deptool एक deployment tool है जिसे DNS और web server configuration को सीधे संचालित करने के लिए बनाया गया है; यह पहले बदलाव की योजना दिखाता है और पुष्टि के बाद target host पर लागू करता है
- यह पूरे cluster configuration को पहले से render करके Git में manage करता है, host-वार
/var/lib/deptool के नीचे commit-आधारित directory रखता है, और current symbolic link बदलकर version को atomic तरीके से switch करता है
- deployment से पहले हर host पर lock लिया जाता है, local को ज्ञात commit और वास्तविक deployed state की तुलना करके stale plan को रोकता है, और केवल तब आगे बढ़ता है जब प्रभावित सभी host के lock मिल जाएँ
- services systemd unit के रूप में चलती हैं; configuration बदलने पर restart होती हैं, और start fail होने पर link को पिछले known-good version पर वापस करके फिर restart किया जाता है, जिससे millisecond स्तर का automatic rollback होता है
- remote execution एक static agent मॉडल है जो SSH को केवल transport layer की तरह उपयोग करता है, और Flatcar Linux जैसे environment में जहाँ Python और package manager नहीं होते, वहाँ भी सिर्फ coreutils से automatic install संभव है
Deptool बनाने की पृष्ठभूमि
- यह काम उस विरोधाभास से बचने की कोशिश से शुरू हुआ जिसमें यूरोपीय digital sovereignty पर लिखी गई सामग्री अमेरिकी hosting और अमेरिकी नियंत्रण वाले hyperscaler पर प्रकाशित हो रही थी, इसलिए blog को यूरोप में migrate किया गया
- DNS भी Cloudflare पर निर्भर था, इसलिए DNS server को खुद चलाने की ज़रूरत पड़ी
- मौजूदा web server एक छोटे VM पर Nginx और certificate renewal के लिए Lego चलाता था, और Nginx configuration Nix से generate करके एक छोटे Python script से server पर copy कर Nginx restart किया जाता था
- DNS server चलाने के लिए कम से कम दो server, अधिक systemd unit, configuration file, और zonefile की ज़रूरत थी, इसलिए पुरानी script पर्याप्त नहीं रही
- NixOS पर switch करना एक विकल्प था, लेकिन मौजूदा तरीके—न्यूनतम base OS और read-only chroot में केवल ज़रूरी binary शामिल करके service चलाना—को बनाए रखते हुए नया deployment tool बनाने का निर्णय लिया गया
Deptool का उपयोग कैसा दिखता है
- Deptool पहले cluster configuration change की योजना दिखाता है, और पुष्टि मिलने के बाद target host पर लागू करता है
- DNS record update के उदाहरण में
deptool deploy चलाने के बाद s4.ruuda.nl और s5.ruuda.nl पर nsd configuration file change और nsd.service restart योजना के रूप में दिखते हैं
- deployment fail होने पर automatic rollback लागू होता है; उदाहरण में
prod cluster के 2 host पर apply करने की पुष्टि लेने के बाद 0.99 सेकंड में सफलता मिलती है
- output target host, बदले गए application, बदली गई file, और restart होने वाले systemd unit को अलग-अलग दिखाता है, जिससे deployment से पहले वास्तविक होने वाले काम की जाँच की जा सकती है
इच्छित deployment tool की शर्तें
-
तेज़
- configuration update 1 सेकंड से कम में होना चाहिए; transatlantic ping भी लगभग 100ms का होता है, इसलिए मूल रूप से इससे अधिक धीमा होने का कारण नहीं माना गया
-
पूर्वानुमेय
- tool को पहले यह दिखाना चाहिए कि वह क्या करेगा, और फिर वही करना चाहिए
- OpenTofu की तरह plan और apply चरण अलग होने चाहिए
- Ansible का check mode भरोसेमंद नहीं माना गया, क्योंकि imperative step चलने के बाद ही chain change सामने आ सकते हैं, और check तथा वास्तविक execution के बीच host state बदलने से भी यह नहीं रोकता
-
सुरक्षित
- यदि Nginx configuration टूट जाए तब भी web server कुछ मिनटों तक down न रहे; tool को millisecond स्तर पर automatic rollback करना चाहिए
-
सरल
- ज़रूरत सिर्फ इतनी है कि laptop से server पर configuration file copy की जाए और कुछ systemd unit restart किए जाएँ
- हर deployment समस्या हल करना, control flow देना, या arbitrary code execution उपलब्ध कराना आवश्यक नहीं है
- configuration file templating अलग tool से की जा सकती है; YAML templating की समस्या को generate और अलग file generation tool में अलग किया गया है
-
declarative होना चाहिए
- configuration से file या application हटाने पर server से भी हट जाना चाहिए
- अलग cleanup step जोड़ने की ज़रूरत न हो, और भूल जाने से drift या leftover file न बनें
-
कोई initial setup नहीं
- server provisioning के तुरंत बाद से उसे manage किया जा सके
- agent, daemon, dependency को manually install करने या host register करने की प्रक्रिया की आवश्यकता नहीं होनी चाहिए; वरना उस प्रक्रिया को भी फिर automate करना पड़ेगा
configuration generation और deployment का अलगाव
- मुख्य विचार configuration generation और deployment को अलग करना है
- कार्यस्थल पर David द्वारा बनाया गया Unsible Ansible playbook को step-by-step चलाने के बजाय local में tarball बनाकर host पर भेजता है और file रखता है
- पुरानी simple deployment script भी configuration को बाहर build करती थी और script की भूमिका लगभग file copy करने जैसी थी
- NixOS को भी इस विचार का local system पर लागू रूप माना जा सकता है; Nix से सीखा जा सकता है कि generated output को ऐसी जगह रखा जाए जहाँ कई version साथ रह सकें, और system administration के imperative हिस्से को कुछ symbolic link बदलने वाले छोटे activation step तक सीमित रखा जाए
- यह design package management और system configuration दोनों पर अच्छी तरह फिट बैठता है
Deptool कैसे काम करता है
-
पूरे cluster configuration को पहले से render करता है
- पूरे cluster की configuration file पहले से generate करके disk की directory में store की जाती है
- directory tree दो स्तर गहरा है; सबसे ऊपर target host-वार directory होती है, और उसके नीचे application-वार directory होती है
-
Git repository में रखता है
- configuration directory को Git repository में रखने से version के बीच अंतर की तुलना की जा सकती है और पूरे cluster में क्या बदला है यह देखा जा सकता है
- diffstat से प्रभावित host और बदले हुए app दिख जाते हैं, और हर configuration file का सटीक diff भी देखा जा सकता है
-
host की isolated directory में files को materialize करता है
- सभी file
/var/lib/deptool के नीचे रखी जाती हैं ताकि वे दूसरी चीज़ों में दखल न दें
- deploy होने वाले commit नाम से directory बनाई जाती है, इसलिए disk पर कई version साथ रह सकते हैं
current symbolic link deployed version की ओर इशारा करता है, जिससे version को atomic तरीके से बदला जा सकता है
- हटाई गई file अगले version में materialize नहीं होती, इसलिए leftover file नहीं बचती
- जिन application को किसी विशेष path पर file चाहिए, उनके लिए filesystem के आवश्यक स्थान से
/var/lib/deptool की ओर symbolic link बनाए जा सकते हैं
- symbolic link बनाना या हटाना atomic नहीं है, लेकिन इसकी ज़रूरत file content modify करते समय नहीं बल्कि link add/remove करते समय ही पड़ती है
- बाद के deployed version में यदि वह symbolic link शामिल न हो, तो diff से पता चल जाता है कि उसे delete करना है, इसलिए file छूटती नहीं है
-
remote-tracking ref से deployment state दर्ज करता है
- operator के laptop पर हर host पर deployed commit को track किया जाता है
- deployment state पूरे cluster की property नहीं बल्कि host-वार property है
- यदि कोई change किसी खास host को प्रभावित नहीं करता, तो उस host पर नया commit deploy करने की ज़रूरत नहीं होती
- इसी जानकारी से cluster diff को offline calculate किया जा सकता है, और यही diff deployment plan बनता है जो millisecond स्तर पर दिखाया जा सकता है
-
deployment से पहले target host पर lock हासिल करता है
- SSH से connect करके lock request भेजी जाती है, और request में वह commit शामिल होता है जिसे उस host पर deployed माना जा रहा है
- lock मिल जाने पर plan valid माना जाता है, और lock release होने तक उस host पर कोई दूसरा deployment नहीं हो सकता, इसलिए plan valid बना रहता है
- deployment केवल तब आगे बढ़ता है जब change से प्रभावित सभी host के lock मिल जाएँ
- यदि ref पुराना हो और host पर कुछ और deploy हो चुका हो, तो plan stale माना जाता है और प्रक्रिया रोक दी जाती है
- बाद में local ref update करने पर अगली run में नया plan देखा जा सकता है
-
systemd unit restart करता है
- सभी service systemd unit के रूप में चलती हैं और तेज़ी से start होती हैं, इसलिए जब संदेह हो तो restart करना बेहतर माना गया है
- application configuration बदलने पर प्रभावित systemd unit restart किए जाते हैं
- यदि unit start fail हो जाए, तो symbolic link को पिछले known-good version पर लौटाकर फिर restart किया जाता है, जिससे millisecond स्तर का automatic rollback संभव होता है
optimistic concurrency model
- Deptool deployment में optimistic concurrency का तत्व है
- यह मानकर योजना बनाई जाती है कि वर्तमान cluster state ज्ञात है; यदि यह मान्यता गलत निकले तो फिर से प्रयास करना पड़ता है
- contention न होने पर यह बहुत तेज़ है; personal infrastructure में एक व्यक्ति द्वारा उसी laptop से deployment इसका उदाहरण है
- ऐसे environment में जहाँ कई लोग लगातार deploy करने की कोशिश करते हों, केवल एक व्यक्ति सफल होगा और बाकी को retry करना पड़ेगा, इसलिए performance बहुत खराब हो सकती है
- यह model
git push जैसा है; यह सैकड़ों लोगों या हज़ारों server तक scale नहीं करता, लेकिन personal infrastructure के लिए पर्याप्त है
- अपना tool खुद बनाने से उसे अपने सटीक use case के अनुसार optimize किया जा सकता है
agent बनाना
-
Flatcar Linux और शुरुआती host constraints
- web server Flatcar Linux पर चलता है
- Flatcar Linux एक image-based OS है जिसका userspace बहुत छोटा है; इसमें coreutils और Bash हैं, लेकिन package manager और Python नहीं हैं
- यह attack surface घटाने के लिए अच्छा है, लेकिन कुछ install करने के लिए असुविधाजनक है
- यदि tool के काम करने से पहले कुछ install करना पड़े, तो उस install process को automate करने की नई समस्या पैदा होती है
-
SSH को केवल transport layer की तरह उपयोग करना
- नए host को बाहर से manage करना होता है, इसलिए SSH और passwordless sudo उपलब्ध हो सकते हैं
- SSH के जरिए सीधे command चलाने का तरीका सिर्फ handshake के कारण धीमा नहीं है, बल्कि argv भी SSH boundary को सुरक्षित रूप से पार नहीं कर पाता, और shell-over-SSH में word splitting तथा escaping की समस्याओं से जूझना पड़ता है
- Deptool एक ही program चलाता है जो बिना argument के predictable location पर होता है, और उसी program को agent की तरह उपयोग करता है
- agent stdin से message पढ़ता है और stdout पर response देता है
- SSH केवल socket जैसी transport के रूप में काम करता है; user-controlled input SSH या shell command में नहीं जाता, इसलिए escaping की समस्या से बचा जा सकता है
-
static binary का उपयोग
- agent को static binary के रूप में build किया जाता है
- kernel के अलावा और क्या मौजूद है, इस बारे में कोई अनुमान नहीं लगाया जाता, और उपयोगी काम शुरू करने से पहले कई MB code parse करने वाले interpreter की भी ज़रूरत नहीं पड़ती
- Ansible, अपनी सबसे खराब कमियों को mitigate करने के बाद भी, हर connection पर कई MB Python module भेजता है, और Flatcar में Python है भी नहीं
-
commit-आधारित path में binary रखना
- agent binary को ऐसे path में store किया जाता है जिसमें build किया गया commit शामिल हो
- इससे यह सुनिश्चित होता है कि connection के दोनों छोर पर एक ही version चल रहा है, इसलिए protocol compatibility की समस्या नहीं होती
- path का प्रारूप
/var/lib/deptool/bin/deptool-<version>-<commit> है
-
पहले मान लेना कि binary मौजूद है
- SSH handshake महँगा है, इसलिए probe या idempotent install step पर इसे बर्बाद नहीं किया जाता
- agent binary लगभग 1.6MB की है; इतनी बड़ी नहीं कि भेजना असंभव हो, लेकिन यह मुफ्त भी नहीं है
- cluster configuration change, Deptool update की तुलना में कहीं अधिक बार होते हैं, इसलिए आमतौर पर माना जाता है कि binary पहले से मौजूद है
-
execution fail होने पर binary install करना
- यदि binary start न हो, तो दूसरी SSH connection से installation की जाती है
- execution command इस प्रकार है
uname -sm
&& sudo mkdir -p /var/lib/deptool/{bin,apps,store}
&& sudo dd status=none of=<remote_bin_path>
&& sudo chmod +x <remote_bin_path>
&& sudo sha256sum <remote_bin_path>
- पहले stdout से एक line पढ़कर
uname output लिया जाता है, जिससे OS और CPU architecture की पहचान कर उसी platform के लिए agent binary भेजी जाती है
- binary को stdin में लिखने पर remote का
dd उसे disk पर लिख देता है
- अंत में stdout से एक और line पढ़कर remote पर calculate किया गया shasum जाँचा जाता है, जिससे transfer की सफलता verify होती है
- यह प्रक्रिया केवल standardized coreutils program पर निर्भर करती है
- इसके बाद agent execution फिर से try करने पर सफल होना चाहिए, और agent पुराने version साफ करता है ताकि disk भर न जाए
agent approach के प्रभाव और लागत
- remote host पर agent चलाने और उससे communication करने का तरीका मिल जाता है
- remote host पर coreutils के अलावा किसी requirement के बिना automatic installation संभव है
- दोनों तरफ एक ही version चलने से protocol compatibility संरचनात्मक रूप से सुनिश्चित होती है
- user-controlled input केवल SSH-आधारित socket से गुजरता है और SSH या shell command में नहीं जाता, इसलिए escaping की समस्या और length limit से बचा जा सकता है
- सामान्य स्थिति में केवल एक SSH handshake की ज़रूरत होती है, इसलिए latency कम रहती है
- नए machine पर deploy करते समय या tool update के बाद जैसे कम होने वाले मामलों में 2 अतिरिक्त connection और एक बार का 1.6MB transfer चाहिए होता है
ControlMaster का उपयोग करने पर बाद के connection का अधिकांश overhead टाला जा सकता है, इसलिए कुल लागत कुछ सेकंड के स्तर पर रहती है
- इस स्थिति में deployment 1 सेकंड से कम नहीं रहता, लेकिन इसे Ansible से बेहतर माना गया है
- configuration deploy करके थोड़ा बदलने और फिर दोबारा deploy करने के workflow में SSH base connection बनाए रख सकता है, इसलिए deployment तुरंत जैसा महसूस होता है
उपयोग के परिणाम और सार्वजनिक रिलीज़
- Deptool का पिछले एक महीने से personal infrastructure management में उपयोग हो रहा है
- connect करने से पहले सटीक plan तुरंत देख पाना और automatic rollback होना अच्छा है, लेकिन सबसे बड़ा बदलाव 1 सेकंड से कम deployment है
- यदि सही deployment तरीका कुछ मिनट लेता है, तो feedback loop घटाने के लिए सीधे server पर file edit करने का मन करता है; लेकिन Deptool में local में edit करके deploy करना, SSH से server में login करके editor खोलने से भी तेज़ है
- सबसे कम friction वाला तरीका ही सही तरीका बन जाता है, और लागू किए गए सभी बदलाव Git history में दर्ज हो जाते हैं
- कुछ टूट भी जाए, तो उसके टूटने का पता चलने से पहले ही Deptool rollback कर देता है
- Deptool को एक व्यक्तिगत समस्या को ठीक-ठीक हल करने के लिए बनाया गया है, और यह हर किसी की हर deployment समस्या हल करने की कोशिश नहीं करता—यही बात इसे इस use case में चमकदार बनाती है
- यह खासकर image-based operating system में उपयोगी हो सकता है, और Codeberg तथा GitHub पर उपलब्ध है, साथ ही विस्तृत manual भी दिया गया है
1 टिप्पणियां
Lobste.rs की राय
इस बात को खुलकर बताना कि इस प्रोजेक्ट में LLM-जनरेटेड टेक्स्ट कहीं भी नहीं डाला गया, सच में अच्छा लगा: not putting LLM-generated text anywhere near this
टूल खुद भी काफ़ी पॉलिश्ड है और इसकी डिज़ाइन अच्छी लगती है, लेकिन मैं फिलहाल NixOS ही इस्तेमाल करता रहूँगा
इसे ज़रूर आज़माने वाला हूँ। यह systemd-आधारित services deploy करने के लिए मैंने खुद जो सिस्टम बनाया था, उसके ज़्यादा पॉलिश्ड version जैसा लगता है
ट्यूटोरियल देखकर अच्छा लगा, लेकिन local state को कैसे handle करना चाहिए, यह जानने की जिज्ञासा है। उदाहरण के लिए, app का sqlite database कहाँ store होना चाहिए, यह मुझे docs में नहीं मिला
यह भी जानना है कि app binary को server पर भेजकर systemd unit में इस्तेमाल करने का कोई तरीका है या नहीं। अगर नहीं, तो binary deployment कैसे किया जाता है, यह समझना चाहूँगा
/var/lib/<yourapp>हैअगर application को systemd unit के रूप में चला रहे हैं, तो
StateDirectory=इस्तेमाल कर सकते हैं ताकि systemd सही user ownership के साथ directory बना देमैं जिन applications को चलाता हूँ, उन्हें इस Nix-आधारित script से छोटे EROFS images में build करता हूँ, और वही script image को server पर push भी करती है। पहले यह अलग step था, लेकिन अब build और push को एक step में जोड़ दिया है, और यह unique directories में जाता है ताकि कई versions साथ रह सकें
build result में file paths वाला JSON भी शामिल होता है, जिसे cluster config में import करके systemd units में render किया जाता है, फिर Deptool से deploy किया जाता है। यानी एक टूल image deployment संभालता है, और Deptool activation संभालता है
अगर containers इस्तेमाल कर रहे हों, तो आम तौर पर registry में push किया जाता है, और server पर सिर्फ यह बताने वाली config file होती है कि क्या pull करना है, इसलिए वह हिस्सा सिर्फ Deptool से manage किया जा सकता है
एक दूसरा तरीका bootable containers इस्तेमाल करना भी है, और वह भी काफ़ी अच्छा है
अभी कमी बस यह है कि सही host पर वास्तव में
bootc update --applyचलाने वाला कुछ मौजूद नहीं है। Auto-update mechanism हैं, लेकिन वे orchestrated नहीं हैं, इसलिए cluster में वह मनचाहा तरीका नहीं हैफिलहाल मैं इसे हाथ से करता हूँ, लेकिन आखिर में चलाना सिर्फ एक bootc command ही है, इसलिए बाद में script बनाना आसान होना चाहिए
हर बार नया deployment tool आता है तो मैं थोड़ा संशय में रहता हूँ, लेकिन यह अच्छी तरह डिज़ाइन किया हुआ और पॉलिश्ड लगता है
sshcommand को सीधे इस्तेमाल करना भी सही फैसला लगता है। user के पास जोsshहै, उसके काम करने का भरोसा होता है, और हो सकता है कि वह किसी बहुत खास setup या patched ssh binary का उपयोग कर रहा होवे टूल जो किसी external library से ssh को खुद implement करने की कोशिश करते हैं, कुछ users के लिए रुकावट बन सकते हैं
EROFS को कैसे और क्यों इस्तेमाल किया जा रहा है, यह और विस्तार से जानना चाहूँगा
Flatcar में package manager नहीं होता, इसलिए software और dependencies को किसी न किसी तरीके से खुद पहुँचाना पड़ता है, और self-contained filesystem image ऐसा ही एक तरीका है
OCI images में Podman या Docker जैसे अलग tools tar को कहीं extract करते हैं और overlay mount stack बनाते हैं, लेकिन अगर वह पहले से filesystem image है, तो
RootImage=के ज़रिए उसे सीधे systemd unit में चलाया जा सकता हैNix से image build की जाती है ताकि उसमें सच में सिर्फ न्यूनतम चीज़ें हों। Nginx binary, LibreSSL, libc, और कुछ shared libraries — Bash भी नहीं
यह defense in depth का हिस्सा है। अगर Nginx में remote code execution vulnerability भी हो, तब भी attacker एक ऐसे filesystem namespace के अंदर फँसा रहेगा जहाँ अगले stage का exploit बनाने के लिए लगभग कुछ नहीं है, और पूरा filesystem read-only है। सिर्फ इसलिए नहीं कि इसे read-only mount किया गया है, बल्कि इसलिए कि EROFS में लिखने की क्षमता ही नहीं होती
पहले मैं Squashfs इस्तेमाल करता था और वह ठीक काम करता था, लेकिन वह filesystem live CD के दौर को ध्यान में रखकर डिज़ाइन किया गया था। EROFS आज के systems के लिए ज़्यादा उपयुक्त समझौते चुनता है, हालांकि ईमानदारी से कहूँ तो मेरे use case में शायद कोई measurable फर्क नहीं होगा
images छोटी ज़रूर हैं, लेकिन वह अलग compression settings की वजह से है। सैद्धांतिक रूप से EROFS content-defined chunking के लिए बेहतर suited है, अगर आप अलग versions की images के बीच data reuse करना चाहें, लेकिन मैं अभी उसे image transfer में वास्तव में इस्तेमाल नहीं कर रहा हूँ
संयोग से मैं एक दोस्त के साथ simple deployment strategy पर बात कर रहा था, और तभी यह पोस्ट आई; यह उन निष्कर्षों के काफ़ी करीब है जिन तक हम पहुँच रहे थे
बस यह जानना है कि इस setup में secret management कैसे किया जाता है
शीर्षक “Prompting the deployment tool I wish I had” है, लेकिन
https://codeberg.org/ruuda/deptool/…
एक मायने में यह प्रभावशाली है कि floating points ने Rust इस्तेमाल करवाया
अच्छे अर्थ में Rust एक “disciplined” language है, और इसमें मज़बूत conventions व tooling ecosystem है। दोनों चीज़ें LLM के लिए मददगार हैं
अजीब तरह से, कम से कम थोड़ी guidance देने पर LLM कुछ दूसरी languages की तुलना में Rust में ज़्यादा छोटे programs बनाते हैं। चूँकि मैं वैसे भी सारा code पढ़कर edit करने वाला हूँ, मेरे लिए छोटा होना बेहतर है
यह secrets को कैसे handle करता है, यह जानना चाहूँगा। क्या कोई पसंदीदा workflow है, क्या उन्हें EROFS image में रखा जाता है, या systemd के ज़रिए inject किया जाता है?
उस directory को Lego unit में read-write mount किया जाता है, और Nginx unit में read-only mount किया जाता है