Skalowanie Immich: Jak przeprojektowaliśmy odwrotne geokodowanie dla ogromnych bibliotek zdjęć

PixelUnion Team
5 min read
Skalowanie Immich: Jak przeprojektowaliśmy odwrotne geokodowanie dla ogromnych bibliotek zdjęć

Wprowadzenie: Magia wiedzenia “Gdzie”

Dla entuzjastów domowych serwerów i zwolenników prywatności danych Immich stał się wiodącym samohostowanym rozwiązaniem do zarządzania zdjęciami i filmami. Oferuje potężną, prywatną alternatywę dla usług opartych na chmurze. Jedną z najbardziej przekonujących funkcji jest fragment magii w tle: odwrotne geokodowanie. Ten proces automatycznie analizuje współrzędne GPS osadzone w danych EXIF zdjęcia i wzbogaca je o czytelny dla człowieka kontekst lokalizacji — miasto, stan i kraj, w którym obraz został zrobiony.

Jak wspomniano w oficjalnej dokumentacji Immich, ta potężna możliwość jest zasilana przez rozległą geograficzną bazę danych GeoNames. Umożliwia to Immich przekształcenie prostego zestawu współrzędnych w znaczące informacje, które można wykorzystać do wyszukiwania i organizowania wspomnień.

Ale co się dzieje, gdy ta elegancka, samodzielna funkcja musi działać nie tylko dla jednego użytkownika, ale w ogromnej, wielodostępnej platformie? To było wyzwanie skalowania, na które natrafiliśmy w PixelUnion, i zmusiło nas do ponownego przemyślenia sposobu dostarczania tej magii.

Wyzwanie: Standardowe geokodowanie Immich w skali

Aby zaprojektować lepsze rozwiązanie, musieliśmy najpierw docenić strategiczny projekt standardowej architektury Immich. Standardowe podejście jest genialne dla zamierzonej grupy odbiorców — pojedynczych użytkowników — ale podstawowe wybory projektowe tworzą przewidywalne wąskie gardła, gdy jest wdrażane dla dużej bazy użytkowników z tysiącami bibliotek zdjęć.

Jak działa standardowe geokodowanie Immich

W standardowej konfiguracji Immich pobiera zestaw danych GeoNames i ładuje go bezpośrednio do lokalnej tabeli bazy danych PostgreSQL. Zgodnie z dokumentacją, ten proces importu jest aktywowany podczas każdej aktualizacji wersji minor, dzięki czemu dane lokalizacji pozostają aktualne.

Główna zaleta tej architektury jest oczywista: utrzymuje całe przetwarzanie danych całkowicie samodzielnie na serwerze użytkownika. Poprawia to prywatność i prostotę operacyjną, ponieważ nie ma zewnętrznych zależności dla tej podstawowej funkcji. Dla typowego samohostera zarządzającego osobistą biblioteką, tabela bazy danych o rozmiarze około 100 MB jest doskonale rozsądnym i wydajnym rozwiązaniem dla szybkich, lokalnych wyszukiwań napędzających zarówno wyświetlanie lokalizacji, jak i funkcjonalność “Smart Search” Immich.

Wąskie gardło w dużych wdrożeniach

Ten samodzielny model zaczyna się rozpadać w wielkich, wieloużytkownikowych środowiskach, takich jak nasze w PixelUnion. Podstawowym problemem jest rozrost bazy danych. Gdy ta pojedyncza tabela geodanych o rozmiarze ~100 MB jest replikowana na setki lub tysiące indywidualnych instancji baz danych, skumulowany narzut na przechowywanie staje się ogromny. Stanowiło to znaczące wyzwania operacyjne, drastycznie zwiększając trudność i koszty związane z skalowaniem naszej bardzo dużej infrastruktury Postgres.

Drugorzędnym problemem był powtarzający się narzut samego zadania importu. Proces pobierania i ładowania zestawu danych, który działa po każdej aktualizacji Immich, staje się niezwykle nieefektywnym i zasobochłonnym zadaniem, gdy jest zwielokrotniony w całym naszym wdrożeniu. Potrzebowaliśmy sposobu na scentralizowanie tej funkcji bez narażania wydajności.

Nasze rozwiązanie: Wyodrębnienie geokodowania za pomocą mikroserwisu

Naszym strategicznym celem było oddzielenie funkcji geokodowania od podstawowej bazy danych Immich. Ta zmiana architektoniczna została zaprojektowana w celu zapewnienia większej elastyczności, wydajności i skalowalności bez zmiany podstawowych funkcji Immich skierowanych do użytkownika. Chcieliśmy rozwiązać problem skalowania na poziomie infrastruktury, zachowując spójne zachowanie aplikacji.

Zmiana architektury z lokalnej tabeli na centralną API

Nasze rozwiązanie polegało na zastąpieniu bezpośredniego zapytania do bazy danych prostym wywołaniem API do dedykowanego mikroserwisu. Zamiast każdej instancji Immich utrzymującej własną kopię danych GeoNames, uruchamiamy teraz jeden, scentralizowany mikroserwis geokodowania w naszym klastrze. Ta usługa przechowuje dane GeoNames i udostępnia punkty końcowe API do obsługi zarówno żądań odwrotnego geokodowania, jak i wyszukiwania nazw miejsc dla wszystkich instancji Immich.

Co kluczowe, mikroserwis jest zaprojektowany do zwracania danych w dokładnie tym samym formacie, którego Immich oczekuje od swojego wewnętrznego zapytania do bazy danych. To sprawia, że zmiana architektoniczna jest całkowicie przezroczysta dla logiki aplikacji, wymagając jedynie niewielkiej modyfikacji do kierowania żądań do nowego API zamiast do lokalnej tabeli.

Techniczne spojrzenie na kod

Aby to zaimplementować, wprowadziliśmy nową zmienną środowiskową, GEODATA_API_URL, jak widać w naszym commit do bazy kodu Immich. Ta zmienna działa jako flaga funkcji, która mówi instancji Immich, czy powinna używać tradycyjnej metody bazy danych, czy naszego nowego zewnętrznego API zarówno do funkcji geokodowania, jak i wyszukiwania.

Logika rdzenia, widoczna w zmianach map.repository.ts i search.repository.ts, podąża za prostym warunkowym sprawdzeniem dla każdej funkcji:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// In map.repository.ts
IF GEODATA_API_URL is configured:
   call reverseGeocodeViaApi(point, GEODATA_API_URL)
ELSE:
   call reverseGeocodeViaDatabase(point)

// In search.repository.ts
IF GEODATA_API_URL is configured:
   call searchPlacesViaApi(placeName, GEODATA_API_URL)
ELSE:
   call searchPlacesViaDatabase(placeName)

Ta elegancka zmiana jest potężna z dwóch powodów. Po pierwsze, czyni nową funkcję ulepszeniem opt-in, zachowując doskonałą kompatybilność wsteczną dla wszystkich istniejących użytkowników Immich. Po drugie, zapewnia czystą, konfigurowalną ścieżkę dla skalowalnych wdrożeń, takich jak nasze, do przeniesienia całego obciążenia geodanymi bez konieczności złożonego lub inwazyjnego forka głównej aplikacji.

Wpływ: Mierzalne zyski i bardziej skalowalny Immich

Te zmiany architektoniczne nie były tylko teoretycznymi ulepszeniami; zaowocowały znaczącymi, mierzalnymi poprawami wydajności, opłacalności i utrzymywalności naszej platformy.

Kluczowe zrealizowane korzyści

  • Drastyczne odchudzenie bazy danych: Najbardziej bezpośrednim zwycięstwem było dramatyczne zmniejszenie rozmiaru każdej instancji bazy danych Immich. Usuwając tabelę geodanych ~100 MB z bazy danych każdego użytkownika, odzyskaliśmy ogromną ilość przestrzeni w całym klastrze.
  • Uproszczone aktualizacje: Nasza zmiana całkowicie eliminuje zadanie importu geodanych, które Immich wykonywał poprzednio po każdej aktualizacji. Oszczędza to znaczny czas i zasoby obliczeniowe podczas naszych cykli aktualizacji całej platformy. Potwierdzają to nowa logika w kodzie, która wyraźnie loguje “pomijanie lokalnego importu geodanych” gdy nasze zewnętrzne API jest skonfigurowane.
  • Poprawiona skalowalność: Przy mniejszym śladzie bazy danych całe wdrożenie Immich jest łatwiejsze do zarządzania, tworzenia kopii zapasowych i skalowania. Ponadto scentralizowanie logiki poprawia wydajność i spójność zarówno wzbogacania danych lokalizacji, jak i wyszukiwań na platformie, pozwalając nam efektywniej i opłacalniej obsługiwać więcej użytkowników.

Otwarte dla wszystkich

Wierzymy w siłę open source i oddawanie społecznościom, które budują niesamowite narzędzia, takie jak Immich. To rozwiązanie zostało przekazane przez nasz publiczny, open-source fork. Możesz sprawdzić dokładną implementację, od nowej zmiennej środowiskowej po warunkowe wywołania API, w poniższym linku do commitu.

Zobacz pełny commit na GitHub

Końcowe przemyślenia

Nasza droga od identyfikacji krytycznego wąskiego gardła skalowalności do wdrożenia solidnego, oddzielonego rozwiązania mikroserwisowego podkreśla powszechne wyzwanie w inżynierii oprogramowania: funkcja, która jest doskonała dla jednej skali, może stać się przeszkodą w innej. Starannie analizując problem i wdrażając elastyczne, kompatybilne wstecznie rozwiązanie, mogliśmy ulepszyć architekturę Immich dla naszych potrzeb, jednocześnie przekazując wartościową opcję szerszej społeczności. W PixelUnion pozostajemy zaangażowani w open source i zachęcamy wszystkich w społecznościach domowych serwerów i samohostingu do eksplorowania, adaptowania i rozwijania tego rodzaju rozwiązań.