Firecracker पर FreeBSD
(usenix.org)- AWS Lambda के लिए Linux-केंद्रित microVM Firecracker VMM पर FreeBSD 14 kernel को boot कराने का काम इस बात का उदाहरण है कि एक न्यूनतम virtualization environment OS initialization path की छिपी assumptions और bottlenecks को कैसे उजागर करता है
- पहली boot PVH boot mode के ELF Note format में अंतर, Xen-विशेष hypercall, और Firecracker की memory layout में अंतर की वजह से अटकी, जिसे FreeBSD के PVH code में बदलाव करके हल किया गया
- Firecracker द्वारा ACPI न देने से CPU और interrupt जानकारी पाने का path बदल गया, और Linux के MPTable handling bug से मेल खाने के लिए
MPTABLE_LINUX_BUG_COMPAToption जोड़ा गया - serial console और Virtio device जोड़ने की प्रक्रिया में UART behavior, kernel command line parsing, और unaligned disk I/O constraints सामने आए; FreeBSD ने
hw.broken_txfifo, FIFO drain workaround, औरbusdma-आधारित bounce handling लागू किया - uncommitted patches सहित, 1 CPU और 128MB RAM VM में FreeBSD kernel 20ms से कम में boot होता है; बाकी काम में PVH support merge करना, Xen code अलग करना, छोटा kernel configuration, और Firecracker की FreeBSD porting की समीक्षा शामिल है
Firecracker पर FreeBSD चलाने की वजह
- Firecracker एक VMM है जो AWS Lambda जैसे serverless execution environments के लिए Linux KVM के ऊपर low-overhead microVM बनाता और manage करता है
- FreeBSD porting का काम जून 2022 में शुरू हुआ
- इस काम की प्रेरणा FreeBSD और Firecracker, दोनों की सीमाओं को साथ में समझना था
- FreeBSD boot speed सुधारने के काम को आगे बढ़ाते हुए देखना था कि minimal hypervisor पर यह कितना तेज हो सकता है
- FreeBSD को नए platform पर port करने से FreeBSD और उस platform, दोनों के bugs सामने आते हैं
- AWS Lambda अभी केवल Linux support करता है, और Lambda adoption से अलग, FreeBSD का Firecracker support करना एक जरूरी prerequisite है
- Firecracker अपने आप में एक रोचक platform है, इसलिए असल में यह चल सकता है या नहीं, यह देखना था
Kernel execution तक पहली बाधा
- Firecracker मूल रूप से Linux kernel चलाने के लिए design किया गया था, लेकिन 2020 में
linuxbootके अलावा PVH boot mode support करने वाला patch था - FreeBSD Xen पर PVH booting support करता था, इसलिए वही path Firecracker पर आजमाया गया
- पहली समस्या यह थी कि Firecracker ने FreeBSD kernel को memory में load करने के बाद kernel entry point नहीं ढूंढ पाया
- PVH boot protocol यह value ELF Note में store करता है
- ELF Note में
PT_NOTEऔरSHT_NOTEहोते हैं, और FreeBSD वह format नहीं दे रहा था जिसे Firecracker खोज रहा था - FreeBSD kernel linker script में छोटा बदलाव करने पर Firecracker FreeBSD kernel execution शुरू कर सका
- Kernel execution लगभग 1 microsecond बाद फिर रुक गया
शुरुआती debugging और Xen dependency हटाना
- Kernel debugger और serial console initialize होने से पहले kernel crash होने पर FreeBSD की debugging capabilities लगभग काम नहीं आईं
- Firecracker process ने सिर्फ यह जानकारी दी कि FreeBSD guest ने triple-fault किया
- Kernel start code के बीच में
hltinstruction डालकर crash की जगह narrow down करने के लिए “kernel bisection” किया गयाhltतक पहुंचने पर Firecracker चलता रहता है, लेकिन host CPU usage 0% हो जाता है- Firecracker exit हो जाए, तो माना जा सकता था कि crash उस point से पहले हुआ
- पहला कारण Xen hypercall था
- FreeBSD का PVH entry point असल में Xen में boot करने वाला code था और मानकर चलता था कि वह Xen के अंदर चल रहा है
- Firecracker द्वारा इस्तेमाल किया गया KVM, Xen hypercall नहीं देता, इसलिए call करते ही VM crash हो गया
- शुरुआत में Xen hypercall comment out किए गए, और बाद में
CPUIDमें Xen signature check करने के बाद ही call करने के लिए बदला गया
- physical memory map query करना एक जरूरी function था जो Xen hypercall संभाल रहा था
- PVH version 1 से memory map pointer
start_infopage के जरिए pass होता है - FreeBSD को Xen hypercall के बजाय PVH version 1 memory map इस्तेमाल करने के लिए बदला गया
- PVH version 1 से memory map pointer
- Firecracker और Xen की memory layout में अंतर ने भी समस्या पैदा की
- Xen पहले kernel load करता है और फिर
start_infopage को अंत में रखता है - Firecracker
start_infopage को fixed low address पर रखता है और उसके बाद kernel load करता है - FreeBSD PVH code
start_infoके ठीक बाद की जगह को scratch space मानता था, और Firecracker में इससे initial kernel stack overwrite हो रहा था - Hypervisor द्वारा initialize किए गए सभी memory regions के बाद scratch space allocate करने के लिए बदलाव करके इसे हल किया गया
- Xen पहले kernel load करता है और फिर
ACPI की अनुपस्थिति और MPTable compatibility
- x86 पर FreeBSD आम तौर पर disk, network adapter, CPU, और interrupt controller की जानकारी ACPI के जरिए पाता है
- Firecracker जानबूझकर minimal implementation चुनता है और ACPI नहीं देता
- इसके बजाय FreeBSD पुराने Intel MultiProcessor Specification की MPTable structure इस्तेमाल कर सकता है
- यह
GENERICkernel configuration में default रूप से शामिल नहीं है - Firecracker के लिए lightweight kernel configuration में
device mptableजोड़कर इसका इस्तेमाल किया जा सकता था
- यह
- Firecracker द्वारा दिया गया MPTable standard नहीं था, बल्कि Linux जिस तरह स्वीकार करता है उसके हिसाब से था
- Linux में MPTable खोजने और parse करने के तरीके में bug था
- Firecracker को Linux boot कराने के लक्ष्य से design किया गया था, इसलिए वह Linux द्वारा support की जाने वाली non-standard layout देता है
- Standards के अनुसार independently implemented FreeBSD गलत जगह रखे MPTable को नहीं ढूंढ पाया, और मिल भी जाए तो invalid MPTable parse नहीं कर पाया
- FreeBSD में
options MPTABLE_LINUX_BUG_COMPATkernel option जोड़ा गया- जब Linux की MPTable handling के साथ bug-for-bug compatibility चाहिए हो, तब इसका इस्तेमाल होता है
- इस option से Firecracker पर FreeBSD booting और आगे बढ़ी
Serial console और Virtio device support
- Firecracker द्वारा दिए जाने वाले कुछ emulated devices में से एक serial port है
- सामान्य configuration में Firecracker process का standard input/output VM के serial port input/output बन जाता है
- FreeBSD kernel root disk को kernel image में शामिल करके boot हुआ, और kernel console output तक पढ़ा जा सका
- User space booting पर जाने के बाद console output 16 characters पर रुक गया
- यह पुराने QEMU के UART bug जैसा symptom था
- UART का transmit FIFO खाली होने पर interrupt नहीं आया, इसलिए FreeBSD 16 bytes के बाद और नहीं लिख पाया
- FreeBSD kernel के मौजूदा workaround
hw.broken_txfifo="1"को kernel environment variable के रूप में compile करके हल किया गया
- Console input भी काम नहीं कर रहा था
- Firecracker ने माना कि emulated UART का receive FIFO भर गया है, इसलिए उसने console नहीं पढ़ा
- FreeBSD UART initialization के दौरान FIFO size मापने के लिए receive FIFO को garbage values से भरता है और फिर FIFO Control Register से खाली करता है
- Firecracker FIFO Control Register implement नहीं करता, इसलिए FIFO भरा ही रहा
- FreeBSD में बदलाव किया गया कि FIFO clear करने के बाद भी अगर
LSR_RXRDYबचा रहे, तो एक-एक character पढ़कर FIFO खाली कर दे
- Disk और network इस्तेमाल करने के लिए Virtio block/network devices चाहिए थे
- Firecracker इन्हें
mmiodevices के रूप में expose करता है - FreeBSD Firecracker kernel configuration में
device virtio_mmioजोड़ा गया
- Firecracker इन्हें
- FreeBSD मूल रूप से उम्मीद करता था कि
mmiodevices FDT से discover होंगे, लेकिन Firecracker kernel command line मेंvirtio_mmio.device=4K@0x1001e000:5जैसे directives pass करता है- FreeBSD ने इन directives को parse करके
virtio_mmiodevice nodes बनाने वाला code जोड़ा - Device node बन जाने पर FreeBSD की मौजूदा device probe process Virtio device type पहचानती है और उचित driver attach करती है
- FreeBSD ने इन directives को parse करके
- कई Virtio devices होने पर kernel command line parsing की समस्या आई
- Firecracker Linux तरीके से कई
key=valuepairs भेजता है - FreeBSD kernel command line को environment variables की तरह parse करता है, इसलिए समान नाम के दो
virtio_mmio.device=होने पर सिर्फ एक बचता है - Early kernel environment parsing code में बदलाव किया गया कि duplicate variables को numbered suffix देकर
virtio_mmio.device=औरvirtio_mmio.device_1=की तरह preserve करे
- Firecracker Linux तरीके से कई
- Abnormal shutdown के बाद अगली boot में
fsckचलने पर kernel panic हुआfsckFreeBSD में page-aligned नहीं होने वाला disk I/O पैदा करने वाले दुर्लभ cases में से एक है- Firecracker का Virtio implementation केवल एक data buffer लेता है, और page boundary cross करने वाले buffer को कई segments में बांटने वाली सामान्य Virtio विधि support नहीं करता
- FreeBSD Virtio block driver को
busdmaइस्तेमाल करने के लिए बदला गया, और unaligned requests को temporary buffer से गुजरने वाली bounce handling के जरिए Firecracker की limit के अनुरूप किया गया
Firecracker ने जो boot optimizations उजागर किए
- FreeBSD के Firecracker पर चलने लगने के बाद boot time और memory usage कम करने की जगहें साफ दिखने लगीं
- 128MB RAM VM में system memory का लगभग आधा हिस्सा
wiredstate में था और processes अक्सर terminate हो रहे थे- जांच में पता चला कि
busdmabounce pages के लिए 32MB reserve कर रहा था - Firecracker की limit के अनुसार हर disk I/O को ज्यादा से ज्यादा एक 4KB bounce page ही चाहिए था
- कम संख्या में I/O segments support करने वाले devices के लिए bounce page reservation सीमित करने वाले patch से memory usage घटकर 512KB हो गया
- जांच में पता चला कि
- Kernel random number generator optimization से boot time घटा
- VM में hardware device-based entropy प्रभावी नहीं हो सकती
- x86 का
RDRANDbackup entropy source के रूप में इस्तेमाल होता है, लेकिन हर request में entropy की मात्रा कम थी और request केवल हर 100ms में एक बार की जाती थी - Fortuna random number generator को पूरी तरह seed करने लायक request करने के लिए बदलकर 2.3 seconds कम किए गए
- Host ID handling भी तेज हुई
- सामान्यतः boot loader BIOS या UEFI जानकारी के आधार पर
smbios.system.uuidset करता है - Firecracker में boot loader नहीं है, इसलिए ID provide नहीं होती
- Hardware गलत ID दे तो warning के बाद 2 seconds wait किया जाता था; ID बिल्कुल न हो तो शांत और तेज आगे बढ़ने के लिए बदलाव किया गया
- सामान्यतः boot loader BIOS या UEFI जानकारी के आधार पर
- IPv6 DAD wait condition संकरी की गई
- FreeBSD network interface पर IPv6 enabled होने पर Duplicate Address Detection का इंतजार करता था
- loopback interface पर हमेशा IPv6 enabled था
- केवल loopback के अलावा किसी interface पर IPv6 हो तभी DAD wait करने के लिए बदलकर 2 seconds कम किए गए
- Reboot और shutdown process में fixed wait time हटाए गए
- Reboot के समय
Rebooting...message के बादprintfcomplete होने और पढ़ने के समय के लिए 1 second wait करने वाला behaviorkern.reboot_wait_timesysctl में बदला गया, और default value 0 है - Shutdown या reboot के समय BSP द्वारा अन्य CPUs को stop signal मिलने के बाद अतिरिक्त 1 second wait भी हटाया गया
- Reboot के समय
- TSLOG का इस्तेमाल करके boot flame chart analyze किया गया
- Firecracker का minimal environment कम noise वाला था, इसलिए बचे हुए bottlenecks देखना आसान था
- VM execution बहुत तेज होने से नया kernel build, run, और flame chart generation अक्सर 30 seconds से कम में संभव था
- TSLOG analysis से millisecond-level के कई bottlenecks घटे
lapic_initके 100000-iteration calibration loop को 1000 iterations तक घटाकर 10ms कम किए गएns8250_drainमें हर character परDELAYcall करने वाले हिस्से कोLSR_RXRDYcheck के बाद केवल जरूरत होने पर delay करने के लिए बदलकर 27ms कम किए गए- Firecracker ने TSC और local APIC clock frequency बताने वाला CPUID leaf implement किया, जिससे 20ms कम हुए
kern.nswbufको हमेशा 256 रखने के बजाय32 * mp_ncpusकरने से 1 CPU VM में 5ms कम हुएmi_startupकी bubblesort को quicksort में बदलने से 2ms कम हो सकते हैं, और 22 अगस्त 2023 तक यह अभी commit नहीं हुआ थाvm_memद्वारा सारी physical memory केvm_pagestructures को तुरंत initialize करने के बजाय lazy initialization करने से 2ms कम हो सकते हैं, और उसी तारीख तक यह अभी commit नहीं हुआ था- Firecracker की guest memory
mmapमेंMAP_POPULATEजोड़ने से पहली page access पर Linux द्वारा page structure बनाने की cost घटकर 2ms कम हो सकती है, और उसी तारीख तक यह अभी commit नहीं हुआ था
मौजूदा स्थिति और बाकी काम
- FreeBSD Firecracker पर boot होता है और बहुत तेज चलता है
- FreeBSD और Firecracker के uncommitted patches सहित, 1 CPU और 128MB RAM VM में FreeBSD kernel 20ms से कम में boot कर सकता है
- बाकी काम PVH support को व्यवस्थित करने और kernel configuration छोटा करने पर केंद्रित है
- ऊपर बताए patches commit करने होंगे
- PVH boot mode support को Firecracker mainline में merge करना होगा
- PVH booting code Xen support के साथ मिला हुआ है, इसे अलग करना होगा
- FreeBSD arm64 kernel अभी PCI या ACPI support के बिना build नहीं हो सकता; गलत dependencies हटाने से FreeBSD/Firecracker kernel और छोटा हो सकता है
- Intel GPU के लिए memory reservation की जरूरत है या नहीं, यह check करने में 25µs लग रहे हैं, इसलिए छोटा kernel configuration boot time को कुछ microseconds और घटा सकता है
- Longer term में Firecracker को FreeBSD पर चलाने के लिए port करने की संभावना भी है
- Firecracker को Linux KVM के उपयोग को मानकर लिखा गया है
- FreeBSD के bhyve hypervisor kernel हिस्से को इस्तेमाल कराने से रोकने वाली कोई बुनियादी वजह नहीं दिखती
- Experiment करने के लिए FreeBSD 14.0 kernel को amd64
FIRECRACKERkernel configuration से build किया जा सकता है, और Firecracker project कीfeature/pvhbranch इस्तेमाल की जा सकती है- अगर वह branch अब नहीं है, तो इसका मतलब है कि code mainline Firecracker tree में merge हो चुका है
1 टिप्पणियां
Hacker News की राय
मुझे पता ही नहीं था कि Firecracker VM सिर्फ़ कोई साधारण Linux container तकनीक नहीं, बल्कि एक पूरी virtual machine है
शुरू में यह अक्षम लग सकता है, लेकिन fly.io जैसे वास्तविक use cases देखें तो हैरानी होती है कि micro VM बहुत छोटे होने के बावजूद काफ़ी शक्तिशाली हैं
KVM और न्यूनतम hardware support (PCI, ACPI नहीं, आदि) की वजह से Firecracker source काफ़ी सरल है, और non-experts के लिए भी अपेक्षाकृत पढ़ने लायक है
Firecracker के अस्तित्व की वजह यही है
लेख में दिया गया ACPI इसका एक उदाहरण है। फिर भी यह बात सही है कि यह kernel नहीं बल्कि hardware को virtualize करता है, और initialization इतना तेज़ है कि ज़्यादातर users को सामान्य containerd को firecracker-containerd से बदलने पर फ़र्क महसूस नहीं होगा
Firecracker के अलावा crosvm, cloud-hypervisor, Kata के Dragonball जैसे कई micro VM अभी KVM पर develop किए जा रहे हैं
Programming language में नया target implement करना इतना मुश्किल लगता नहीं, इसलिए WASI/WASM जैसे similar OS targets बनाकर supported languages में PR भेजे जाएँ तो overhead का अधिकांश हिस्सा घटाया जा सकता है। सबसे मुश्किल हिस्सा Linux user space की पर्याप्त सटीक नकल करना होगा, लेकिन उसका surface area इतना बड़ा है कि उल्टा similar OS target बनाने वाला रास्ता सबसे अच्छा लगता है
Colin के patches FreeBSD और Firecracker में आ जाएँ तो पूरा kernel boot time 20ms से कम हो जाएगा
सचमुच अविश्वसनीय दौर में जी रहे हैं
जल्दी search करने पर numbers मिल जाते हैं, लेकिन वे कुछ साल पुराने हैं, और boot time मापने का तरीका वही है या “boot time” की परिभाषा वही है, यह भी साफ़ नहीं, इसलिए पता नहीं कि तुलना की जा सकती है या नहीं
यह Colin का हाल का BSDCan presentation है, जो कुछ दिन पहले आया था
https://youtu.be/MT3cdeuRTzs?si=l6baNriUjcvy0ZOE
BSDCan presentation के बाद FreeBSD Journal ने पूछा कि “presentation अच्छा था, क्या आप इसे article में बदल सकते हैं”, और FreeBSD Journal article आने के बाद ;login: ने पूछा कि क्या वे इसे republish कर सकते हैं
qemu में firecracker से प्रेरित microvm है
https://qemu.readthedocs.io/en/latest/system/i386/microvm.ht...
बेशक कुछ तो FreeBSD bug fixes हैं, इसलिए उनकी ज़रूरत तो पड़नी ही है
यह दिलचस्प है कि 1 second wait का बड़ा हिस्सा असल में अनिवार्य नहीं निकलता
गलत machine UUID की वजह से system रुकने पर कितने system administrators ने सच में कोई meaningful action लिया होगा, यह सोचता हूँ
दूसरी ओर “user को reboot करने का message दिखाओ, console पढ़ने के लिए 1 second रुको, फिर reboot करो” वाला मामला थोड़ा अलग है
दिखावा करने जैसा नहीं लगना चाहता, लेकिन जानना चाहता हूँ कि Firecracker instances किस तरह के use cases के लिए सही हैं
मैं FreeBSD को colocation servers से लेकर personal PC तक हर जगह इस्तेमाल करता हूँ, और developer से ज़्यादा पुराने Unix admin जैसा हूँ। Bare metal पसंद है, लेकिन operating system में योगदान देने वाली future technologies का स्वागत है। बस Lambda या Firecracker जैसे buzzwords सुनता हूँ तो भी यह ठीक से नहीं समझ आता कि ये असल में कहाँ इस्तेमाल होते हैं। Docker और containers समझता हूँ और k8s बस मुश्किल से समझता हूँ, लेकिन जब ज़रूरत हो तब इस्तेमाल करने के लिए एक VM चला लेना ही काफ़ी है, तो VM को चालू करके तुरंत खत्म क्यों करना है, यह समझ नहीं आता। क्या यह purely cloud experience या cost saving के लिए है?
इससे compute plane का कोई भी node किसी भी application traffic को handle कर सकता है। कोई application traffic pattern में बदलाव के हिसाब से plane के बचे हुए compute resources को dynamically consume करते हुए बढ़ सकता है, और जब traffic handle नहीं कर रहा हो तो resources इस्तेमाल नहीं करता। Compute plane की capacity बढ़ाने का मतलब और nodes को online लाना है। बड़े पैमाने पर deployments को बहुत manage करने के अलावा कोई खास use case ध्यान में नहीं आता, और अगर environment “scale” वाला नहीं है तो यह ऐसी technology होगी जो vendor boundary के नीचे छिपी रहती है
अगर आप ऐसी service चला रहे हैं जिसकी API बार-बार इस्तेमाल नहीं होती, लेकिन call होने पर जल्दी response देना होता है, तो Lambda या इसी तरह का तरीका ठीक बैठता है। दरअसल mobile phone apps के लिए APIs का बड़ा हिस्सा इसी category में आता है, और ऐसी API calls का response देने के लिए ऐसी machine हमेशा चालू रखना नहीं चाहेंगे जो 99% समय idle रहे
ज़्यादातर ऐसा नहीं करतीं क्योंकि मेहनत बचत से ज़्यादा होती है, लेकिन potential मौजूद है। Lambda और Firecracker जैसी चीज़ें इसे काफ़ी आसान बना देती हैं
यह अफसोसजनक है कि AWS और ARM वाला macOS भी nested virtualization को support नहीं करते
अगर support होता, तो Firecracker-आधारित तकनीक को develop और deploy करना कहीं ज़्यादा आसान होता
असल में किसी भी instance type पर virtualization संभव है, लेकिन hardware acceleration इस्तेमाल कर पाने वाले सिर्फ .metal instances हैं, ऐसा मुझे पता है
Firecracker कमाल का है, लेकिन ऐसे बहुत सारे edge cases हैं जिन्हें document करने की ज़रूरत है
Colin Percival ने यह share किया, इसके लिए बहुत धन्यवाद। खासकर “low-hanging fruit चुन लेने के बाद” वाला वाक्य मुझे पसंद है; Colin के लिए इसका मतलब custom bus_dma patches है। अब कोई भी “1 CPU और 128MB RAM पर FreeBSD kernel 20ms से कम में boot होता है” का फायदा मुफ्त में ले सकता है। अगर आप k8s cluster या बहुत सारे Docker इस्तेमाल करने वाले DevOps से परिचित हैं, तो यह वाकई हैरान करने वाली बात है
मैंने Firecracker को थोड़ा आज़माया, और boot time वादे के मुताबिक मिलता है, लेकिन user experience काफी कठिन है
उदाहरण के लिए, boot सफल होने पर खुश होने के बाद पता चलता है कि networking setup करने के लिए फिर एक लंबा tutorial follow करना पड़ेगा, तो उत्साह कम हो गया
अगर एक single binary download करके run करने पर web interface और API दोनों start हो जाएँ, जल्दी configure किया जा सके, और वह ज़रूरी चीज़ें खुद download कर दे, तो बहुत अच्छा होगा
“1 CPU और 128MB RAM वाली virtual machine में FreeBSD kernel 20ms से कम में boot हो सकता है”
अरे वाह, VM के बिना real hardware पर वही चीज़ कैसे हासिल की जा सकती है ;)
धीमा तो बाकी सब कुछ है। उदाहरण के लिए मेरी machine पर
Startup finished in 14.552s (firmware) + 2.885s (loader) + 741ms (kernel) + 23.116s (initrd) + 11.191s (userspace) = 52.488sहै