Wstęp
Jest taki żart brzmiący “It’s always DNS”, który wyjaśnia, że w razie jakichś problemów z dostępem do internetu, to zazwyczaj winnym jest DNS. I tak też bywa u mnie, ponieważ sam korzystam z alternatywnego DNS, aby móc blokować reklamy i inne nieprzyjemne połączenia. Wykorzystuję do tego oprogramowanie AdGuard Home, które spełnia podobną rolę jak Pi-Hole, ale AdGuard Home ma przewagę w postaci świetnie działającego providera do Terraform/OpenTofu. Dlatego też postanowiłem zmigrować konfigurację AdGuarda właśnie do OpenTofu. Wiedziałem jednak, że tylko jeden node z AdGuardem to nie jest najlepsze wyjście, a niedługo później miałem się o tym przekonać.
W tym artykule słowa “OpenTofu” i “Terraform” będę wykorzystywać naprzemiennie.
Problem jednego node’a DNS
Kiedy to poprawiałem zasilanie swojej “szafy”, to odpiąłem z prądu komputer, na którym to AdGuard Home był zainstalowany. Po chwili mój telefon przestał odtwarzać film na YouTube, który uruchomiłem sobie w tle, a Telegram przestał otrzymywać nowe wiadomości. Szybko zrozumiałem co się stało i postanowiłem rozwiązać przyszłe występowanie tego problemu tak, abym mógł bezproblemowo odpinać node’y z AdGuardem z zachowaniem pełnej funkcjonalności sieci.
Dlatego też zdecydowałem, że dołożę kolejne dwa urządzenia z instalacjami AdGuarda Home – jeden w sieci lokalnej, a drugi poza nią, w momencie pisania artykułu na serwerze Mikrus. Wszystkie te node’y (w sumie trzy) spiąłem za pomocą sieci Tailscale. Pozostał jednak problem zarządzania konfiguracją. Nawet jeśli teraz zaprę się i ręcznie poustawiam tak samo wszystkie te instalacje, to i tak w przyszłości każda jedna zmiana będzie bolączką. Całe szczęście, miałem już konfigurację zapisaną w kodzie Terraform/OpenTofu, a więc doszło mi tylko rozdystrybuowanie jej na więcej urządzeń.
Postanowiłem więc pozostać w świecie Terraforma. Wyzwaniem jednak był fakt, że dokładnie ten sam kod w tym samym repozytorium miał zacząć obsługiwać kilka równorzędnych sobie node’ów z oprogramowaniem AdGuard Home. To problem logiczny, którego do tej pory jeszcze nie spotkałem.
Dlaczego Terraform?
Idea IaC (Infrastructure as Code) jest mi bardzo bliska, głównie ze względu na pracę zawodową. Ideę tę postanowiłem też pielęgnować i rozwijać w ramach homelabu, jednakże w środowisku “domowym” zasobnik wykorzystywanych narzędzi jest znacznie inny. To jednak mnie nie zraziło i zacząłem rozwijać swój kod OpenTofu, najpierw dla maszyn wirtualnych w Proxmox (specjalnie dla OpenTofu zacząłem korzystać z Proxmoxa), a później właśnie dla AdGuard Home.
A dlaczego w ogóle wybrałem Terraforma/OpenTofu? Przyznam, że pisanie kodu właśnie w tym narzędziu/środowisku zaczęło mi dawać dużo zabawy i chęci eksploracji. W pracy miałem dużo okazji, aby zapoznać się z Terraformem i szybko uznałem, że idea i sposób działania świetnie odnajdzie się również w środowisku homelabowym.
Możliwe, że ktoś z Was korzysta z AdGuard Home zainstalowanego na wielu urządzeniach i wcale nie musiał rozwiązywać takich problemów, a przynamniej nie w taki sposób jak zrobiłem to ja. I to prawda, jest to możliwe, bowiem istnieje oprogramowanie adguardhome-sync, które właśnie pozwala na łatwe zarządzanie replikami Adguarda. Jednakże, jest to kolejny software, który trzeba instalować, ponadto nie jest on zgodny z ideą GitOps i, przede wszystkim, byłoby to pójście na skróty, a ja ten projekt chciałem wykorzystać do nauki.
GitOps i “Load Balancing”
Do nauki właśnie metodologii GitOps do zarządzania konfiguracją. Adguard Home to oprogramowanie całkiem proste, a przygotowany przeze mnie kod Terraform również nie należy do najbardziej zaawansowanych (wszystko na czystych resources, bez żadnych modułów). Dlatego też uznałem że mógłbym się trochę pobawić w automatyzacje i GitOps. Do tego od jakiegoś czasu korzystałem z codeberg.org (Forgejo), co też dawało mi okazję do pouczenia się Forgejo Actions i to na własnym (self-hosted) runnerze.
Musiałem jednak najpierw rozwiązać wcześniej wspomniane problemy. Wiedziałem, że do wszystkich node’ów powinien być tylko jeden config, tudzież jedno repozytorium z jednym katalogiem – to ograniczy potrzebę skakania po folderach. Jednak, aby działała dystrybucja konfiguracji, to muszę mieć wiele plików stanu (plik stanu w Terraform przechowuje aktualne odwzorowanie zasobów w infrastrukturze na definicje w kodzie, dzięki czemu Terraform wie, co istnieje i jakie zmiany trzeba wprowadzić). Dlatego też uznałem, że ścieżka do pliku stanu może być zmienną podawaną na wejściu komendy tofu apply (komenda, która najpierw wyświetla zmiany, które zostaną wdrożone, a następnie wdraża zmiany na prawdziwą architekturę). Wszelkie inne potrzebne wartości (jak adresy IP, loginy, hasła) również zacząłem zapodawać jako variables, które są podawane przy komendzie wdrożenia zmian. Całe szczęście już wcześniej plik stanu miałem przechowywany w miejscu zdalnym, a dokładnie w buckecie S3. To mi znacznie ułatwiło pracę.
tofu init -var="state_file_path=${{ secrets.STATEFILE_PATH_HOST1 }}"
tofu validate
tofu apply -auto-approve \
-var="adguard_url=${{ secrets.ADGUARD_URL_HOST1 }}" \
-var="adguard_username=${{ secrets.ADGUARD_USERNAME_HOST1 }}" \
-var="adguard_password=${{ secrets.ADGUARD_PASSWORD_HOST1 }}" \
-var="state_file_path=${{ secrets.STATEFILE_PATH_HOST1 }}"
Nie chciałem jednak trzy razy wpisywać tofu apply z różnymi zmiennymi. Wszak nie jest to zbyt wygodne i może sprawić, że zapomnę o którymś z node’ów, a przejście na Terragrunta nie wchodziło w grę. Dlatego też zacząłem pisać własny workflow dla Forgejo Actions. Nie było to trudne, bowiem Forgejo Actions to w rzeczywistości Github Actions, więc byłem już obyty ze składnią czy konceptami. Finalnie przygotowałem workflow, który wdraża zmiany po merge’u do głównej gałęzi. Workflow testuje kod, a następnie wdraża zmiany komendą tofu apply na wszystkich dostępnych node’ach. Przyznam jednak, że workflow wymaga aktualizacji, ponieważ działa na zbyt sztywnych komendach.
Postanowiłem jeszcze się nieco pobawić i przygotowałem kolejne workflowy. Wiedziałem, że odwiecznych problemem DevOpsów są drifty, czyli rozbieżności między kodem, a realną infrastrukturą. Dlatego też przygotowałem cronjoba, który regularnie sprawdza czy takowe drifty występują. Finalnie też przygotowałem workflow, który generuje plan zmian (tofu plan) na wskutek utworzenia nowego PR (zmiany są dodawane jako komentarz do PR).
A jak zacząć korzystać z tych wszystkich node’ów? To bardzo proste. W przypadku sieci lokalnej rozwiązanie było przewidywalne – wystarczy podać adresy obydwóch node’ów w ustawieniach DNS w routerze. Dla reszty urządzeń wykorzystałem Tailscale, gdzie podałem wszystkie trzy node’y jako adresy DNS. Co więcej, ustawiłem, że DNS Tailscale’a nadpisuje domyślny DNS na urządzeniach, gdzie Tailscale jest zainstalowany, dzięki czemu mam pewność, że mój Adguard Home jest w użyciu.
A może jakiś dashboard do tego?
Uznałem jednak, że fajnie byłoby wyciągać metryki ze wszystkich node’ów i je jakoś ładnie wizualizować. Tak się składa, że Adguard Home integruje się z Home Assistant. Wystarczyło dodać wszystkie trzy instalacje AGH do Home Assistanta i utworzyć widgety z wykresami, gdzie widziałbym połączone wyniki.

Utworzyłem sobie również automatyzację, które pozwala mi włączać i wyłączać blokowanie reklam na wszystkich node’ach jednocześnie. Wykorzystuję do tego właśnie automatyzację oraz pomocnik w postaci przełącznika, który jest w stanie ON, kiedy wszystkie node’y mają włączone blokowanie i w stanie OFF kiedy chociaż jeden z node’ów ma wyłączone blokowanie.

Kod tych rozwiązań udostępniłem w repozytorium. Dodam jednak, że wykresy wykorzystują zewnętrzny widget custom:mini-graph-card, który można zainstalować za pomocą HACS (Home Assistant Community Store).
Publikacja projektu AdGuard Home CM Repository
No właśnie, repozytorium. Tak się składa, że projekt postanowiłem opublikować. Nie jest on w żaden sposób zaawansowany, w zasadzie to naprawdę prosty kod i niezbyt rozbudowane automatyzacje. Jednakże projekt reprezentuje kilka ważnych dla mnie rzeczy.
- Projekt może być zarówno dla mnie, jak i dla innych prototypem i wzorcem dla innych repozytoriów wdrażających GitOps w homelab.
- Projekt pokazuje inne podejście do zarządzania i konfiguracji oprogramowania Adguard Home.
- Projekt wykazuje, że Forgejo to nowoczesne oprogramowanie typu Forge, które sprawdzi się w środowisku domowym i biznesowym.
- Projekt dokłada malutką cegiełkę do zjawiska migracji użytkowników z GitHuba na alternatywne rozwiązania Forge.
- Projekt, wbrew pozorom, może przydać się wielu entuzjastom selfhostingu, a jednocześnie prezentuje rzadko wykorzystywane podejście w takim środowisku. Projekt tym samym może zachęcić do nauki Terraforma/OpenTofu i poznania Forgejo Actions.
Podsumowanie
Adguard Home CM Repository to projekt, z którego ciągle korzystam. Adguard spełnia u mnie kilka funkcji, jest blockerem reklam i złośliwych stron, tworzy proste przekierowania domen .local, a także wytworzył u mnie podstawy metodologii GitOps do homelaba. W połączeniu z Tailscale i właśnie Forgejo dostałem bardzo wygodne rozwiązanie, które potrafi sporo nauczyć i zabezpiecza mi bardzo ważny element sieci domowej, czyli właśnie DNS.