← Späť na blog

Prečo si budujem vlastné operačné centrum pre homelab (a čo ma to zatiaľ naučilo o testovaní)

25. júla 2026 · 8 min čítania

self-hostedhomelab

Rozpracovaný projekt, nie hotový produkt — internal alpha, aktívne v štádiu spevňovania (hardening) pred prípadným ďalším krokom. Tento článok popisuje, ako sa buduje, nie hotový výsledok.

Prečo vlastný nástroj namiesto hotového monitoringu

Homelab s desiatkami docker kontajnerov, viacerými fyzickými strojmi a rôznorodými službami (media stack, domáca automatizácia, vlastné appky) rýchlo narazí na jeden problém: existujúce monitoring nástroje buď ukazujú príliš veľa nepodstatných grafov, alebo príliš málo skutočného kontextu — vidíš, že niečo je červené, ale nie prečo, ani čo s tým.

MidgardOps vzniká ako vlastná odpoveď na tento problém — postupne budované "operačné centrum" pre homelab: systém, Docker, úložisko, sieť. (V kóde a v Docker kontajneroch sa projekt volá heimdall — pracovný/interný kódový názov, ktorý ostal z raného štádia vývoja; produkt sám sa volá MidgardOps.) Nie je to hračka vedľa — má reálne nasadené agenty na spravovaných hostoch, reálne akcie na diaľku (štart/stop/reštart služby). Dôležité pre správne pochopenie tohto článku: appka je momentálne v štádiu, ktoré vlastná dokumentácia projektu označuje ako "Core Baseline / internal alpha" — aktívne sa spevňuje (hardening), nie je to hotový, nasadený produkt pripravený na verejné použitie. Časť z toho, čo je nižšie opísané, je preto skôr o procese, akým appka vzniká, než o hotovom výsledku.

Pravidlo, ktoré appku formovalo najviac: "over to naozaj"

Najcharakteristickejší vzorec naprieč celým vývojom nie je konkrétna funkcia, ale opakovaný rituál: po dokončení práce, ktorá už vyzerala hotovo a otestovane, príde otázka "prejdi to znova, určite ešte niečo nájdeš" — a takmer vždy sa niečo nájde.

Jeden konkrétny večer to ukazuje najlepšie. Po dokončení viacerých funkcií, ktoré prešli vlastnou test sadou aj vizuálnou kontrolou, prišiel explicitný re-audit tej istej práce. Výsledok: tri ďalšie reálne problémy, ktoré predchádzajúce kolo prehliadlo:

  • Tlačidlo na kopírovanie diagnostiky ticho nefungovalo na skutočnom nasadení — appka beží cez obyčajné HTTP (nie HTTPS), a prehliadačové API na kopírovanie do schránky vyžaduje zabezpečený kontext. Backend volanie prešlo v poriadku (200 OK v reálnom logu z používateľovho telefónu), ale samotné kopírovanie by zlyhalo bez akejkoľvek viditeľnej chyby.
  • Reálna výkonnostná regresia — funkcia rozšírená len o pár hodín skôr sa volala prakticky z každého endpointu appky, nielen tam, kde bola pôvodne potrebná, a pridávala pol sekundy k takmer každej odpovedi. Opravené jednoduchým krátkodobým cache.
  • Názov domény konkrétneho nasadenia unikol priamo do zdrojového kódu appky — ktorá je pritom zámerne stavaná ako produkt pre kohokoľvek, nie viazaná na jeden konkrétny stroj. Tento konkrétny prípad chytil vlastný regresný test, ktorý presne na toto existuje.

Poučenie zapísané priamo do interného denníka projektu: "overené naživo" v praxi vždy znamenalo niečo užšie, než sa na prvý pohľad zdalo. Pýtať sa na explicitný re-audit dokončenej práce nie je zbytočné opakovanie — je to samostatný krok, ktorý nachádza veci, aké prvé kolo systematicky nevidí.

Bugy, ktoré sa nedali nájsť čítaním kódu

Viacero z najzávažnejších nálezov v appke zdieľa jeden spoločný znak: vyzerali by v code review úplne v poriadku.

CSRF kontrola, ktorá bola logicky správna a napriek tomu blokovala legitímne požiadavky. Appka porovnáva Origin hlavičku prehliadača s Host hlavičkou, ktorú posiela nginx. Problém: toto konkrétne nasadenie beží na neštandardnom porte, takže prehliadač do Origin port zahŕňa (pri štandardných portoch 80/443 by ho vynechal), zatiaľ čo nginx posielal hostname bez portu bez ohľadu na to, na akom porte appka reálne počúva — tie dve hodnoty sa teda nemohli zhodovať, bez ohľadu na to, aké správne bolo samotné porovnávacie pravidlo. Nájdené až reálnym klikom v prehliadači, nie žiadnym z desiatich pôvodných jednotkových testov, ktoré cez skutočnú nginx konfiguráciu vôbec neprechádzali.

iOS vyžaduje kopírovanie do schránky synchrónne. Predchádzajúca oprava kopírovacieho tlačidla stále čakala (await) na dokončenie sieťového volania pred samotným skopírovaním — na desktope to fungovalo, na iOS-e prehliadač vyžaduje, aby sa volanie clipboard API zavolalo priamo, synchrónne, v obsluhe kliknutia. Opravené predzískaním dát vopred, aby bol samotný klik čisto synchrónny.

Binárne rámcovanie v Dockerových logoch. Keď kontajner beží bez alokovaného terminálu (bežný prípad), Docker API multiplexuje stdout a stderr do vlastného binárneho formátu s 8-bajtovou hlavičkou pred každým riadkom. Appka to nikdy nedemultiplexovala — logy sa zobrazovali s viditeľnými netlačiteľnými znakmi na začiatku každého riadku. Nájdené vizuálne, priamo cez screenshot počas testovania v reálnom prehliadači.

Symlink, ktorý sa nerozbaľoval tam, kde mal. Kolektor veľkosti systémového žurnálu narazil na to, že tento konkrétny hostiteľ presúva žurnál na iný disk cez absolútny symlink — ktorý sa pod bind-mountom vyhodnocoval voči koreňu kontajnera, nie voči hostiteľovi. Opravené všeobecným riešením cesty, nie hackom pre jeden konkrétny prípad.

Spoločný záver zo všetkých štyroch: kód môže byť logicky bezchybný a napriek tomu nefunkčný v reálnom prostredí, keď závisí od presných hodnôt, ktoré si dopĺňa prehliadač, konkrétny operačný systém, alebo konfigurácia infraštruktúry okolo appky (nginx, bind-mounty). Jednotkové testy nad izolovanou logikou toto systematicky nevidia — treba appku skutočne spustiť a použiť.

Keď audit appky odhalí, že appka meria dve rôzne veci dvoma rôznymi spôsobmi

Jeden z väčších architektonických nálezov prišiel z jednoduchej otázky: prečo klik na incident niekedy ukáže len generickú odporúčanie namiesto konkrétnej diagnózy? Vyšetrenie odhalilo, že appka mala dva úplne nezávislé systémy na určenie "čo je zle" — jeden pre celkové skóre na hlavnom dashboarde, druhý pre zoznam konkrétnych problémov — počítajúce nad rovnakými dátami oddelene, s vlastnými prahmi. Jeden z nich navyše vôbec nekontroloval spotrebu pamäte — syntetický test s hostom na 97 % RAM dostal od tohto systému čisté skóre bez jedinej poznámky.

Zjednotenie oboch systémov do jednej spoločnej logiky prišlo až o pár dní neskôr, ako samostatná, vedome naplánovaná úloha — presne preto, že narýchlo zlepenej opravy dvoch paralelných systémov merania stavu by pravdepodobne vzniklo viac skrytých nezhôd, než by vyriešila.

Bezpečnostný dlh, ktorý sa priznáva nahlas namiesto skrývania

Zaujímavý zvyk, ktorý sa objavil pri pridávaní ďalších troch kolektorov dát (TLS certifikáty, stav záloh, stav plánovaných úloh): každý z nich potreboval privilegovaný prístup k hostiteľskému systému cez nástroj nsenter. Namiesto toho, aby táto rastúca závislosť na privilegovanom prístupe zostala nepovšimnutá, appka má samostatný dokument s aktívnym varovaním presne na tento typ architektonického dlhu — a pravidlo, že každý nový kolektor tohto typu sa doň musí zapísať hneď, nie dodatočne.

Tento zvyk — vedome si priznať dlh a naplánovať, ako sa z neho časom vyjde (napríklad presunom na existujúci, menej privilegovaný mechanizmus namiesto privilegovaného kontajnera) — je presne to, čo odlišuje appku budovanú s ohľadom na bezpečnosť od appky, ktorá len "funguje".

Veľký upratovací audit: keď appka narastie rýchlejšie než jej dokumentácia

Po sérii rýchlych prírastkov (root-cause korelácia medzi problémami, viditeľnosť zlyhaných plánovaných úloh, sledovanie expirácie TLS certifikátov, stav zálohovania) prišla samostatná, päťkolová upratovacia fáza:

  1. Zmazaný mŕtvy legacy systém spúšťania akcií — štyri API cesty bez jediného volajúceho z frontendu, jedna z nich navyše unikala premenné prostredia do odpovede. Čisto zmazané, nie ponechané "pre istotu": -866 riadkov kódu.
  2. Opravených päť reálnych bugov nájdených pri samotnom audite — vrátane logickej chyby, ktorá nesprávne označovala neznámu závažnosť ako kritickú, a duplicitného načítavania tých istých dát na jednej stránke.
  3. Odstránený ďalší mŕtvy kód — opustené pokusy o dátové modely, prázdne priečinky, nepoužívané importy. Veľkosť výsledného CSS balíka reálne klesla, čo potvrdilo, že odstránený kód bol naozaj mŕtvy, nie len teoreticky nepoužívaný.
  4. Doplnené testy pre moduly, ktoré predtým nemali žiadne — čisto pridávacia zmena, žiadny runtime kód sa nemenil.
  5. Uprataná dokumentácia tak, aby presne odrážala, čo je už hotové a čo je stále len nápad.

Uzáver tohto auditu je rovnako dôležitý ako jeho výsledky: žiadny zo zistených problémov si nevyžiadal štrukturálny prepis — všetko boli malé, cielené opravy. To je znak toho, že appka bola priebežne budovaná s primeranou disciplínou, nie že by potrebovala záchranu.

Čo si z toho odniesť

  • "Hotovo a otestované" je vždy len také dobré, ako to, čo sa reálne testovalo. Explicitný re-audit tej istej práce po jej dokončení opakovane nachádzal veci, ktoré prvé kolo prehliadlo.
  • Logicky správny kód môže byť prakticky nefunkčný. Presnosť hlavičiek, ktoré si dopĺňa prehliadač, správanie API na konkrétnom operačnom systéme, alebo spôsob, akým sa vyhodnocujú symlinky pod bind-mountom — toto sa dá overiť len skutočným behom, nie čítaním.
  • Dva nezávislé systémy merajúce to isté sú horšie než jeden nedokonalý. Keď appka rástla organicky, dva paralelné spôsoby výpočtu "čo je zle" sa rozišli bez toho, aby to niekto zámerne spôsobil.
  • Architektonický dlh sa oplatí zapisovať v momente, keď vzniká, nie objaviť ho neskôr ako prekvapenie. Pravidlo "každý nový rizikový kolektor sa musí zapísať hneď" je jednoduché, ale funguje presne preto, že je mechanické, nie závislé od toho, či si na to niekto spomenie.
  • Pravidelný upratovací audit má zmysel aj vtedy, keď appka "funguje". Mŕtvy kód a rozídené systémy sa hromadia ticho — a ich odstránenie po fakte je oveľa lacnejšie než ich neskoršie nechať spôsobiť skutočný bug.

MidgardOps dnes beží ako živé, no stále rozpracované operačné centrum nad reálnym homelabom — v štádiu internal alpha, s otvorenými položkami (napr. plnohodnotný owner/"break-glass" prístup, first-run wizard pre nasadenie, presun z privilegovaného nsenter prístupu na menej rizikový mechanizmus). Nie je hotový preto, že by bol napísaný bez chýb, ale preto, že proces jeho vzniku počíta s tým, že chyby vzniknú — a stavia do seba spôsob, ako ich nachádzať skôr, než niekomu spôsobia problém.

Rovnaká disciplína "over to naozaj" sa vinie aj cez Sindri (katalóg skriptov) a Muninn (archivácia dokumentov).