Nie robiłem badań, nie mam żadnych wyników ankiet, ale wydajemisię, że monitoring i obserwowalność nie stoją na zbyt wysokiej pozycji priorytetowych zadań wielu entuzjastów selfhostingu. I nie ma w tym nic dziwnego, bo selfhosting (czy tam “homelabbing”) to dla wielu głównie zabawa w szukaniu coraz to ciekawszych aplikacji, które mogą zastąpić komercyjne odpowiedniki czy poprawić codzienne życie. Monitoring, sposoby deploymentów, zarządzanie konfiguracją może więc wydawać się tym “the boring work”. Ale według mnie wcale tak nie musi być i monitoring oraz obserwowalność stały się dla mnie głównymi “zabawkami” selfhostingu na niekrótki czas.

Na początku dodam, że choć niezbyt lubię to słowo, tak w tym wpisie będę stosował słowo “homelab” w celu nazwania domowego serwera opartego o sprzęt konsumpcyjny.

Początki monitorowania mojego homelaba

Kiedy to zacząłem bawić się w selfhosting, to nie przykładałem żadnej uwagi monitorowaniu. Nic dziwnego, początki to właśnie faza na szukanie coraz to ciekawszych aplikacji, wszak otworzył się przede mną kompletnie nowy katalog oprogramowania, którego nigdy wcześniej nie eksplorowałem. Początki monitoringu więc sięgają dopiero instalacji Home Assistanta, na którego skusiłem się po około 6 miesiącach zabawy. W Home Assistant monitorowałem zużycie energii przez serwer, a także dodałem (co jest w Home Assistant bardzo proste) zbieranie takich metryk jak użycie CPU, RAM, zajętość dysków itd. Nie miałem żadnych alertów, więc zdany byłem tylko i wyłącznie na to, że jak zauważyłem, że coś działa nie tak, to wchodziłem i sprawdzałem metryki.

Uptime Kuma

Pierwszym takim oprogramowaniem, które faktycznie pełniło rolę źródła informowania o problemach była aplikacja Uptime Kuma, która jest w zasadzie silnikiem dla healtchecków, tudzież sprawdza czy strona pod danym adresem “żyje”/odpowiada kodem 200. Jeśli nie, to wysyłany jest alert, w moim przypadku na Telegrama. Po drodze uznałem, że posiadanie tego programu na tym samym serwerze, który monitoruję jest bez sensu, więc przeniosłem go na zewnątrz, na “VPS-a” od dostawcy Mikrus. Później dodałem kolejną instancję Uptime Kuma, która monitorowała ten serwer VPS i tym samym obydwie Kumy sprawdzały siebie. Z takiego rozwiązania korzystałem do niedawna.

W tak zwanym międzyczasie zacząłem swoją pracę zawodową na pozycji SysOps. Dla osoby zajmującej to stanowisko monitoring i support to główne “dania” i zajawki. Szybko poznałem kilka rozwiązań do monitorowania, w tym właśnie Zabbix czy Amazon CloudWatch. To mnie też skłoniło do uznania, że monitoring moich usług jest po prostu słaby, nieistniejący, choć - muszę przyznać - Uptime Kuma to dobry początek. Dlatego też po jakimś czasie znalazłem bardzo świeże na tamten moment rozwiązanie o nazwie Beszel.

Beszel

Beszel pozostaje oprogramowaniem, które do dzisiaj jestem w stanie polecić. Jest ono stale ulepszane i dodawane są kolejne ważne funkcje dlatego warto je mieć na uwadze. A co robi Beszel? Zapewnia funkcje monitorowania podstawowych pozycji (pozycje to oficjalne tłumaczenie słowa items w Zabbix) takich jak użycie CPU, RAM, zajętość dysków, ale też monitoring użycia zasobów przez kontenery Docker. Jako administrator można ustawić progi alertowania. No właśnie, alertowania, bo można też wysyłać alarmy z użyciem wybranego sposobu, ja zdecydowałem się na bota Telegram.

Beszel nieco przypomina Zabbixa, bowiem mamy tutaj oprogramowanie serwer+frontend (jako monolit), które deployujemy gdzieś i mamy także agentów, które instalujemy na sprzętach, które mają być monitorowane. Instalacja agenta może odbyć się zarówno z użyciem Dockera, jak i natywnej instalacji (skryptem). Z Beszela korzystałem całkiem długo i w zasadzie potrafi on być wystarczający dla wielu entuzjastów selfhostingu, a nawet myślę, że sprawdziłby się w mniejszych firmach. Zarówno agent, jak i oprogramowanie serwer+frontend jest niezwykle lekkie i szybkie - ja to drugie utrzymywałem z powodzeniem na Mikrus Frog. Dlatego do dzisiaj Beszela potrafię polecać.

Ale zaczęło mi brakować większej liczby funkcji. Zwłaszcza monitorowania innych sprzętów, chociażby routera, ale też jakichś aplikacji, np. RabbitMQ. Beszel Agent nie działał też na każdym sprzęcie, na przykład nie mogłem z powodzeniem uruchomić go na Orange Pi Zero 2. Co więcej, chciałem rozpocząć monitoring SMART dysków, które mam w RAID1. Myślałem o jakichś skryptach Bash i powiadomieniach, ale uznałem, że to bez sensu – wszystko powinno być scentralizowane. Czarę goryczy przelała sama Uptime Kuma, która zaczęła generować fałszywe alarmy, a także miewała problemy, które były oznaczone jak wontfix na Githubie. I nie, przesiadka na Uptime Kuma v2 nic nie dała. Finalnie też chciałem zdobywać ekspercką wiedzę w oprogramowaniu poziomu enterprise, a Zabbix bardzo mi się spodobał, bo zacząłem poświęcać mu całkiem dużo czasu w pracy. Co więcej, rozpocząłem proces certyfikacji i na moment pisania tego artykułu mam certyfikat Zabbix Certified User 7.0 oraz nabyłem książkę polskiego autora Mateusza Dampca pod tytułem “Zabbix 7.0. Efektywny monitoring infrastruktury IT dla każdego”, która bardzo mi się spodobała, ale potrzebowałem miejsca, gdzie będę mógł testować nowe rozwiązania.

Początki Zabbixa w moim domu

Po tym, jak ułożyłem sobie potrzeby wobec monitoringu, nieco lepiej poznałem Zabbixa, a także zapoznałem się z częścią książki “Zabbix 7.0. Efektywny monitoring infrastruktury IT dla każdego” uznałem, że Zabbix faktycznie będzie najlepszym wyjściem. Postanowiłem jednak zrobić sobie pewnego rodzaju dry-run i zdeployowałem Zabbixa u siebie w domu, na komputerze Mini PC z Proxmoxem. Wykorzystałem do tego OpenTofu oraz przygotowałem sobie skrypt Cloud Init. Do tego uznałem, że najlepszym wyborem jest architektura z dwoma maszynami wirtualnymi, gdzie jedna hostuje zarówno Zabbix-Frontend (czyli moduł odpowiedzialny za GUI) oraz Zabbix-Server (czyli serce tego systemu). Natomiast na drugiej maszynie wirtualnej zainstalowałem MariaDB jako bazę danych dla Zabbixa. Dodam, że obydwie aplikacje Zabbixa zainstalowałem z użyciem Dockera, natomiast bazę danych natywnie, za pomocą wbudowanego w Debiana menedżera pakietów.

Użycie RAM przez router z OpenWrt

Na różnych maszynach doinstalowałem agentów, skonfigurowałem media do wysyłania alarmów i… zaczęło mi się to podobać – tym bardziej, że do świata monitoringu dołączył kolejny mój sprzęt, czyli router ASUS AX-53U z OpenWrt – pakiet zabbix-agent tak sobie czekał w repozytorium na zainstalowanie i skonfigurowanie. Do tego zacząłem szukać różnych templatek i innych rzeczy do monitorowania. Tak zacząłem dodawać monitoring kontenerów Docker dzięki zabbix-agent2 i wtyczce Docker, monitoring Home Assistanta z użyciem niestandardowej templatki czy brokera MQTT RabbitMQ. Wiem też, że mam sporo innych rzeczy, które mogę łatwo wchłonąć do monitoringu, ale nie miałem za bardzo czasu, aby do tego przysiąść. Nie miałem czasu, bo zacząłem myśleć nad produkcyjną wersją tego monitoringu.

Przygotowania do wypuszczenia Zabbixa w świat

Dalej wiedziałem, że monitoring powinien żyć poza moim domem, a więc musiał być zdeployowany w jakiejś chmurze, ale nie mógł być to Mikrus, ponieważ nie sprawdziłby się – w dłuższej perspektywie jest trudny w utrzymaniu i nie oferuje personalnego adresu IP. Mikrus ma sporo ciekawych funkcji potrzebnych i ułatwiających życie z perspektywy osoby wchodzącej w ten świat, ale ja już potrzebowałem czegoś innego. Szybko zdecydowałem więc, że najlepszym wyjściem będzie deployment w chmurze Hetzner. Nie będę kłamał, wielkim fanem tej usługi nie jestem, ale akurat w tym przypadku spełniała większość wymogów, przede wszystkim instancje EC2 można deployować za pomocą Terraforma oraz płaci się w formie Pay as you Go, a nie w abonamencie. Ponadto, nawet najniższy typ instancji EC2 jest naprawdę wydajny i spełniający wymogi, a cena za nią jest wręcz bajeczna. CX22, bo taki typ wybrałem (aktualnie zastąpiony przez jeszcze tańszy typ CX23) kosztuje lekko ponad 4 Euro z polskim VAT-em i za taki hajs oferuje 4 GB RAM i 2 rdzenie vCPU.

Koszt dwóch instancji i jednego publicznego adresu IP nie byłby wysoki, jednakże chciałem ten koszt obniżyć, a także zachować podejście projektowe, gdzie kilka osób pracuje nad utrzymaniem i rozwojem infrastruktury. Dlatego też do projektu zaprosiłem dwóch kolegów, którzy również posiadają domowe serwery. Długo namawiać ich nie musiałem, ponieważ obydwaj korzystali z Beszel i szybko zrozumieli zalety Zabbixa. To podzieliło koszty na trzech. Finalnie jednak, na moment pisania tego artykułu, w skład projektu wchodzi już 5 osób.

Deployment w chmurze Hetzner

Przejdźmy więc do głównego dania. Hetzner nie daje ogromnych możliwości w projektowaniu rozwiązań chmurowych – liczba usług jest mała, a opcje modyfikacji tych istniejących są po prostu niewielkie. Przykładowo, byłem gotowy zacząć płacić nieco więcej zyskując zmniejszenie swojej odpowiedzialności i właśnie w tym mogłaby mi pomóc instancja silnika bazodanowego, coś w stylu RDS. Niestety, Hetzner czegoś takiego nie oferuje. Również myślałem nad skorzystaniem z Load Balancera, ale pricing za takowy w Hetzner nie jest dla mnie korzystny. Co więcej, chciałem też skorzystać z bucketów S3 (chociażby do trzymania pliku stanu Terraforma), ale, ponownie, pricing za buckety w Hetzner jest niekorzystny. Finalnie potrzebowałem też menedżera sekretów, którego w Hetzner po prostu nie ma.

Ale to też nie oznacza, że nie da się czegoś wymyślić. Dlatego też przyjąłem kilka zasad:

  1. Architektura będzie powielona z dry-run, tudzież znowu postawię dwie instancje EC2, gdzie jedna będzie serwować frontend oraz serwer Zabbixa, a druga bazę danych (MariaDB).
  2. Tylko instancja z serwerem będzie dostępna bezpośrednio dla użytkowników.
  3. Skorzystam z usługi Networks w Hetzner, aby umieścić obydwie instancje w sieci prywatnej.
  4. Skorzystam z firewalla dostępnego w Hetzner.
  5. Kod IaC będzie zawierał jak najwięcej możliwie ustawień, ale bez zapisanych sekretów czystym tekstem.
  6. Skorzystam z funkcji backupów w Hetzner.

Musiałem więc przeskoczyć niektóre z braków Hetznera. Brak usługi RDS zastąpiłem po prostu drugą instancją EC2. Brak menedżera sekretów nadrobiłem usługą Secret Manager w BitWarden, natomiast bucket S3 zapewniła mi chmura Scaleway, z którą już miałem do czynienia. Firewall pozostał w Hetzner, ale zarządzanie domeną (tak jak planowałem od samego początku) przeniosłem do Cloudflare. Zapytacie więc jak zarządzać tym chaosem korzystania z wielu usług? Normalnie byłby to koszmar, ale mając jeden interfejs do tego wszystkiego nie jest tak ciężko. A właśnie takim interfejsem jest Terraform, choć w rzeczywistości to jego fork o lepszej licencji, czyli OpenTofu. Mając OpenTofu tak naprawdę nie ma dla mnie znaczenia gdzie spoczywają sekrety, gdzie zapisany jest plik stanu Terraforma czy inne elementy, bo w kodzie to niemalże wszystko to samo. Niejako widać to na listingu poniżej.

module "zabbix_database" {
  source = "../../Modules/hetzner_server_instance" #moduły są wersjonowane tagami w git, ta linia jest zamiennikiem na potrzeby artykułu

  name     = local.instance.database_server.name
  location = local.hetzner.location
  type     = local.instance.type
  image    = local.instance.image
  backups = true

  networking = {
    ipv4_enabled = false
    ipv6_enabled = true
  }
  private_network = {
    network_id = module.arvalis_network.id
    alias_ips  = []
  }

  cloud_init = {
    config = join("\n", [
      templatefile("${path.module}/files/cloud-init/users-init.yaml.tpl", {
        users = local.users_list
      }),
      templatefile("${path.module}/files/cloud-init/database-instance/cloud-init.yaml.tpl", {
        users                  = local.users_list
        hostname               = local.instance.database_server.name
        zabbixdb_user_password = sensitive(data.bitwarden_secret.zabbixdb_user_password.value)
      })
    ])
    gzip          = true
    base64_encode = true
  }

  labels = merge(
    local.common_tags,
    local.instance.database_server.tags,
    {
      name = local.instance.database_server.name
    }
  )
}

OpenTofu daje tak bardzo koherentne doświadczenie, że wiele elementów deploymentu w Proxmox mogłem po prostu skopiować do kodu infrastruktury Hetznera. Ważnym też dla mnie elementem była konfiguracja użytkowników. Ta zapisana jest w pliku user-data.yaml.tpl i to właśnie tam zdefiniowani są wszyscy użytkownicy wraz z publicznymi kluczami SSH. Plik ten jest częścią standardu Cloud init (często w chmurach chowającego się pod postacią skryptu User Data) i deployowany jest na obie instancje. Wspomniany Cloud Init używam również do wstępnej konfiguracji maszyn, np. instalacji Dockera czy instalacji MariaDB.

Wspomniane kontenery Docker również deployuję z poziomu OpenTofu, co umożliwia mój autorski moduł, który ma niejako udawać Docker Compose. Definicja wygląda tak:

module "zabbix_frontend_container" {
  source       = "git::ssh://[email protected]/cichy1173/cichyform.git//Docker/docker_service?ref=docker_service_v1.0.0"
  service_name = "zabbix-web-nginx-mysql"
  image_name   = "zabbix/zabbix-web-nginx-mysql:alpine-7.0.21"

  volumes = []

  ports = [
    {
      internal = 8080
      external = 80
      protocol = "tcp"
    },
  ]

  environment_variables = [
    "DB_SERVER_HOST=${module.zabbix_database.ipv4_private_address}",
    "MYSQL_DATABASE=zabbix",
    "MYSQL_USER=zabbix",
    "ZBX_SERVER_HOST=${module.zabbix_server_frontend.ipv4_private_address}",
    "ZBX_SERVER_PORT=10051",
    "ZBX_SERVER_NAME=Arvalis Monitoring"
  ]
  depends_on = [module.zabbix_server_frontend, module.zabbix_database]
}

Praca z Zabbix zainstalowanym w Hetzner oraz koszty

Od samego początku infrastruktura zakładała skorzystanie z Zabbix Proxy w trybie Active. Dlaczego? Nie chciałem, aby każdy z nas w jakiś sposób otwierał port 10051 u siebie, tym bardziej, że niektórzy są za podwójnym NAT-em. Ponadto korzystanie z Zabbix Proxy Active jest mniej zasobożerne dla instancji Zabbixa.

A czym jest Zabbix Proxy? W skrócie, Proxy to taka usługa, która może zbierać metryki z różnych instancji w danej sieci, a następnie przesłać je do faktycznego Zabbix Serwera. No to czym więc jest tryb Active? Zabbix Proxy w trybie Active sam inicjuje połączenie do serwera, wysyła metryki do Zabbix-Server na port 10051, a ten w odpowiedzi przesyła konfigurację. W trybie Passive połączenie odbywa się w odwrotną stronę – Server inicjuje połączenie na port 10051 Proxy, a to w odpowiedzi zwraca metryki. W dosyć analogiczny sposób działa Zabbix Agent Passive/Active, ale agenci w tej historii nie są ważni.

Schemat działania Zabbix Proxy

No więc plan zakładał, że każdy sobie deployuje Zabbix Proxy w swojej sieci, gdziekolwiek tam sobie chce, jakkolwiek sobie chce, a następnie w trybie Active przesyła dane do Zabbix Server. Ponadto, dołożyliśmy szyfrowanie za pomocą certyfikatów TLS. Zadziałało to bardzo fajnie. Zabbix Server też prowadzi działania odpytujące, aczkolwiek bardziej w celu zastąpienia Uptime Kuma, bowiem Zabbix może robić dokładnie to samo dzięki Web Scenarios. Jednak aby sobie ułatwić proces dodawania i zarządzania monitoringiem Web, utworzyliśmy skrypt Python, który, wykorzystując bibliotekę Zabbix-Python-Utils, wrzuca nam monitoring stron internetowych zarówno z poziomu internetu, jak i w sieci lokalnej (dzięki monitoringu za pomocą Zabbix Proxy).

Tak jak wspomniałem, do projektu dołączyły kolejne osoby, co nie zamęczyło Zabbix Server. Użycie RAM wynosi około 50%, a CPU to użycie około 10-15%. Naprawdę niewiele i mamy spory margines mocy, aby wrzucać kolejne hosty do monitorowania, a nawet całe lokalizacje wraz z Zabbix Proxy Groups. Tak, z Proxy Groups też korzystamy i to naprawdę fajna funkcja.

Użycie CPU przez Zabbix

Użycie RAM przez Zabbix

A jak kilku użytkowników może korzystać z Zabbix? Wykorzystujemy funkcję Host Groups, gdzie każdy z nas ma swoją dedykowaną, imienną grupę hostów. Natomiast hosty do monitorowania są rozdzielone między użytkownikami właśnie z użyciem tych grup. Każdy użytkownik ma dostęp tylko do swojej grupy hostów i tym samym widzi metryki i alarmy związane tylko ze swoją grupą. Rozdzielenie logiczne jest nieco większe, ale sercem tego podziału są właśnie te grupy.

Co z kosztami? No jest lepiej niż myślałem. Musimy płacić za dwie instancje EC2, adres publiczny IPv4, bucket S3 (to są grosze) oraz za domenę (koszt jednorazowy, raz na rok). Finalnie miesięcznie koszt to 10-13 złotych za osobę. Jak za tak potężny monitoring jest to wręcz niewiele.

Podsumowanie

To nie koniec rozwoju projektu! Nadal musimy opracować wiele rozwiązań, które pomogą nam zarządzać usługą. Pierwszą rzeczą w tej kolejce jest wprowadzenie automatyzacji do obsługi zmiennego adresu IP. Chcę również dodać wykrywanie dryftów w kodzie infry (Forgejo Actions jest już gotowe, należy tylko je wdrożyć w repo), czy lepiej zautomatyzować patchowanie. Ale już na tym etapie uważam, że projekt działa fajnie. W żaden sposób nie jest skomplikowany, w zasadzie to niezwykle prosta architektura, ale z jakiegoś powodu nawet może być zadowalająca.