Zarządzanie logami na jednym VPS: Loki czy OpenSearch?
Porównanie kosztów utrzymania systemów logowania na pojedynczym VPS. Analiza wymagań RAM dla journald, Loki oraz OpenSearch wraz z zasadami retencji danych dla małych maszyn.
Rzeczywiste koszty samodzielnie utrzymywanego zarządzania logami na jednym VPS
Samodzielne utrzymywanie systemu zarządzania logami na jednym serwerze VPS (virtual private server) sprowadza się do jednego pytania: czy potrzebujesz klastra wyszukiwania, czy wystarczy rotacja i polecenie grep? Większość poradników dostawców odpowiada na to, zaczynając od trzech węzłów i 12 GB pamięci RAM, zanim jeszcze zostanie przesłana choćby jedna linia logów. Na pojedynczym serwerze taka odpowiedź jest bezużyteczna, dlatego poniższe porównanie skupia się na tym, jakich zasobów wymaga każda z opcji od małej maszyny, zanim zacznie ona cokolwiek przechowywać.
Jeśli obsługujesz jeden lub dwa serwery i chcesz sprawdzić, co wydarzyło się w zeszły wtorek, systemd-journald oraz logrotate wykonują to zadanie i możesz zakończyć lekturę po następnej sekcji. Jeśli logi z kilku maszyn muszą trafiać w jedno miejsce z możliwością przeszukiwania danych z wielu tygodni, Grafana Loki sprawdzi się na małej maszynie, ponieważ indeksuje etykiety, a nie treść linii. Elasticsearch oraz OpenSearch oferują pełne wyszukiwanie tekstowe, za które płacą pamięcią operacyjną, ponieważ sterta JVM (Java virtual machine) posiada minimalny próg, którego nie można obniżyć.
Rozpocznij od journald, ponieważ większość osób na tym kończy
Usługa systemd-journald działa domyślnie na każdym współczesnym serwerze Ubuntu lub Debian. Rejestruje ona standardowe wyjście każdej jednostki usługi, komunikaty jądra oraz wszystko, co jest przesyłane do syslog. Cztery polecenia pozwalają rozwiązać większość incydentów.
journalctl -u nginx.service --since "2026-08-14 09:00" --until "2026-08-14 10:00"
journalctl -p err -b
journalctl -f -u ssh.service
journalctl --disk-usageOstatnie z nich wyświetla linię w formacie Archived and active journals take up 1.1G in the file system.. Jest to wartość decydująca o tym, czy wymagane są dalsze kroki. Jeśli rozmiar wynosi kilkaset megabajtów, a poszukiwane informacje można znaleźć za pomocą -u oraz --since, analiza jest zakończona.
To, czy dziennik przetrwa restart, zależy od ustawienia Storage= oraz od istnienia katalogu /var/log/journal. Przy standardowym ustawieniu Storage=auto, journald zapisuje dane do /var/log/journal, jeśli ten katalog istnieje, a w przeciwnym razie do /run/log/journal. Lokalizacja /run jest przechowywana w pamięci RAM, więc na systemach bez wspomnianego katalogu wszystkie logi są usuwane podczas restartu – czyli dokładnie w momencie, w którym są najbardziej potrzebne. Obrazy Ubuntu zawierają ten katalog. Obrazy minimalne oraz kontenerowe często go nie posiadają.
ls -d /var/log/journal
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --disk-usagePo restarcie polecenie journalctl --disk-usage powinno wskazywać rozmiar poniżej /var/log/journal, a nie /run. Wartości domyślne są ograniczone, co sprawia, że journald jest rozwiązaniem profesjonalnym, a nie tylko awaryjnym. Strona podręcznika systemowego journald.conf definiuje SystemMaxUse= jako 10% rozmiaru systemu plików oraz SystemKeepFree= jako 15%, z limitem 4G dla każdej obliczonej wartości domyślnej. Parametr SystemMaxFileSize= domyślnie wynosi jedną ósmą SystemMaxUse=, z limitem 128M, co zazwyczaj pozwala na przechowywanie siedmiu rotowanych plików. Wartość MaxRetentionSec= domyślnie wynosi 0, co wyłącza usuwanie logów na podstawie ich wieku. Warto zwrócić uwagę na ten ostatni parametr: domyślnie dziennik jest ograniczony wyłącznie rozmiarem, a nie czasem przechowywania.
[Journal]
Storage=persistent
SystemMaxUse=2G
MaxRetentionSec=30dayZapisz to ustawienie w /etc/systemd/journald.conf.d/99-size.conf, zrestartuj journald, a następnie sprawdź, czy journalctl --disk-usage zbliżyło się do nowego limitu. Aby odzyskać miejsce natychmiast, zamiast czekać na kolejną rotację, wykonaj sudo journalctl --vacuum-size=500M lub sudo journalctl --vacuum-time=14d. Oba polecenia wyświetlają listę usuwanych plików, więc brak danych wyjściowych oznacza, że nie było nic do usunięcia.
Wszystko poza dziennikiem, na przykład /var/log/nginx/access.log, podlega działaniu logrotate, który uruchamia się codziennie za pomocą timera systemd. Warto znać jeden błąd, ponieważ przypomina on usterkę w df. Po rotacji stary plik znika z listy plików w katalogu, podczas gdy demon nadal go przetrzymuje, przez co df -h zgłasza pełny dysk, podczas gdy du -sh /var/log wskazuje znacznie mniejsze zużycie. Miejsce zwalnia się dopiero, gdy proces ponownie otworzy swój plik logu, do czego służy linia postrotate w konfiguracji. Polecenie sudo lsof -nP +L1 wyświetla usunięte pliki, które są nadal otwarte, oraz wskazuje procesy, które je przetrzymują. Regułę można przetestować bez wprowadzania zmian za pomocą sudo logrotate -d /etc/logrotate.d/nginx.
Przesyłanie logów z wielu serwerów do jednego kolektora
Gdy liczba maszyn przekracza jedną, zarządzanie wieloma serwerami Linux jednocześnie staje się łatwiejsze, gdy logi trafiają w jedno miejsce. rsyslog jest już zainstalowany w większości dystrybucji, więc najtańszym sposobem na centralną kolekcję jest jeden plik na każdym nadawcy.
*.* action(type="omfwd" target="logs.example.com" port="514" protocol="tcp")Zapisz to jako /etc/rsyslog.d/50-forward.conf, sprawdź za pomocą sudo rsyslogd -N1, co zweryfikuje konfigurację i zakończy działanie bez uruchamiania czegokolwiek, a następnie zrestartuj rsyslog. Na kolektorze włącz wejście TCP.
module(load="imtcp")
input(type="imtcp" port="514")Dwa ostrzeżenia, oba natury technicznej. Standardowy syslog nie posiada szyfrowania ani uwierzytelniania, więc każdy, kto ma dostęp do portu 514, może wstrzyknąć linie logów wyglądające identycznie jak Twoje. Powiąż usługę z siecią prywatną lub VPN i zabezpiecz port firewallem. Po drugie, domyślna kolejka działań znajduje się w pamięci RAM, więc gdy kolektor jest nieosiągalny, kolejka zapełnia się, a komunikaty są odrzucane bez tworzenia kopii. rsyslog dokumentuje kolejkę wspomaganą dyskiem dla takich przypadków w swoim poradniku niezawodnego przesyłania.
Dlaczego stos ELK nie mieści się na małym VPS
ELK oznacza Elasticsearch do przechowywania i wyszukiwania, Logstash do potoku przetwarzania danych oraz Kibana do interfejsu. Podstawą jest sterta JVM, która jest rezerwowana przed dotarciem jakichkolwiek logów.
Dokumentacja Elastic zaleca ustawienie sterty na nie więcej niż 50% całkowitej pamięci dostępnej dla każdego węzła Elasticsearch, ponieważ proces wykorzystuje również bufory poza stertą i polega na pamięci podręcznej plików systemu operacyjnego w celu szybkiego odczytu plików indeksu. Zatem sterta 2 GB wymaga maszyny 4 GB, jeszcze przed uruchomieniem Kibana i właściwej aplikacji, dla której serwer został zakupiony. Elastic podaje również, że Elasticsearch automatycznie dobiera rozmiar sterty na podstawie ról węzła i całkowitej pamięci, co oznacza, że mała maszyna otrzymuje małą stertę i spędza większość czasu na odśmiecaniu pamięci (garbage collection).
Logstash to element, który całkowicie wykracza poza mały budżet. Strona ustawień JVM firmy Elastic zaleca stertę nie mniejszą niż 4 GB i nie większą niż 8 GB dla typowego przetwarzania danych. To cała pamięć VPS 4 GB dla jednego procesu w środku potoku.
The data behind this chart
[
{
"label": "Loki plus Alloy (no JVM)",
"documented_heap_mb": 0
},
{
"label": "OpenSearch demo compose",
"documented_heap_mb": 512
},
{
"label": "OpenSearch production example",
"documented_heap_mb": 2048
},
{
"label": "Logstash recommended minimum",
"documented_heap_mb": 4096
}
]Są to wartości publikowane przez każdy projekt w jego własnej dokumentacji. Nie są to pomiary wykonane na maszynie testowej, a obciążenie będzie je zmieniać. Przykładowy plik Compose dla OpenSearch ustawia 512 MB na węzeł w wersji demonstracyjnej oraz 2048 MB w przykładzie produkcyjnym, podczas gdy zalecana dolna granica dla Logstash to 4096 MB. Kolumna sterty wskazuje 0 dla Loki i Alloy, ponieważ są to programy w języku Go, które nie wymagają rezerwacji sterty JVM. To cała różnica wyrażona jedną liczbą: komponent JVM zajmuje swoją rezerwację niezależnie od tego, czy napływają jakiekolwiek logi.
Jeśli mimo to chcesz uruchomić stos Elastic na jednym małym serwerze, zrezygnuj z Logstash i przesyłaj dane bezpośrednio do Elasticsearch za pomocą lekkiego kolektora. Logstash służy do parsowania i transformacji przy dużych wolumenach, a na pojedynczej maszynie można wykonać tę pracę na etapie zbierania lub z niej zrezygnować.
Zarówno Elasticsearch, jak i OpenSearch wymagają również zwiększenia vm.max_map_count do 262144, ponieważ mapują one pliki indeksów w pamięci, a domyślny limit systemu Linux jest dla nich zbyt niski. Kontener, który wyłącza się kilka sekund po starcie na nowej maszynie, zazwyczaj cierpi właśnie z tego powodu.
OpenSearch czy Elasticsearch: co można wdrożyć?
Krótka historia licencji jest istotna, ponieważ określa zasady użytkowania oprogramowania. W styczniu 2021 roku firma Elastic zmieniła licencję Elasticsearch i Kibana z Apache 2.0 na model podwójny: SSPL (Server Side Public License) oraz Elastic License 2.0. AWS utworzył fork ostatniej wersji kodu na licencji Apache 2.0 pod nazwą OpenSearch, która pozostaje na licencji Apache 2.0. We wrześniu 2024 roku Elastic dodał AGPLv3 (GNU Affero General Public License version 3) jako kolejną opcję dla darmowego kodu źródłowego. W przypadku samodzielnego hostingu na jednym VPS przez osobę prywatną, każda z tych licencji pozwala na realizację zamierzonego celu. Ograniczenia licencyjne stają się istotne dopiero w momencie oferowania oprogramowania innym podmiotom jako usługi zarządzanej.
Różnice praktyczne na małym serwerze są mniejsze, niż sugeruje historia, ponieważ oba rozwiązania bazują na tym samym silniku. Różnią się nazewnictwem: cykl życia indeksu to ISM (Index State Management) w OpenSearch oraz ILM (Index Lifecycle Management) w Elasticsearch. Według stanu na sierpień 2026 roku, OpenSearch w wersji 2.12 i nowszych nie uruchomi się bez ustawienia hasła administratora przy pierwszym uruchomieniu.
sudo sysctl -w vm.max_map_count=262144
printf 'vm.max_map_count = 262144\n' | sudo tee /etc/sysctl.d/99-opensearch.conf
docker run -d -p 9200:9200 -p 9600:9600 -e "discovery.type=single-node" \
-e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=<custom-admin-password>" \
opensearchproject/opensearch:latestLinia sysctl -w wprowadza ustawienie natychmiast, natomiast plik w /etc/sysctl.d/ zapewnia trwałość konfiguracji po restarcie. Należy sprawdzić, czy kontener został uruchomiony za pomocą curl -k -u admin:<password> https://localhost:9200. Odpowiada on przez https przy użyciu certyfikatu demonstracyjnego, dlatego -k pomija weryfikację, a poprawna odpowiedź to krótki blok JSON zawierający nazwę klastra oraz wersję. Strona instalacyjna OpenSearch zaleca użytkownikom Docker Desktop przydzielenie hostowi co najmniej 4 GB pamięci, co stanowi wyraźny sygnał dotyczący wymagań tego procesu.
Jak Loki zachowuje niewielki rozmiar: etykiety zamiast pełnego indeksu tekstowego
Loki utrzymuje jeden indeks oparty na etykietach i przechowuje linie logów jako skompresowane fragmenty (chunks). Zapytanie najpierw wybiera strumienie, a następnie filtruje tekst. {unit="ssh.service"} |= "Failed password" wybiera strumień na podstawie jego etykiety, a potem skanuje te fragmenty w poszukiwaniu ciągu znaków. Treść linii nie jest indeksowana, dzięki czemu proces przyjmowania danych (ingestion) jest tani, a w pamięci nie trzeba przechowywać odwróconego indeksu. Koszt operacji przenosi się na czas wykonywania zapytania, co jest korzystnym rozwiązaniem, gdy zazwyczaj wiadomo, której usługi dotyczy wyszukiwanie.
Dokumentacja Grafana określa tryb monolityczny, czyli działanie całego Loki w jednym procesie z -target=all, jako odpowiedni dla małych wolumenów odczytu i zapisu, wynoszących do około 20GB dziennie. Jeden VPS mieści się w tym zakresie bez problemu.
Pułapką jest kardynalność etykiet. Każda unikalna kombinacja wartości etykiet tworzy jeden strumień, a liczba strumieni wpływa na zużycie pamięci i rozmiar indeksu w Loki. Etykieta przechowująca adres IP klienta lub identyfikator żądania tworzy osobny strumień dla każdej wartości, więc obciążony serwer WWW może generować dziesiątki tysięcy strumieni dziennie, co prowadzi do wzrostu zużycia pamięci, aż do momentu przerwania procesu przez jądro systemu. Należy ograniczyć etykiety do wartości, które można policzyć na kartce papieru: unit, host, job, level. Zmienne szczegóły warto umieszczać w samej linii logu, gdzie wyrażenie filtrujące odnajdzie je w czasie wykonywania zapytania.
Instalacja Loki i Alloy na jednym VPS
Dwa procesy wykonują pracę. Loki przechowuje dane i odpowiada na zapytania. Grafana Alloy odczytuje logi i przesyła je dalej. Promtail był wcześniej używany jako przekaźnik, jednak osiągnął status end of life 2 marca 2026, dlatego nowe instalacje wykorzystują Alloy, a przykładowa konfiguracja Docker dla Loki zawiera teraz konfigurację Alloy.
wget https://raw.githubusercontent.com/grafana/loki/v3.7.0/cmd/loki/loki-local-config.yaml -O loki-config.yamlPrzed użyciem pliku należy go przeczytać. Ustawia on path_prefix: /tmp/loki z fragmentami (chunks) w /tmp/loki/chunks, co jest poprawne dla wersji demonstracyjnej, ale nie dla serwera: żadne dane wewnątrz /tmp kontenera nie przetrwają jego odtworzenia, więc historia zniknie przy następnej aktualizacji obrazu. Należy wskazać ścieżkę, która jest zamontowana.
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemorydocker volume create loki-data
docker run --name loki -d \
-v $(pwd):/mnt/config -v loki-data:/loki \
-p 127.0.0.1:3100:3100 \
grafana/loki:3.7.0 -config.file=/mnt/config/loki-config.yaml
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3100/readyOstatnie polecenie powinno zwrócić 200, ponieważ /ready zwraca kod HTTP 200, gdy Loki jest gotowy do przyjmowania ruchu. Każda inna wartość oznacza, że proces nadal się uruchamia lub konfiguracja została odrzucona, a docker logs loki wskazuje przyczynę. Dwa szczegóły w poleceniu uruchomieniowym są zamierzone. Port jest publikowany tylko na 127.0.0.1, ponieważ przykładowa konfiguracja zawiera auth_enabled: false, a Loki nie posiada własnego mechanizmu uwierzytelniania użytkowników. Oznacza to, że każdy, kto ma dostęp do portu 3100, może odczytać wszystkie logi i zapisać fałszywe wpisy. Należy pozostawić go na interfejsie loopback lub za VPN bądź uwierzytelniającym reverse proxy. Nazwany wolumen jest istotny, ponieważ obraz działa jako użytkownik loki z UID 10001, więc katalog na hoście montowany przez bind mount, którego właścicielem jest root, nie będzie zapisywalny dla kontenera.
sudo apt-get update && sudo apt-get install -y gpg wget
sudo mkdir -p /etc/apt/keyrings/
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" \
| sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y alloyAlloy odczytuje /etc/alloy/config.alloy. Konfiguracja ta pobiera systemowy journal oraz zestaw plików i przesyła oba źródła do lokalnego Loki.
loki.write "local" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
}
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
}
loki.source.journal "read" {
forward_to = [loki.write.local.receiver]
relabel_rules = loki.relabel.journal.rules
labels = {job = "systemd-journal", host = "app-01"}
}
local.file_match "nginx" {
path_targets = [{"__path__" = "/var/log/nginx/*.log", "job" = "nginx", "host" = "app-01"}]
}
loki.source.file "nginx" {
targets = local.file_match.nginx.targets
forward_to = [loki.write.local.receiver]
}Reguła relabel kopiuje pole journala __journal__systemd_unit do etykiety o nazwie unit, co sprawia, że {unit="ssh.service"} działa później poprawnie. Bez tej reguły nazwa jednostki znajduje się wewnątrz wpisu, a nie w etykiecie, co uniemożliwia filtrowanie po niej i zmusza każde zapytanie do skanowania wszystkich danych.
sudo systemctl reload alloy
systemctl show -p User alloy
sudo journalctl -n 5
sudo -u alloy journalctl -n 5To etap, na którym większość konfiguracji napotyka problemy. Alloy działa na własnym koncie serwisowym, a nie jako root, a odczyt systemowego journala wymaga przynależności do grupy systemd-journal, podczas gdy pliki w /var/log/nginx należą do grupy adm w systemach Debian i Ubuntu. Należy podstawić konto, które systemctl show wyświetliło w ostatnim poleceniu. Jeśli zwraca ono znacznie mniej wpisów niż uruchomienie jako root, oznacza to, że konto nie może odczytać systemowego journala, a Loki pozostanie pusty, niezależnie od poprawności konfiguracji. Należy dodać grupy i zrestartować usługę za pomocą sudo usermod -aG systemd-journal,adm alloy, a następnie sudo systemctl restart alloy.
curl -G -s "http://127.0.0.1:3100/loki/api/v1/query_range" \
--data-urlencode 'query={job="systemd-journal"}' \
--data-urlencode 'limit=5' | jq '.data.result | length'Liczba powyżej 0 oznacza, że strumienie z tą etykietą istnieją i zawierają wpisy. Wartość 0 oznacza, że pod tą etykietą nie dotarły jeszcze żadne dane. Jedno ustawienie domyślne wyjaśnia częsty fałszywy alarm: loki.source.journal ustawia max_age na 7h, więc świeży start odczytuje ostatnie siedem godzin journala i nic starszego. W celu uzyskania interfejsu graficznego należy uruchomić Grafana na tej samej maszynie i wskazać źródło danych Loki na http://127.0.0.1:3100.. Logi kontenerów wymagają innego źródła: Alloy wykrywa działające kontenery Docker i śledzi je (tail), co jest zgodne z przykładem "getting started" dla Loki, a w przypadku jednowęzłowego klastra k3s na VPS to zadanie przenosi się do katalogu logów poda, do którego zapisuje kubelet.
Retencja: wybierz dzień, w którym logi zostaną usunięte
Prawie nikt nie ustala okresu retencji, dopóki dysk nie zapełni się całkowicie, co zazwyczaj dzieje się o 3 nad ranem, gdy usługa nie działa. Należy podjąć tę decyzję pierwszego dnia, odpowiadając na dwa pytania: jak daleko wstecz faktycznie sięgasz oraz co musi pozostać dostępne podczas analizy incydentu w przyszłym miesiącu. Dla pojedynczego serwera okres od 14 do 30 dni jest wystarczający w obu przypadkach.
Loki nie usuwa żadnych danych, dopóki nie zostanie włączony compactor. Retencja jest domyślnie wyłączona, co zaskakuje użytkowników, których wolumen zapełnił się, podczas gdy retention_period w konfiguracji nie wykonywał żadnej pracy.
limits_config:
retention_period: 744h
compactor:
working_directory: /loki/retention
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
delete_request_store: filesystemWartość 744h odpowiada 31 dniom. Ten blok podlega czterem udokumentowanym zasadom:
- Retencja jest stosowana przez compactor, a dokumentacja Grafana zaleca uruchamianie go jako pojedynczej instancji. Na jednym VPS dzieje się to automatycznie.
- Minimalny okres retencji wynosi 24h, a mechanizm działa tylko wtedy, gdy okres indeksowania wynosi 24h. Przykładowy plik
schema_configjuż używaperiod: 24h, więc nie należy zmieniać tego ustawienia. delete_request_storejest wymagane, gdyretention_enabledma wartość true. Wskazuje ono magazyn przechowujący żądania usunięcia, więc w przypadku pojedynczego węzła korzystającego z systemu plików, musi być zgodne zobject_store: filesystemjuż zdefiniowanym w schemacie.- Fragmenty (chunks) są najpierw oznaczane, a usuwane dopiero po upływie
retention_delete_delay, co w tym przypadku wynosi 2h, więc wolne miejsce pojawia się z opóźnieniem względem polityki. Nie należy oceniać skuteczności ustawienia za pomocądfpięć minut po przeładowaniu.
OpenSearch usuwa całe indeksy, a nie pojedyncze linie, dlatego indeksy logów są tworzone codziennie. Polityka ISM przeprowadza indeks przez kolejne stany i usuwa go, gdy stanie się wystarczająco stary, a ism_template przypisuje tę politykę do nowych indeksów, dzięki czemu nie trzeba o tym pamiętać.
Polityka ISM usuwająca indeksy logów po 14 dniach
{
"policy": {
"description": "delete log indexes after 14 days",
"default_state": "hot",
"states": [
{
"name": "hot",
"actions": [],
"transitions": [
{ "state_name": "delete", "conditions": { "min_index_age": "14d" } }
]
},
{
"name": "delete",
"actions": [ { "delete": {} } ],
"transitions": []
}
],
"ism_template": { "index_patterns": ["logs-*"], "priority": 100 }
}
}Utwórz ją za pomocą żądania PUT do _plugins/_ism/policies/logs-retention. Szablon ma zastosowanie do indeksów utworzonych po zdefiniowaniu polityki, więc dla danych już znajdujących się na dysku należy przypisać politykę ręcznie.
Niezależnie od używanego systemu, wartość retencji jest skuteczna tylko wtedy, gdy towarzyszy jej monitorowanie wolnego miejsca. Usuwanie danych po 14 dniach nie pomoże, jeśli 10 dni logów już zapełnia wolumen, dlatego należy połączyć politykę z monitorowaniem stanu dysku na VPS oraz alertem przy 80% wykorzystania.
Ile miejsca na dysku na GB logów
Uczciwa odpowiedź zależy od liczby linii i pól, dlatego należy dokonać pomiaru na własnych danych, zamiast polegać na opublikowanych wskaźnikach. Mechanizmy działania są na tyle odmienne, że można przewidzieć jedynie kierunek zmian. OpenSearch i Elasticsearch tworzą odwrócony indeks dla każdego indeksowanego pola obok przechowywanego dokumentu, więc rozmiar danych na dysku jest większy niż tekst źródłowy, a każda replika dodatkowo go zwielokrotnia. W przypadku pojedynczego węzła należy ustawić liczbę replik na 0, ponieważ replika shard na tym samym węźle nie przetrwa jego awarii: pozostawienie wartości 1 podwaja zajętość dysku i trwale utrzymuje status klastra jako yellow. Loki zapisuje skompresowane fragmenty (chunks) oraz niewielki indeks etykiet, więc zajętość dysku odpowiada skompresowanemu rozmiarowi linii.
sudo du -sh /var/lib/docker/volumes/loki-data/_data
curl -k -u admin:<password> "https://localhost:9200/_cat/indices?v&h=index,docs.count,store.size"Uruchom odpowiednie polecenie przez dwa kolejne dni. Różnica stanowi dzienny przyrost danych. Pomnóż tę wartość przez liczbę dni retencji, dodaj około 30% zapasu na operacje kompaktowania i scalania, a następnie porównaj wynik z dostępną pojemnością wolumenu. Jeśli dane się nie mieszczą, skróć okres retencji przed zakupem dodatkowego miejsca na dysku, ponieważ większy wolumen jedynie odsunie ten sam problem w czasie o kilka tygodni.
Co ulega awarii jako pierwsze na małym serwerze
Pamięć operacyjna wyczerpuje się najszybciej. Mechanizm OOM (out of memory) killera w jądrze systemu wybiera duży proces, a największym procesem na serwerze logów jest zazwyczaj JVM. journalctl -k | grep -i "killed process" wskazuje na zakończenie procesu z jego nazwą w nawiasach. Ofiarą nie zawsze jest stos logowania: system może wybrać sshd lub bazę danych, co sprawia, że eksperyment z logowaniem unieruchamia aplikację, z której logi miały być zbierane. Należy nadać kontenerom jawne limity, aby awaria wystąpiła w przewidywalnym miejscu, do czego służą limity pamięci w Docker Compose.
Dysk zapełnia się jako drugi, a silniki wyszukiwania ulegają awarii w specyficzny i rozpoznawalny sposób. Elasticsearch oraz OpenSearch monitorują wykorzystanie dysku na kilku poziomach. Dolny próg (low watermark) wynosi 85%, a górny (high watermark) 90%. Po osiągnięciu poziomu krytycznego 95% (flood stage), każdy indeks posiadający shard na tym węźle otrzymuje blokadę index.blocks.read_only_allow_delete, a operacje zapisu kończą się błędem blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]. Blokada jest zwalniana, gdy wykorzystanie spadnie poniżej górnego progu. Należy najpierw zwolnić miejsce, a dopiero w przypadku utrzymywania się problemu ręcznie usunąć blokadę.
curl -k -u admin:<password> -X PUT "https://localhost:9200/_all/_settings" \
-H 'Content-Type: application/json' \
-d '{"index.blocks.read_only_allow_delete": null}'Loki ulega awarii w sposób mniej oczywisty. Nie posiada trybu tylko do odczytu, więc zapełniony wolumen objawia się jako nieudane operacje push po stronie nadawcy oraz luki w wynikach zapytań. Problem kardynalności objawia się powolnym wzrostem zużycia pamięci, a nie komunikatem o błędzie. Należy monitorować rozmiar katalogu chunks zgodnie z harmonogramem, a nie dopiero po wystąpieniu incydentu.
Ostatnim rodzajem awarii jest wprowadzanie niewłaściwych danych. System logów nie jest systemem metryk: obciążenie procesora próbkowane co 10 sekund i przechowywane jako tekst jest kosztowne w utrzymaniu i niewygodne w wizualizacji; to zadanie dla narzędzi takich jak serwer monitorujący Zabbix na Ubuntu 24.04. Wyjątki aplikacji wymagają grupowania, deduplikacji i podglądu śladu stosu (stack trace), co jest zadaniem samodzielnie hostowanego systemu śledzenia błędów. Informacja o tym, czy witryna w ogóle działa, to osobne zadanie, realizowane przez narzędzie do monitorowania czasu pracy i stronę statusu, taką jak Uptime Kuma. System logów należy wykorzystywać do przechowywania linii tekstu, które będą czytane przez człowieka.
FAQ
Czy do przeszukiwania logów serwera potrzebuję Elasticsearch?
Nie w przypadku jednego lub dwóch serwerów. journalctl umożliwia filtrowanie według jednostki, priorytetu, rozruchu oraz zakresu czasowego, a rotowane pliki obsługuje się za pomocą grep oraz zgrep. Klaster wyszukiwania staje się opłacalny pod względem zużycia pamięci dopiero przy dużej liczbie maszyn, gdy wymagane jest przeszukiwanie pełnotekstowe wielu źródeł jednocześnie lub gdy z interfejsu musi korzystać wielu użytkowników. W mniejszych środowiskach journald z ustawionym limitem rozmiaru i czasu przechowywania realizuje te same zadania bez dodatkowego obciążenia pamięci RAM.
Ile pamięci RAM potrzeba do samodzielnego zarządzania logami?
Należy kierować się oficjalnymi wymaganiami poszczególnych projektów, a nie ogólnymi szacunkami. Loki i Alloy to programy napisane w Go, które nie wymagają rezerwacji sterty z góry, a dokumentacja Grafana określa wydajność monolitycznego Loki na około 20 GB logów dziennie. Przykładowy plik compose dla OpenSearch ustawia 512 MB sterty dla wersji demonstracyjnej i 2 GB w przykładzie produkcyjnym. Elastic zaleca, aby sterta nie przekraczała 50% całkowitej pamięci, co oznacza, że przy 2 GB sterty wymagana jest maszyna z 4 GB RAM, nie licząc zasobów dla Kibana. Dokumentacja Logstash zaleca co najmniej 4 GB samej sterty. Są to ustawienia dokumentowane, a nie wyniki testów wydajnościowych, dlatego przed zaplanowaniem zasobów należy zmierzyć rzeczywiste obciążenie.
Jaka jest rzeczywista różnica między Loki a OpenSearch w kontekście logów?
Różnica tkwi w modelu indeksowania. Loki indeksuje wyłącznie etykiety, a treść logów przechowuje w skompresowanych blokach, które są skanowane w momencie zapytania. Dzięki temu zapis jest mało kosztowny, ale szerokie zapytania wymagają więcej zasobów. OpenSearch indeksuje zawartość pól, co przyspiesza dowolne wyszukiwanie pełnotekstowe, jednak wymaga większej ilości pamięci i miejsca na dysku na potrzeby indeksu. Loki należy wybrać, gdy znana jest usługa i przedział czasowy. OpenSearch jest lepszym wyborem, gdy konieczne jest wyszukiwanie tekstu, którego nie można przewidzieć z wyprzedzeniem.
Jak długo należy przechowywać logi na VPS?
Należy określić ten czas, zanim zrobi to za nas brak miejsca na dysku. Ustawienie to należy skonfigurować w jednym miejscu dla każdego systemu: MaxRetentionSec= i SystemMaxUse= dla journald, retention_period z włączonym kompaktorem dla Loki oraz politykę ISM z min_index_age dla OpenSearch. W większości konfiguracji jednoserwerowych okres od 14 do 30 dni jest wystarczający do debugowania i analizy incydentów. Wszystkie dane, które muszą być przechowywane dłużej, powinny być kopiowane poza serwer, ponieważ logi przechowywane wyłącznie na maszynie, która uległa awarii, nie stanowią wiarygodnej dokumentacji.
Czy Promtail to nadal właściwe narzędzie do przesyłania logów do Loki?
Nie. Cykl życia Promtail zakończył się 2 marca 2026, a jego następcą jest Grafana Alloy. Przykład instalacji Loki w Dockerze zawiera teraz konfigurację dla Alloy, a Grafana udostępnia konwerter, który przekształca istniejącą konfigurację Promtail na składnię Alloy. Istniejące instalacje Promtail będą działać nadal, ale nie otrzymują już poprawek, dlatego migrację należy traktować jako zadanie konserwacyjne, a nie aktualizację, którą można dowolnie odkładać w czasie.