{"id":4149,"date":"2026-07-14T09:02:53","date_gmt":"2026-07-14T07:02:53","guid":{"rendered":"https:\/\/www.sycope.com\/?p=4149"},"modified":"2026-07-14T09:06:00","modified_gmt":"2026-07-14T07:06:00","slug":"architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite","status":"publish","type":"post","link":"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite","title":{"rendered":"Architektura LinkSense: dlaczego agent\u2013serwer, Rust i SQLite"},"content":{"rendered":"<p><strong><a href=\"https:\/\/www.sycope.com\/pl\/post\/linksense-lekkie-open-sourceowe-narzedzie-do-synthetic-monitoringu-od-zespolu-sycope\">W pierwszym tek\u015bcie<\/a><\/strong> pokazali\u015bmy, czym jest LinkSense \u2014 lekkie, open source narz\u0119dzie do synthetic monitoringu. Tutaj schodzimy o poziom ni\u017cej, do decyzji architektonicznych. Bo akurat w tym projekcie niemal ka\u017cdy wyb\u00f3r \u2014 od modelu komunikacji, przez j\u0119zyk programowania, po format bazy danych \u2014 wynika z konkretnego problemu, a nie z mody. Ten tekst jest dla os\u00f3b, kt\u00f3re chc\u0105 wiedzie\u0107 dlaczego to dzia\u0142a tak, jak dzia\u0142a: dla administrator\u00f3w, zespo\u0142\u00f3w bezpiecze\u0144stwa, architekt\u00f3w i devops\u00f3w.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_89 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Model_agent%E2%80%93serwer_dlaczego_agent_laczy_sie_z_serwerem_a_nie_odwrotnie\" >Model agent\u2013serwer: dlaczego agent \u0142\u0105czy si\u0119 z serwerem, a nie odwrotnie<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Bezpieczenstwo_zero_otwartych_portow_po_stronie_agenta\" >Bezpiecze\u0144stwo: zero otwartych port\u00f3w po stronie agenta<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Masowa_rekonfiguracja_z_poziomu_serwera\" >Masowa rekonfiguracja z poziomu serwera<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Local-first_dane_przede_wszystkim_lokalnie\" >Local-first: dane przede wszystkim lokalnie<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Agregacja_dlaczego_akurat_co_60_sekund_i_zapis_w_paczkach\" >Agregacja: dlaczego akurat co 60 sekund i zapis w paczkach<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Model_danych_co_dokladnie_trafia_do_bazy\" >Model danych: co dok\u0142adnie trafia do bazy<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Dlaczego_Rust\" >Dlaczego Rust<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Dlaczego_SQLite\" >Dlaczego SQLite<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Konfiguracja_jako_kod_TOML\" >Konfiguracja jako kod: TOML<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Wdrozenie_binarki_CI_i_pulapka_pinga_na_Linuksie\" >Wdro\u017cenie: binarki, CI i pu\u0142apka pinga na Linuksie<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Logi_dlaczego_nie_trafiaja_na_standardowe_wyjscie\" >Logi: dlaczego nie trafiaj\u0105 na standardowe wyj\u015bcie<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Swiadome_%E2%80%9Enie%E2%80%9D_kontenery\" >\u015awiadome \u201enie\u201d: kontenery<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/www.sycope.com\/pl\/post\/architektura-linksense-dlaczego-agent-serwer-rust-i-sqlite\/#Podsumowanie\" >Podsumowanie<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Model_agent%E2%80%93serwer_dlaczego_agent_laczy_sie_z_serwerem_a_nie_odwrotnie\"><\/span>Model agent\u2013serwer: dlaczego agent \u0142\u0105czy si\u0119 z serwerem, a nie odwrotnie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Punktem wyj\u015bcia by\u0142o jedno za\u0142o\u017cenie: chcemy mie\u0107 wielu agent\u00f3w w r\u00f3\u017cnych lokalizacjach. Z tego wynika reszta. Skoro agent\u00f3w ma by\u0107 wielu i maj\u0105 by\u0107 rozsiani po \u015bwiecie, logiczn\u0105 konsekwencj\u0105 jest centralny serwer, do kt\u00f3rego wysy\u0142aj\u0105 dane.<\/p>\n<p class=\"isSelectedEnd\">Kluczowa decyzja dotyczy jednak kierunku po\u0142\u0105czenia. W LinkSense to agent nawi\u0105zuje po\u0142\u0105czenie z serwerem, czyli wykonuje po\u0142\u0105czenie wychodz\u0105ce, a nie serwer \u201epuka\u201d do agent\u00f3w, czyli inicjuje po\u0142\u0105czenie przychodz\u0105ce. Wyb\u00f3r tego kierunku ma dwa uzasadnienia \u2014 bezpiecze\u0144stwo i \u0142atwo\u015b\u0107 zarz\u0105dzania \u2014 i to one zdefiniowa\u0142y ca\u0142\u0105 architektur\u0119.<\/p>\n<p class=\"isSelectedEnd\">Agent wykonuje zlecone testy, zapisuje wyniki lokalnie, a co 60 sekund tworzy zagregowane metryki i wypycha je, czyli wykonuje \u201epush\u201d, do centralnego serwera. Je\u015bli akurat nie mo\u017ce si\u0119 po\u0142\u0105czy\u0107, przy nast\u0119pnej pr\u00f3bie dodatkowo sprawdza, czy jego konfiguracja jest aktualna. Serwer odbiera metryki i r\u00f3wnie\u017c zapisuje je u siebie w lokalnej bazie.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Bezpieczenstwo_zero_otwartych_portow_po_stronie_agenta\"><\/span>Bezpiecze\u0144stwo: zero otwartych port\u00f3w po stronie agenta<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">To najmocniejszy argument za takim modelem. Agent mo\u017ce dzia\u0142a\u0107 na maszynie z firewallem ustawionym wed\u0142ug klasycznej, restrykcyjnej polityki: \u201eblokuj przychodz\u0105ce, zezwalaj na wychodz\u0105ce\u201d, czyli deny incoming, allow outgoing. Skoro to agent inicjuje po\u0142\u0105czenie na zewn\u0105trz, nie musi mie\u0107 \u017cadnego otwartego portu nas\u0142uchuj\u0105cego.<\/p>\n<p class=\"isSelectedEnd\">Dlaczego to istotne? Ka\u017cdy otwarty port to potencjalne wej\u015bcie dla atakuj\u0105cego. W \u017cargonie bezpiecze\u0144stwa oznacza to powi\u0119kszanie attack surface, czyli powierzchni ataku \u2014 sumy wszystkich miejsc, przez kt\u00f3re kto\u015b m\u00f3g\u0142by pr\u00f3bowa\u0107 dosta\u0107 si\u0119 do systemu. Agenty monitoruj\u0105ce bywa\u0142y ju\u017c w przesz\u0142o\u015bci przejmowane, a przej\u0119ty agent z otwartym portem to powa\u017cne zagro\u017cenie, bo zwykle znajduje si\u0119 g\u0142\u0119boko w infrastrukturze. Brak nas\u0142uchuj\u0105cego portu po prostu zamyka t\u0119 drog\u0119.<\/p>\n<p class=\"isSelectedEnd\">Drugi aspekt jest czysto operacyjny. Otwarte porty musi mie\u0107 tylko jedna maszyna \u2014 serwer, bo to on zbiera metryki. Zadbanie o bezpiecze\u0144stwo jednej, dobrze pilnowanej maszyny jest niepor\u00f3wnanie prostsze ni\u017c utrzymywanie otwartych port\u00f3w na dziesi\u0105tkach czy setkach rozproszonych host\u00f3w.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Masowa_rekonfiguracja_z_poziomu_serwera\"><\/span>Masowa rekonfiguracja z poziomu serwera<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Skoro agent i tak regularnie odpytuje serwer, mo\u017cna to wykorzysta\u0107 do zdalnej rekonfiguracji. Konfiguracj\u0119 agent\u00f3w zmienia si\u0119 centralnie, po stronie serwera \u2014 r\u00f3wnie\u017c w trybie zbiorczym, czyli bulk reconfigure, naraz dla wielu agent\u00f3w.<\/p>\n<p class=\"isSelectedEnd\">Przyk\u0142ad z webinaru: wyobra\u017a sobie statek z szesnastoma lub wi\u0119ksz\u0105 liczb\u0105 agent\u00f3w na pok\u0142adzie. Chcesz doda\u0107 im wszystkim jedno nowe zadanie. Zamiast logowa\u0107 si\u0119 na ka\u017cd\u0105 maszyn\u0119 z osobna, dodajesz jeden wpis w pliku konfiguracyjnym po stronie serwera.<\/p>\n<p class=\"isSelectedEnd\">Mechanizm dzia\u0142a jako \u201epull\u201d inicjowany przez agenta i jest odporny na b\u0142\u0119dy:<\/p>\n<ol start=\"1\" data-spread=\"true\">\n<li>Edytujesz konfiguracj\u0119 na serwerze, na przyk\u0142ad odkomentowujesz zadania.<\/li>\n<li>Serwer odnotowuje, \u017ce dla danego agenta ma nowsz\u0105 wersj\u0119 konfiguracji ni\u017c ta, kt\u00f3r\u0105 agent posiada.<\/li>\n<li>Agent przy kolejnej aktualizacji zg\u0142asza: \u201emoja konfiguracja jest nieaktualna, przy\u015blij nowsz\u0105\u201d.<\/li>\n<li>Serwer wysy\u0142a now\u0105 wersj\u0119, agent j\u0105 waliduje, podmienia u siebie i natychmiast zaczyna z niej korzysta\u0107.<\/li>\n<\/ol>\n<p class=\"isSelectedEnd\">Na \u017cywej demonstracji ta zmiana propagowa\u0142a si\u0119 w ci\u0105gu kilku sekund \u2014 odkomentowane zadania od razu pojawi\u0142y si\u0119 w surowych metrykach agenta.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Local-first_dane_przede_wszystkim_lokalnie\"><\/span>Local-first: dane przede wszystkim lokalnie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Architektura jest \u201elocal-first\u201d, czyli \u201enajpierw lokalnie\u201d: agent zapisuje wyniki u siebie, zanim cokolwiek wy\u015ble. Konsekwencje s\u0105 trzy:<\/p>\n<ul data-spread=\"false\">\n<li>brak utraty danych, nawet gdy serwer chwilowo nie odpowiada;<\/li>\n<li>ci\u0105g\u0142o\u015b\u0107 monitoringu w trakcie problem\u00f3w sieciowych \u2014 agent nie przestaje pracowa\u0107 tylko dlatego, \u017ce nie ma \u0142\u0105czno\u015bci z central\u0105; po jej przywr\u00f3ceniu do\u015ble zebrane metryki;<\/li>\n<li>mo\u017cliwo\u015b\u0107 lokalnego odpytania danych bezpo\u015brednio na agencie, co bardzo u\u0142atwia debugowanie.<\/li>\n<\/ul>\n<p class=\"isSelectedEnd\">Z tego wynika te\u017c tryb standalone, czyli w pe\u0142ni samodzielny: agent nie musi mie\u0107 centralnego serwera w og\u00f3le. Je\u015bli u\u017cywasz LinkSense jako ma\u0142ego, prywatnego projektu, wystarczy podejrze\u0107 lokaln\u0105 baz\u0119 dowolnym narz\u0119dziem i ewentualnie samodzielnie zbudowa\u0107 alerting na tych danych.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Agregacja_dlaczego_akurat_co_60_sekund_i_zapis_w_paczkach\"><\/span>Agregacja: dlaczego akurat co 60 sekund i zapis w paczkach<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Agent nie wysy\u0142a ka\u017cdego pojedynczego pomiaru z osobna. Najpierw je agreguje, a do serwera trafia zagregowana porcja w interwale jednej minuty. Co wa\u017cne, to \u015bwiadomy kompromis, a nie przypadek.<\/p>\n<p class=\"isSelectedEnd\">Rozwa\u017c scenariusz wysokiej cz\u0119stotliwo\u015bci: pingujesz co\u015b co sekund\u0119 albo co 5 sekund, i to z du\u017cej liczby agent\u00f3w \u2014 powiedzmy setek. Gdyby ka\u017cdy pomiar by\u0142 wysy\u0142any natychmiast, zala\u0142by\u015b sie\u0107 i serwer ruchem. Agregacja co minut\u0119 okaza\u0142a si\u0119 najlepszym kompromisem mi\u0119dzy dok\u0142adno\u015bci\u0105 a ilo\u015bci\u0105 przesy\u0142anych i przechowywanych danych. Sama liczba \u201e60 sekund\u201d nie jest magiczna \u2014 to warto\u015b\u0107 dobrana praktycznie.<\/p>\n<p class=\"isSelectedEnd\">Agregacja nie gubi przy tym istotnych informacji. W zagregowanej porcji znajduj\u0105 si\u0119 mi\u0119dzy innymi: \u015brednie czasy dla wszystkich mierzonych etap\u00f3w, maksymalny czas ca\u0142kowity, liczba pr\u00f3bek, czyli sample count, oraz procent skuteczno\u015bci, czyli success rate. Dzi\u0119ki warto\u015bci maksymalnej wida\u0107 nie tylko, \u017ce \u201e\u015brednio by\u0142o OK\u201d, ale r\u00f3wnie\u017c najgorszy przypadek w danym oknie.<\/p>\n<p class=\"isSelectedEnd\">Dodatkowo zapis do bazy odbywa si\u0119 w paczkach, czyli batch, a nie po jednym rekordzie. To kolejny zabieg pod wydajno\u015b\u0107: mniej operacji zapisu oznacza mniejszy narzut.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Model_danych_co_dokladnie_trafia_do_bazy\"><\/span>Model danych: co dok\u0142adnie trafia do bazy<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Dla os\u00f3b, kt\u00f3re b\u0119d\u0105 potem konsumowa\u0107 te dane, najciekawsze jest to, jak bogaty jest pojedynczy pomiar. Zadanie HTTP nie zwraca samego \u201edzia\u0142a\/nie dzia\u0142a\u201d. Rozbija odpowied\u017a na konkretne sk\u0142adniki:<\/p>\n<ul data-spread=\"false\">\n<li>czasy TCP, czyli nawi\u0105zanie po\u0142\u0105czenia;<\/li>\n<li>czasy TLS, czyli zestawienie szyfrowania;<\/li>\n<li>time-to-first-byte, czyli czas do pierwszego bajtu odpowiedzi;<\/li>\n<li>ca\u0142kowity czas pobrania;<\/li>\n<li>informacj\u0119, czy po\u0142\u0105czenie by\u0142o szyfrowane, czyli TLS, czy certyfikat jest wa\u017cny i ile dni zosta\u0142o do jego wyga\u015bni\u0119cia;<\/li>\n<li>kody statusu HTTP, czyli informacj\u0119, czy by\u0142 sukces, czy b\u0142\u0105d.<\/li>\n<\/ul>\n<p class=\"isSelectedEnd\">Do ka\u017cdego zadania dochodzi te\u017c adres IP urz\u0105dzenia, z kt\u00f3rym faktycznie nawi\u0105zano po\u0142\u0105czenie. Dzi\u0119ki temu wida\u0107 na przyk\u0142ad, jak zmienia si\u0119 rozwi\u0105zany adres IP w czasie.<\/p>\n<p class=\"isSelectedEnd\">Jest r\u00f3wnie\u017c opcjonalne, bardzo praktyczne pole meta: <code dir=\"ltr\">target_id<\/code>. Pozwala oznaczy\u0107 tym samym identyfikatorem r\u00f3\u017cne zadania dotycz\u0105ce tego samego celu, na przyk\u0142ad ping, HTTP GET i TCP wskazuj\u0105ce na t\u0119 sam\u0105 aplikacj\u0119, aby p\u00f3\u017aniej \u0142atwo je grupowa\u0107 i filtrowa\u0107. To pole istnieje wy\u0142\u0105cznie po to, by pom\u00f3c uporz\u0105dkowa\u0107 w\u0142asny monitoring.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Dlaczego_Rust\"><\/span>Dlaczego Rust<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Wydajno\u015b\u0107, kt\u00f3r\u0105 wida\u0107 w liczbach \u2014 agent na poziomie oko\u0142o 31 MB pami\u0119ci i 0\u20131% CPU przy kilkudziesi\u0119ciu zadaniach \u2014 nie wzi\u0119\u0142a si\u0119 znik\u0105d. Zesp\u00f3\u0142 w\u0142o\u017cy\u0142 du\u017co pracy w to, aby program dzia\u0142a\u0142 mo\u017cliwie oszcz\u0119dnie, i to przes\u0105dzi\u0142o o wyborze Rusta jako j\u0119zyka.<\/p>\n<p class=\"isSelectedEnd\">Dwie cechy s\u0105 tutaj kluczowe. Po pierwsze, asynchroniczne wykonywanie, czyli model async\/await: zamiast blokowa\u0107 si\u0119 na ka\u017cdym pojedynczym te\u015bcie i czeka\u0107 na jego wynik, agent prowadzi wiele zada\u0144 wsp\u00f3\u0142bie\u017cnie, nie marnuj\u0105c czasu procesora na bezczynne oczekiwanie. Po drugie, wydajno\u015b\u0107 i niskie zu\u017cycie pami\u0119ci \u2014 Rust nie ma garbage collectora i pozwala precyzyjnie panowa\u0107 nad zasobami.<\/p>\n<p class=\"isSelectedEnd\">Mia\u0142o to r\u00f3wnie\u017c potwierdzenie w testach: oprogramowanie uruchomiono w d\u0142ugoterminowym przebiegu trwaj\u0105cym ponad miesi\u0105c, pod sporym obci\u0105\u017ceniem, w ramach stress test\u00f3w na w\u0142asnej infrastrukturze. Celem by\u0142o wychwycenie ewentualnych wyciek\u00f3w pami\u0119ci lub degradacji dzia\u0142ania. Testy zako\u0144czy\u0142y si\u0119 bez ani jednego problemu.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Dlaczego_SQLite\"><\/span>Dlaczego SQLite<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Dane trafiaj\u0105 do SQLite \u2014 zar\u00f3wno po stronie agenta, jak i serwera. SQLite to lekka, plikowa baza danych: nie wymaga osobnego procesu ani serwera bazodanowego, a ca\u0142o\u015b\u0107 mie\u015bci si\u0119 w jednym pliku.<\/p>\n<p class=\"isSelectedEnd\">Wyb\u00f3r SQLite \u2014 i w\u0142a\u015bnie takich, bardzo prostych struktur tabel \u2014 by\u0142 celowy. Chodzi\u0142o o to, aby:<\/p>\n<ul data-spread=\"false\">\n<li>integracja z czymkolwiek by\u0142a trywialna: \u017ceby wyci\u0105gn\u0105\u0107 dane \u201egdzie indziej\u201d, wystarczy odczyta\u0107 je z SQLite i skopiowa\u0107 do docelowego systemu;<\/li>\n<li>mo\u017cliwy by\u0142 tryb local-only: mo\u017cesz podejrze\u0107 baz\u0119 dowoln\u0105 przegl\u0105dark\u0105 SQLite albo innym narz\u0119dziem do wizualizacji czy alertowania, bez stawiania dodatkowej infrastruktury.<\/li>\n<\/ul>\n<p class=\"isSelectedEnd\">Prostota schematu nie jest tutaj kosmetyk\u0105. To w\u0142a\u015bnie ona sprawia, \u017ce LinkSense \u0142atwo \u201edoklei\u0107\u201d do istniej\u0105cego stacku.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Konfiguracja_jako_kod_TOML\"><\/span>Konfiguracja jako kod: TOML<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Ca\u0142a konfiguracja to pliki tekstowe w formacie TOML \u2014 czytelnym formacie konfiguracyjnym opartym na parach klucz\u2013warto\u015b\u0107. Rozdzielono j\u0105 na warstwy:<\/p>\n<ul data-spread=\"false\">\n<li>konfiguracja agenta \u2014 mi\u0119dzy innymi adres centralnego serwera i klucz, czyli app key, potrzebny do uwierzytelnienia; w trybie local-only te pola s\u0105 zb\u0119dne;<\/li>\n<li>konfiguracja zada\u0144 \u2014 lista tego, co agent ma mierzy\u0107; dodanie nowego zadania to skopiowanie wpisu i podmiana kilku warto\u015bci, a ka\u017cdy typ zadania ma w\u0142asne, kr\u00f3tkie README;<\/li>\n<li>konfiguracja serwera \u2014 jeszcze prostsza: adres nas\u0142uchu, w\u0142asny klucz, miejsce przechowywania konfiguracji agent\u00f3w oraz rozmiar testu przepustowo\u015bci.<\/li>\n<\/ul>\n<p class=\"isSelectedEnd\">Skoro wszystko jest tekstem, ca\u0142o\u015b\u0107 naturalnie poddaje si\u0119 wersjonowaniu, na przyk\u0142ad w gicie. Masz histori\u0119 zmian i mo\u017cliwo\u015b\u0107 przegl\u0105du \u2014 czyli configuration as code.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Wdrozenie_binarki_CI_i_pulapka_pinga_na_Linuksie\"><\/span>Wdro\u017cenie: binarki, CI i pu\u0142apka pinga na Linuksie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Mo\u017cesz zbudowa\u0107 LinkSense ze \u017ar\u00f3de\u0142, ale nie musisz. Projekt ma GitHub Actions, czyli mechanizm CI \u2014 automatycznego budowania i wydawania z poziomu repozytorium \u2014 kt\u00f3re przygotowuj\u0105 gotowe wydania na wiele architektur. Pobierasz binark\u0119 odpowiedni\u0105 dla swojej platformy i uruchamiasz.<\/p>\n<p class=\"isSelectedEnd\">Jest jedna pu\u0142apka warta zapami\u0119tania: uprawnienia pinga na Linuksie. Je\u015bli chcesz wykonywa\u0107 wiele ping\u00f3w r\u00f3wnolegle, musisz doda\u0107 u\u017cytkownika lub proces do grupy ping. Pow\u00f3d jest techniczny: ping na Linuksie wymaga uprzywilejowanego dost\u0119pu, czyli tzw. raw socket \u2014 niskopoziomowego gniazda do wysy\u0142ania pakiet\u00f3w ICMP \u2014 a spos\u00f3b, w jaki Linux to ogranicza, zmienia\u0142 si\u0119 przez lata.<\/p>\n<p class=\"isSelectedEnd\">Tw\u00f3rcy mieli wyb\u00f3r: albo u\u017cy\u0107 systemowego polecenia ping jako zwyk\u0142ej nak\u0142adki \u2014 pro\u015bciej, ale ze strat\u0105 asynchroniczno\u015bci i wydajno\u015bci \u2014 albo zrealizowa\u0107 ping samodzielnie, asynchronicznie i wydajnie, co wymaga wspomnianego uprawnienia. \u015awiadomie wybrali to drugie, w imi\u0119 wydajno\u015bci.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Logi_dlaczego_nie_trafiaja_na_standardowe_wyjscie\"><\/span>Logi: dlaczego nie trafiaj\u0105 na standardowe wyj\u015bcie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Drobny, ale przemy\u015blany szczeg\u00f3\u0142: agent z za\u0142o\u017cenia nie wypisuje log\u00f3w na standardowe wyj\u015bcie, czyli stdout. To efekt tego, \u017ce narz\u0119dzie ma dzia\u0142a\u0107 \u201ebezobs\u0142ugowo\u201d, r\u00f3wnie\u017c w odleg\u0142ych lokalizacjach. Logi s\u0105 dost\u0119pne w plikach log\u00f3w \u2014 i w\u0142a\u015bnie tam nale\u017cy ich szuka\u0107, je\u015bli chcesz co\u015b prze\u015bledzi\u0107.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Swiadome_%E2%80%9Enie%E2%80%9D_kontenery\"><\/span>\u015awiadome \u201enie\u201d: kontenery<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Na pytanie o gotow\u0105 wersj\u0119 skonteneryzowan\u0105 odpowied\u017a brzmi: \u201ei tak, i nie\u201d. Pomys\u0142 jest rozwa\u017cany, ale na razie zesp\u00f3\u0142 nie widzi w nim du\u017cej warto\u015bci \u2014 i to tak\u017ce jest decyzja architektoniczna.<\/p>\n<p class=\"isSelectedEnd\">Logika jest sp\u00f3jna z reszt\u0105 projektu: LinkSense to jedna prosta binarka, a wdro\u017cenie sprowadza si\u0119 do schematu \u201eskopiuj binark\u0119 i uruchom us\u0142ug\u0119\u201d. Skonteneryzowanie tego samodzielnie jest trywialne \u2014 wystarczy w\u0142asny Dockerfile lub docker compose, kt\u00f3ry kopiuje binark\u0119 i uruchamia us\u0142ug\u0119.<\/p>\n<p class=\"isSelectedEnd\">Co wi\u0119cej, w kontenerze zaczynasz traci\u0107 argument o minimalnym zu\u017cyciu zasob\u00f3w, kt\u00f3ry jest jedn\u0105 z g\u0142\u00f3wnych zalet narz\u0119dzia. Da si\u0119 to ograniczy\u0107, u\u017cywaj\u0105c lekkiego obrazu \u2014 Alpine Linux i build pod musl, czyli alternatywn\u0105, lekk\u0105 bibliotek\u0119 standardow\u0105 C \u2014 cho\u0107 tej \u015bcie\u017cki autorzy sami nie testowali, wi\u0119c traktuj\u0105 j\u0105 jako \u201epowinno dzia\u0142a\u0107\u201d.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Podsumowanie\"><\/span>Podsumowanie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"isSelectedEnd\">Architektura LinkSense to seria sp\u00f3jnych, \u015bwiadomych decyzji: agent \u0142\u0105czy si\u0119 z serwerem, bo to bezpieczniejsze; dane s\u0105 przechowywane lokalnie, bo nie chcemy ich traci\u0107; agregacja jest minutowa, bo to rozs\u0105dny kompromis; baza to SQLite, bo narz\u0119dzie ma si\u0119 \u0142atwo integrowa\u0107; j\u0119zyk to Rust, bo ma by\u0107 szybko i oszcz\u0119dnie.<\/p>\n<p class=\"isSelectedEnd\">Nic tutaj nie jest przypadkowe \u2014 i dok\u0142adnie dlatego ca\u0142o\u015b\u0107 pozostaje tak lekka.<\/p>\n<p>Chcesz dowiedzie\u0107 si\u0119 wi\u0119cej o LinkSense bezpo\u015brednio od jego tw\u00f3rc\u00f3w? <strong><a href=\"https:\/\/youtu.be\/brb1i0JTlWo\" target=\"_blank\" rel=\"noopener\">Obejrzyj dedykowany webinar<\/a><\/strong> z Maciejem Wilamowskim i Marcinem Ka\u017amierczakiem, w kt\u00f3rym opowiadaj\u0105 o decyzjach architektonicznych stoj\u0105cych za projektem, pokazuj\u0105, jak dzia\u0142a LinkSense w praktyce, i wyja\u015bniaj\u0105, w jakich scenariuszach mo\u017ce realnie pom\u00f3c w monitorowaniu rozproszonych \u015brodowisk.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>W pierwszym tek\u015bcie pokazali\u015bmy, czym jest LinkSense \u2014 lekkie, open source narz\u0119dzie do synthetic monitoringu. Tutaj schodzimy o poziom ni\u017cej, do decyzji architektonicznych. Bo akurat w tym projekcie niemal ka\u017cdy wyb\u00f3r \u2014 od modelu komunikacji, przez j\u0119zyk programowania, po format bazy danych \u2014 wynika z konkretnego problemu, a nie z mody. Ten tekst jest dla [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1396,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[99],"tags":[242,243],"product":[245],"topic":[168],"sycope_author":[249],"class_list":["post-4149","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-linksense-pl","tag-monitoring-sieci","product-linksense-pl","topic-monitorowanie-i-analiza-sieci","sycope_author-marcin-kazmierczak"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/posts\/4149","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/comments?post=4149"}],"version-history":[{"count":3,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/posts\/4149\/revisions"}],"predecessor-version":[{"id":4290,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/posts\/4149\/revisions\/4290"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/media\/1396"}],"wp:attachment":[{"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/media?parent=4149"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/categories?post=4149"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/tags?post=4149"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/product?post=4149"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/topic?post=4149"},{"taxonomy":"sycope_author","embeddable":true,"href":"https:\/\/www.sycope.com\/pl\/wp-json\/wp\/v2\/sycope_author?post=4149"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}