← Блог

· 15 мин читања #rust#mqtt#iot#unikernel#open source

urMQTT на правом VPS-у: ISO, руковање од 0,24 ms, сопствени диск и ојачано језгро

Шта се догодило са три обећања из првог текста о urMQTT-у (један ISO, сирови диск без датотечног система, доказиво безбедно) након прва четири дана развоја, са мерењима, табелама и списком онога што још није готово.

У првом тексту о urMQTT-у описали смо уникернел који јесте MQTT брокер, до прекретнице M17, и завршили са три обећања под насловом „Шта следи”: један ISO који се подиже на више платформи, сирови диск без датотечног система и безбедност која може да се докаже, а не само да се тврди. Ништа од тога тада није било изграђено. Овај текст прати шта се догодило са сваким обећањем, истим редом као и раније: шта смо планирали, шта смо изградили, шта нас је изненадило и шта је још отворено. Успут ћемо показати и колико је то трајало, јер је и то део приче.

Једна напомена унапред: пројекат је и даље пред-алфа. Бројке у тексту мере код и тестове, а не зрелост у продукцији, а прва објава је намерно ограничена на VPS-ове са KVM-ом. Bare metal остаје за касније.

Како је то изгледало у времену

Први commit настао је 28. октобра 2026. у 23:48 (M0). До 31. октобра у 23:00 у репозиторијуму је 119 commitова, распоређених као 3, 39, 38 и 39 по календарским данима. Одмах кажимо и како је то могуће: пројекат се гради уз Claude Code, а commitови носе Co-Authored-By: Claude trailere. Четири календарска дана историје не значе да је једна особа за четири дана написала 90 000 редова; значе да се план, имплементација, тестови и ADR-ови пишу у кратким, затвореним петљама, а сваки корак завршава истим ритуалом провере као и раније. Бројке испод говоре о количини кода и тестова, а не о томе колико је све то спроведено у стварном раду.

Раст пројекта током четири дана са прекретницама од M0 до данас: хиљаде редова Rusta (од 1,6 до 89,8), број #[test] функција (од 27 до 739) и број ADR-ова (од 13 до 104), уз стубце commitова по дану (39, 38 и 39 за 29., 30. и 31. октобар).
Раст пројекта током четири дана са прекретницама од M0 до данас: хиљаде редова Rusta (од 1,6 до 89,8), број #[test] функција (од 27 до 739) и број ADR-ова (од 13 до 104), уз стубце commitова по дану (39, 38 и 39 за 29., 30. и 31. октобар).

График показује три криве које расту готово заједно, што и јесте намера: тестови и записане одлуке прате код, а не касне за њим. Нагиб је најстрмији 29. октобра, када настаје цео пут од језгра до брокера. Даље се редови додају спорије, али тестови расту брже од кода (27 тестова на 1,6 хиљада редова на почетку, 739 на 89,8 хиљада данас).

Прекретница Commit Време Rust редова Тестова ADR-ова
M0 9412a9c 28.10. 23:48 1 592 27 13
M5 a61083b 29.10. 01:32 9 314 88 24
M9 4be8cc9 29.10. 02:21 15 729 149 28
M11 4800904 29.10. 03:43 22 737 180 30
M13 a558d5e 29.10. 05:22 26 556 193 32
M17 7df4f98 29.10. 08:48 33 504 232 36
S1 ecf72ec 30.10. 00:46 41 910 299 45
X8 fb1504a 30.10. 19:08 56 341 456 59
PERF 76f7c36 31.10. 14:19 72 511 618 76
данас HEAD 31.10. 23:00 89 790 739 104

Редови су све .rs датотеке изван vendor/ и fuzz/, а тестови су #[test] функције. Шта је који део додао, по датуму из git историје:

Датум Прекретнице Шта је додато
29.10. M0 до M17 језгро, мрежа, TLS, брокер, SQLite, више језгара, сопствени TCP, ојачање, репродуцибилно издање
29.10. увече и 30.10. X0 до X10, S1, H2, H3 (X0 почиње 29.10. у 20:54) порт на x86_64, универзални ISO, један диск и самоинсталирајући ISO, TLS провере, нападачки клијенти
31.10. O1 до O4, T1, PERF, H4, H5, H9, S2 до S6 увид и конзола преко MQTT-а, живи режим рада и дијагностика, SNTP, убрзања, ојачање, верификација, ланац набавке, похрана

Обећање 1: један ISO за више платформи

Шта смо планирали. Једна датотека која се подиже на aarch64 UEFI-ју, x86_64 UEFI-ју и x86_64 BIOS-у. Шта смо изградили. Порт на x86_64 (X0 до X10) и универзални ISO са Limine bootloaderom, један хибридни ISO са ког се подижу све те комбинације; smoke тестови га подижу на четири виртуелне машине. Мени има три уноса: urMQTT (живи режим рада, ради из RAM-а, ништа не пише на диск), инсталација на диск машине на којој је покренут (инсталација пише GPT и податке, па гаси машину) и Дијагностика (детекција хардвера и мерења).

Boot мени ISO-а снимљен на VPS-у: уноси „urMQTT”, „Install urMQTT on this machine's disk” и „Diagnostic: hardware detection and benchmarks” са одбројавањем до аутоматског подизања. Снимак је са пробног ISO-а са дужим одбројавањем; подразумевано је 10 секунди.
Boot мени ISO-а снимљен на VPS-у: уноси „urMQTT”, „Install urMQTT on this machine's disk” и „Diagnostic: hardware detection and benchmarks” са одбројавањем до аутоматског подизања. Снимак је са пробног ISO-а са дужим одбројавањем; подразумевано је 10 секунди.

Прва стварна мета био је VPS, а не физички рачунар: одлука је да прво издање подржава само VPS (KVM), а bare metal долази после њега. Тестирали смо на Contabo VPS-у са AMD EPYC процесором (породица 23, Zen 1), 4 vCPU-а и 8 GiB RAM-а, са virtio-net мрежом и virtio-scsi диском. Први покушај нас је изненадио: ISO прикључен као виртуелни CD подизао је задати унос, продукцијска слика одбила је да служи са медијума само за читање и ресетовала се, па смо добили бесконачну петљу мени, језгро, ресет без иједне читљиве поруке. Данас фатална грешка у продукцијској слици оставља поруку на конзоли 60 секунди пре ресетовања, а са оптичког медијума исписује да је подигнуто са медијума само за читање и стаје. Живи режим рада у RAM-у (O3) касније је тај проблем заобишао за уобичајени случај.

Дијагностички унос настао је из тог искуства: машину коју не видимо морамо моћи да питамо шта има. Исписује CPU, RAM, мрежу, дискове и PCI уређаје, мери пропусност меморије, диск и криптографију, а иста мерења исписује као JSON на серијски порт. Записивање на диск дозвољено је само у нераспоређеном простору и тек након што се укуца WIPE; на снимку испод то је прескочено.

Дијагностички екран на Contabo VPS-у (AMD EPYC, KVM, 4 vCPU): систем, меморија, диск, PCI уређаји, пропусност и латенција RAM-а те крипто-мерења, укључујући 244 us по mTLS руковању, X25519 48 us, ECDSA потпис 35,7 us и проверу 107 us.
Дијагностички екран на Contabo VPS-у (AMD EPYC, KVM, 4 vCPU): систем, меморија, диск, PCI уређаји, пропусност и латенција RAM-а те крипто-мерења, укључујући 244 us по mTLS руковању, X25519 48 us, ECDSA потпис 35,7 us и проверу 107 us.

Шта је отворено. Прави хардвер. После првог издања следи bare metal (USB подизање, NVMe и AHCI, мрежна картица стварног рачунара), заједно са IOMMU-ом и Secure Bootом, јер без IOMMU-а злонамеран или неисправан уређај може писати по целој меморији. У овој верзији о томе не тврдимо ништа.

Руковање: од 1,2 ms до 0,24 ms

Прво мерење дијагностике на VPS-у дало је незгодну бројку: mTLS руковање, само криптографија на страни сервера, трајало је 1,2 ms. На aarch64 смо већ користили хардверске AES и SHA инструкције, али x86_64 није имао ништа слично за јавни кључ: RustCrypto је преносив и constant-time, па и спор. Три промене су то спустиле.

  • За P-256 смо преузели nistz256 асемблер из OpenSSL-а и ring-а (ADR-130), уз табеле са фиксном базом за потписивање и проверу потписа CA (ADR-110, ADR-111).
  • X25519 је преписан на четири 64-битна уда са MULX/ADX инструкцијама (ADR-131).
  • Након тога смо профилисали и остатак (ADR-132, ADR-200).
Руковање на VPS-у: 1200 us пре, 237 us после асемблера и табела; A/B поређење memcpy-а на истом покретању, 278,8 us са старим, 229,4 us са новим; те пре и после за X25519 (146 на 48 us), ECDSA потпис (673 на 34 us) и проверу (309 на 90 us).
Руковање на VPS-у: 1200 us пре, 237 us после асемблера и табела; A/B поређење memcpy-а на истом покретању, 278,8 us са старим, 229,4 us са новим; те пре и после за X25519 (146 на 48 us), ECDSA потпис (673 на 34 us) и проверу (309 на 90 us).
Мерење на VPS-у (server, микросекунде) Пре После
mTLS руковање, цела криптографија 1200 око 240 (237 у мерењу са графа, 244 на снимку)
X25519 146 48
ECDSA потпис 673 34
ECDSA провера 309 90 (107 на снимку)
руковање, стари memcpy, исти boot 278,8 229,4 (-17,7%)

Бројке се разликују по покретању око 10%, па смо записали распон уместо једне вредности. Укупно је то отприлике пет пута мање CPU времена за руковање.

Шта смо научили о memcpy-у. Кад је криптографија пала, профил је показао нешто што нисмо очекивали: функције mem* (memcpy, memmove, memset) трошиле су 7% руковања, 13 до 39% пута TLS записа (39% за 64 B, 13% за 4 KiB) и 17 до 34% разгранавања PUBLISH-а. Заменили смо их SSE2 асемблером за x86_64 (ADR-200), уз диференцијалне тестове према референтној имплементацији за све величине, поравнања и преклапања. По позиву је то 55 до 75 циклуса према 7 до 12 за 1 до 64 бајта.

Лево: удео времена сервера у mem* функцијама пре преписивања (руковање 7%, TLS запис 64 B 39%, 256 B 37%, 1 KiB 24%, разгранавање 17 до 34%). Десно: колико је сваки пут бржи после (руковање 17,7%, 64 B 26,4%, 256 B 19,6%, 1 KiB 16,1%, 4 KiB 6,6%, разгранавање 7 до 34%).
Лево: удео времена сервера у mem* функцијама пре преписивања (руковање 7%, TLS запис 64 B 39%, 256 B 37%, 1 KiB 24%, разгранавање 17 до 34%). Десно: колико је сваки пут бржи после (руковање 17,7%, 64 B 26,4%, 256 B 19,6%, 1 KiB 16,1%, 4 KiB 6,6%, разгранавање 7 до 34%).
Пут Удео mem* пре Брже после
руковање 7% 17,7%
TLS запис 64 B 39% 26,4%
TLS запис 256 B 37% 19,6%
TLS запис 1 KiB 24% 16,1%
TLS запис 4 KiB 13% 6,6%
разгранавање порука 17 до 34% 7 до 34%

Читање графика: што је запис мањи, то већи део времена одлази на копирање, па је и добитак већи. За мале поруке, које су већина MQTT саобраћаја, управо то је оно што се рачуна.

Шта ово не значи. На нативном x86_64 смо OpenSSL 3.5.5 (ADR-130) мерили само као референцу за асемблер, а не као поређење производа. Поређење Linux плус Mosquitto на истом VPS-у (задатак X11) још нисмо урадили. Зато не тврдимо да је urMQTT бржи од Linuxа: није измерено. Бројке изнад пореде urMQTT са самим собом.

Преоптерећење је био други резултат. На VPS-у, са једног Maca (RTT 35 ms), 128 клијената дало је 618 руковања у секунди, 256 клијената 393, а 512 клијената 67, уз хиљаде одбијених веза. То није био плато него урушавање. Узроке смо пронашли у коду (два су потврђена у QEMU-у/HVF-у, на VPS-у се нису могли поновити), више њих одједном: одбијена веза враћала је клијента преко следећег сегмента који носи важећи SYN колачић, одбијање је RST-ом говорило клијенту да одмах покуша поново, а пријем је био O(ред). Исправак (ADR-080 до ADR-082) испушта SYN који не би био прихваћен, памти одбијене токове, држи пријем у O(1) и броји руковања по језгру. Могуће је (хипотеза, није проверено) да је део узрока био и на клијенту: алат за оптерећење је затварао први, па је један Mac са 16 384 ефемерних портова и TIME_WAIT-ом од 30 секунди ограничен на око 550 веза у секунди. Зато прави строп сервера још нисмо измерили.

Обећање 2: сирови диск, али пажљив

Шта смо планирали. Задржати сопствени распоред без датотечног система и додати све оно што датотечни систем иначе даје: поравнање, раст, discard, сигурносне копије и провере. Зашто не датотечни систем. О томе смо расправљали и одлука је потврђена: urMQTT пише једну базу, један телеметријски дневник и сопствене метаподатке, не треба му директоријуми, дозволе, ни имена датотека, а сваки додатни слој био би још кода у поверљивом делу и још један начин да запис тихо нестане. Диск је GPT са ESP партицијом и једном urMQTT партицијом података, на којој је сопствени распоред.

Шта смо изградили (S2 до S6).

  • Распоред v2 (S2). Топологија диска, поравнање на 1 MiB или оптималну величину I/O-а, цели физички блокови, A/B пар суперблокова са генерацијом (ADR-140). Распоред v1 кернел одбија уз јасну поруку, а мигрира га ванмрежни алат urmqtt-disk migrate (ADR-141): задато је пробни пролаз, копира се па тек онда пребацује, уз повраћај и убризгавање грешке на сваком запису.
  • Раст (S3). При подизању диск може да порасте (никад се не смањује), а на virtio-blk и за време рада, преко прекида промене конфигурације (ADR-142, ADR-143).
  • Discard и write-zeroes (S4). Танко додељени дискови враћају простор домаћину. У тесту је qcow2 датотека на домаћину пала са 62 на 16 MiB након што су истекли телеметријски сегменти, а опоравак никад не верује садржају који је одбачен.
  • Сигурносна копија базе на диску (S5). A/B копија помоћу SQLite online backup API-ја у корацима по 16 страница: најдужи корак 4,6 ms наспрам 445 ms кад се копира у једном позиву. Комитови током копије имали су максимум 2,4 ms према 2,2 ms без ње. Ако quick_check при подизању не успе, база се враћа из копије.
  • Чишћење и ограничено подизање (S6). Scrubber чита диск уз ограничен I/O буџет и пријављује оштећене записе, а подизање чита само најновији сегмент сваке партиције, тако да време подизања не расте са величином телеметријског региона. Тиме смо затворили проблем који смо унапред израчунали: на великом диску потпуни опоравак трајао би од минута до десетина минута.

Цела група smoke тестова за похрану пролази на обе архитектуре: 79 успешних, 0 неуспешних, на aarch64 под HVF-ом и на x86_64 под TCG-ом. Мерили смо и да S4 није успорио базу: 53,3 хиљаде уметања у секунди у пакетима пре и после, без измерљиве разлике.

Шта је изненадило. Колико од „једноставног” распореда отпада на рубне случајеве: virtio-scsi и virtio-blk пријављују топологију на различите начине, а непотпун запис у сред миграције мора увек оставити бар једну важећу верзију. Зато је сваки запис на путу миграције и раста прошао убризгавање грешке.

Шта је отворено. SQLite странице немају контролни збир, па промењен бит унутар вредности у реду база неће открити; телеметријски записи и садржај копија имају CRC и откривају се. Шифровање диска (S7) није готово.

Обећање 3: доказиво безбедно, колико год успемо

Ојачање (H4). Алокатор брише ослобођену меморију и проверава је при поновној употреби, открива двоструко ослобађање, веже показиваче слободне листе XOR-ом и нулира кључеве при ослобађању (ADR-170). Заштита стека (-Zstack-protector=strong, 1315 функција, а SQLite са -fstack-protector-strong, ADR-171). Извештаји о грешкама у продукцији не садрже регистре ни адресе (ADR-172). KASLR на обе архитектуре: кернел је PIE и Limine га помера за око 19 бита, а насумично се постављају heap, стекови и MMIO прозор (ADR-173). На aarch64 MTE означава heap (ADR-174), али само под TCG-ом: HVF и x86 га немају. kCFI смо проценили и нисмо га прихватили јер преводилац на прикаченом nightlyју пада са унутрашњом грешком; IOMMU је одгођен са bare metalом (ADR-175, ADR-176).

Цена је измерена, не претпостављена. На путу руковања (dp-bench, aarch64 HVF, пуна TLS 1.3 + mTLS + CONNECT руковања):

Корак 1 језгро 4 језгра Промена
без ојачања 1941 7620
+ алокатор 1898 7429 -2,2% до -2,5%
+ заштита стека 1845 7243 заједно -4,9% до -5%
+ PIE и ознаке око 1800 око 6750 на оптерећеном рачунару, шумовито

Свеукупно је дакле цена на путу руковања отприлике 5% за делове мерене чисто, а са PIE-јем и ознакама излази око 7% на једном и око 11% на четири језгра, али то је мерење шумовито. Сматрамо је прихватљивом, али нисмо је поновили на x86_64 VPS-у.

Вреди упоредити са бројком из првог текста (862 руковања у секунди на једном језгру). Исти алат данас, са погоном хардверског криптовања на aarch64 (X0b) и низом убрзања која су стигла од тада, даје 1941 пре ојачања. Нисмо изоловали допринос сваке промене, па о узроку не тврдимо ништа.

Како је проверено, и шта није. H5 је додао Miri на подскуп host crateова, Kani доказе, санитајзере, модел-фазер за ACL и извештаје о покривености. Kani је доказао record_len у TLS-у, parse у TCP-у и две аритметике точка тајмера. Није доказано (истекло време): оба MQTT декодера, оба парсирања конфигурације и понашање точка тајмера; CI посао за Kani зато још не блокира. Miri не може да извршава асемблер. Ланац набавке (H9): CycloneDX SBOM са 215 компоненти, cargo-vet пролази са 39 потпуно ревидираних и 140 изузетака, а cargo-deny је конфигурисан да савете о рањивостима третира као пад CI-ја (ти CI послови до сада су проверени само локално, не на GitHubу, а негативни тест није покренут), уз VULNERABILITY-HANDLING.md. Искрено стање: од 67 кернел-линкованих верзија трећих crateова, 53 су изузеци, а не ревизије. Vet доказује да се скуп зависности не може променити без видљиве разлике, а не да је TCB прегледан. Сопствену ревизију ни једног cratea нисмо написали.

SNTP (T1) је у том смислу мала прича о безбедности: опционалан је, само према приватним IPv4 серверима, са максималним скоком и условом да се извори слажу (медијан, већина унутар једне секунде). Сат који било ко на путу може померити није сат коме смемо веровати код важења сертификата.

На Macu: aarch64 изворно и x86_64 емулацијом

Развијамо на Macu, а тај има два начина за покретање истог кода и они су веома различити.

aarch64 под HVF-ом x86_64 под TCG-ом
Шта је то изворно извршавање, брзина близу стварног процесора QEMU емулира инструкције
Руковање у госту око 0,58 ms (први пут мерено око M13); dp-bench данас 1941/s по језгру око 3,3 ms
TLS smoke сценарији брзи око 15 минута по сценарију
MTE не (нема га на HVF-у) x86 га нема; означавање heapа ради само за aarch64 госта под TCG-ом
Чему служи стварна мерења исправност: x86 пут се проверава, бројке нису перформансе

Правило је једноставно: бројке под TCG-ом су емулација и не цитирамо их као перформансе. x86 перформансе мерим само на правом VPS-у. Зато криптографске бројке изнад долазе са Contabo VPS-а, а не са Maca.

Лекције

  • Мерите на стварној машини пре оптимизације. Први приступ био је оптимизовати оно што смо мислили да је споро; тек је дијагностика на VPS-у показала 1,2 ms и где тачно одлази време.
  • Преоптерећење је засебна функција. Систем који је брз под оптерећењем може се урушити изнад њега; одбијање мора бити јефтиније од прихватања.
  • Профил изненађује. Највећи појединачни добитак за мале поруке није био у криптографији него у копирању меморије.
  • Диск без датотечног система тражи више посла, не мање. Оно што систем датотека иначе даје (поравнање, раст, discard, копије, провере) морамо написати и проверити сами, сваки корак са убризгавањем грешака.
  • Искреност у бројкама. Меморијска мерења на дељеном VPS-у варирају до 10 пута међу покретањима и не говоре ништа о коду, па их не наводимо. Крипто бројке су стабилне до око 10%.
  • Брзина развоја није зрелост. Тестови и ADR-ови расту уз код, али четири дана нису прави погон.

Шта следи

  • H6: SQLite изолован на нивоу EL0, тако да грешка у бази не отвара језгро.
  • H7: остатак безбедности времена (NTS и обрада скокова); SNTP је тек први корак.
  • X11: поређење са Linuxом и Mosquittom на истом VPS-у, тек тада има смисла говорити о релативној брзини.
  • Издање: прва јавна објава за VPS (KVM), уз потписане артефакте и упутства.
  • После издања: bare metal (X12 до X16), IOMMU и Secure Boot, те шифровање диска.

Код је на github.com/zeljan-alduk/urMQTT, а docs/PROGRESS.md и даље је једини извор истине о томе шта стварно ради. Пред-алфа, као и раније; пријаве грешака и безбедносне пријаве по упутствима у SECURITY.md добродошле су.

Коментари

Правила коментарисања

Правила коментарисања

Коментари су ту за размену искустава и питања о темама блога. Да би расправа била корисна свима, важе ова правила.

  1. Пристојност Без увреда, претњи, узнемиравања, говора мржње и дискриминације по било ком основу. Критикујте аргумент, не особу.
  2. На тему Коментар треба да се односи на објаву или на расправу испод ње.
  3. Без реклама и спама Без оглашавања, ланчаних порука и веза без контекста. Везе и слике су добродошле кад доприносе расправи.
  4. Приватност других Не објављујте туђе личне податке: имена и презимена приватних лица, адресе, телефоне, е-пошту, фотографије без пристанка.
  5. Ауторска права Објављујте само садржај који смете да делите. Код цитирања наведите извор.
  6. Један идентитет Користите један надимак и не представљајте се као неко други.
  7. Потврда е-поштом Коментар се објављује тек кад потврдите везу из е-поште. Адреса се не приказује и не користи се ни за шта друго; непотврђени коментари се бришу након 48 сати.
  8. Модерација Коментаре који крше правила скривамо или бришемо без најаве, а расправу можемо закључати. Одлуке модератора су коначне.
  9. Одговорност За садржај коментара одговара његов аутор. Коментари не одражавају ставове ALDO TECH-а.
  10. Пријава Непримерен садржај пријавите на info@aldo.tech.