SSD Nodes Learn 🎉 VPS від $5.50/міс
Посібники Matt ConnorВід Matt Connor

Керування журналами на одному VPS: Loki чи OpenSearch

Порівняйте journald і logrotate з Loki та OpenSearch: дізнайтеся про мінімум 12 GB RAM для кластера, правила retention і коли вистачить grep.

Скільки насправді коштує self-hosted керування журналами на одному VPS

Self-hosted керування журналами на одному VPS (virtual private server) зводиться до одного запитання: вам потрібен кластер пошуку чи достатньо ротації журналів і grep? У більшості посібників постачальників опис починається з трьох вузлів і 12 GB RAM — ще до того, як буде передано один рядок журналу. Для одного сервера така відповідь непридатна, тому нижче варіанти порівнюються за вимогами до невеликого сервера ще до зберігання на ньому будь-яких даних.

Якщо у вас один або два сервери й потрібно знати, що сталося минулого вівторка, systemd-journald і logrotate вже виконують це завдання, і після наступного розділу можна зупинитися. Якщо журнали кількох машин потрібно зберігати в одному місці та шукати за ними протягом кількох тижнів, Grafana Loki підходить для невеликого сервера, оскільки індексує labels, а не текст рядків. Elasticsearch і OpenSearch забезпечують повнотекстовий пошук і потребують за це більше пам’яті, оскільки heap JVM (Java virtual machine) має мінімальний обсяг, нижче якого не можна опуститися.

Почніть із journald, оскільки на цьому зупиняється більшість користувачів

systemd-journald уже запущений на будь-якому сучасному сервері Ubuntu або Debian. Він записує стандартний вивід кожного service unit, повідомлення ядра та все, що надходить до syslog. У більшості випадків достатньо чотирьох команд.

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-usage

Остання команда виводить рядок на зразок Archived and active journals take up 1.1G in the file system.. Саме це значення визначає, чи потрібно робити щось іще. Якщо там указано кілька сотень мегабайтів і потрібні записи можна знайти за допомогою -u і --since, на цьому все.

Збереження журналу після перезавантаження залежить від Storage= і від наявності /var/log/journal. За поширеного значення Storage=auto journald записує дані до /var/log/journal, якщо цей каталог існує, і до /run/log/journal, якщо його немає. /run зберігається в пам’яті, тому на системі без цього каталогу всі журнали видаляються під час перезавантаження — саме тоді, коли вони потрібні для діагностики. Образи Ubuntu містять цей каталог. Мінімальні образи та образи на основі контейнерів часто його не мають.

ls -d /var/log/journal
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --disk-usage

Після перезапуску journalctl --disk-usage має показувати розмір менше за /var/log/journal, а не за /run. Значення за замовчуванням уже мають обмеження, і саме тому journald є повноцінним рішенням, а не запасним варіантом. На сторінці довідки journald.conf параметр SystemMaxUse= установлено на рівні 10% розміру файлової системи, а SystemKeepFree= — 15%; кожне обчислене значення за замовчуванням обмежене 4G. SystemMaxFileSize= за замовчуванням дорівнює одній восьмій від SystemMaxUse=, але не більше ніж 128M, тому зазвичай зберігаються сім ротованих файлів. MaxRetentionSec= за замовчуванням дорівнює 0, що вимикає видалення за віком. Повторимо це останнє значення: за замовчуванням журнал обмежений лише розміром, а не віком.

[Journal]
Storage=persistent
SystemMaxUse=2G
MaxRetentionSec=30day

Запишіть це до /etc/systemd/journald.conf.d/99-size.conf, перезапустіть journald і перевірте, чи наблизився journalctl --disk-usage до нового обмеження. Щоб звільнити місце одразу, а не чекати наступної ротації, виконайте sudo journalctl --vacuum-size=500M або sudo journalctl --vacuum-time=14d. Обидві команди виводять назви всіх видалених файлів, тому відсутність виводу означає, що видаляти було нічого.

Усім, що розташоване поза журналом, наприклад /var/log/nginx/access.log, керує logrotate, який запускається щодня через systemd timer. Важливо знати про одну особливість, оскільки вона виглядає як помилка в df. Після ротації старий файл зникає зі списку каталогу, але daemon усе ще тримає його відкритим, тому df -h повідомляє про заповнений диск, тоді як du -sh /var/log показує значно менший обсяг. Місце звільняється лише після повторного відкриття журналу процесом. Саме для цього в конфігурації передбачено рядок перезавантаження postrotate. sudo lsof -nP +L1 виводить видалені файли, які все ще відкриті процесами, і назви процесів, що їх утримують. Перевірте правило без внесення змін за допомогою sudo logrotate -d /etc/logrotate.d/nginx.

Надсилання журналів із кількох серверів до одного збирача

Якщо серверів стає більше одного, керувати кількома Linux-серверами одночасно простіше, коли їхні журнали надходять в одне місце. rsyslog уже встановлено в більшості дистрибутивів, тому найдешевший варіант центрального збирача — один файл на кожному сервері-відправнику.

*.* action(type="omfwd" target="logs.example.com" port="514" protocol="tcp")

Збережіть це у /etc/rsyslog.d/50-forward.conf, перевірте за допомогою sudo rsyslogd -N1. Ця команда перевіряє конфігурацію та завершує роботу, не запускаючи нічого. Потім перезапустіть rsyslog. На сервері-збирачі увімкніть приймання через TCP.

module(load="imtcp")
input(type="imtcp" port="514")

Два суто технічні застереження. Звичайний syslog не забезпечує шифрування та автентифікацію. Тому будь-який вузол, який має доступ до порту 514, може додати рядки журналу, що виглядатимуть так само, як ваші. Прив’яжіть сервіс до приватної мережі або VPN і налаштуйте firewall для цього порту. Друге застереження: черга дій за замовчуванням зберігається в пам’яті. Якщо збирач недоступний, черга заповнюється, а повідомлення втрачаються без збереженої копії. Для цього випадку rsyslog описує чергу з підтримкою диска у своєму навчальному посібнику з надійної пересилки.

Чому стек ELK не підходить для невеликого VPS

ELK означає Elasticsearch для зберігання та пошуку, Logstash для конвеєра приймання даних і Kibana для інтерфейсу. Мінімальний поріг визначає heap JVM, який задається ще до надходження першого журналу.

У документації Elastic зазначено, що heap потрібно встановлювати на рівні не більше 50% загального обсягу пам’яті, доступної кожному вузлу Elasticsearch, оскільки процес також використовує буфери поза heap і покладається на файловий кеш операційної системи для швидкого читання файлів індексів. Отже, heap обсягом 2 GB передбачає машину з 4 GB пам’яті — ще до запуску Kibana та інших завдань, для яких фактично придбано сервер. Elastic також зазначає, що Elasticsearch автоматично визначає розмір heap на основі ролей вузла та загального обсягу пам’яті. Тому невеликий сервер отримує невеликий heap і постійно витрачає ресурси на збирання сміття.

Саме Logstash остаточно перевищує бюджет невеликого сервера. На власній сторінці з налаштуваннями JVM Elastic рекомендує для типового приймання даних heap обсягом не менше 4GB і не більше 8GB. Це весь обсяг пам’яті VPS на 4 GB для одного процесу в середині конвеєра.

ChartDocumented JVM heap settings, from each project's own docs
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
  }
]

Це значення, опубліковані кожним проєктом у власній документації. Це не результати вимірювань на тестовому сервері, і ваше робоче навантаження може змінити їх. У прикладі compose-файлу OpenSearch задано 512 MB heap на вузол для демонстрації та 2048 MB у прикладі для production, тоді як рекомендована нижня межа для Logstash становить 4096 MB. У стовпці heap для Loki та Alloy указано 0, оскільки це програми на Go, яким не потрібно резервувати heap JVM. У цьому й полягає вся різниця, виражена одним числом: компонент JVM резервує пам’ять незалежно від того, надходять журнали чи ні.

Якщо ви все одно хочете запустити стек Elastic на одному невеликому сервері, відмовтеся від Logstash і надсилайте дані безпосередньо до Elasticsearch за допомогою легкого збирача. Logstash призначений для масового розбору та перетворення даних, а на одному сервері цю роботу можна виконувати на периферії або пропустити.

Для Elasticsearch і OpenSearch також потрібно збільшити vm.max_map_count до 262144, оскільки вони відображають файли індексів у пам’ять, а стандартний ліміт Linux для цього замалий. Якщо контейнер завершує роботу через кілька секунд після запуску на щойно налаштованому сервері, зазвичай причина саме в цьому.

OpenSearch чи Elasticsearch: який із них можна розгорнути?

Коротка історія ліцензування, адже саме вона визначає, що вам дозволено запускати. У січні 2021 року Elastic перевів Elasticsearch і Kibana з Apache 2.0 на подвійну модель ліцензування SSPL (server side public license) та Elastic License 2.0. AWS відгалузив останній код під Apache 2.0 у проєкт OpenSearch, який і надалі поширюється за Apache 2.0. У вересні 2024 року Elastic додав AGPLv3 (GNU Affero General Public License version 3) як ще один варіант для вільного вихідного коду. Для самостійного розгортання однією людиною на одному VPS усі ці ліцензії дозволяють те, що ви робите. Обмеження ліцензій стають важливими, коли ви надаєте програмне забезпечення іншим людям як керований сервіс.

Практична різниця на невеликому сервері менша, ніж можна припустити з історії ліцензування, оскільки в основі обох продуктів лежить той самий рушій. Відрізняються назви: в OpenSearch керування життєвим циклом індексів називається ISM (index state management), а в Elasticsearch — ILM (index lifecycle management). Станом на серпень 2026 року OpenSearch 2.12 і новіші версії відмовляються запускатися, якщо під час першого запуску не встановлено пароль адміністратора.

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:latest

Рядок sysctl -w застосовує параметр негайно, а файл у /etc/sysctl.d/ містить його частину, яка зберігається після перезавантаження. Перевірте запуск контейнера за допомогою curl -k -u admin:<password> https://localhost:9200. Він відповідає через https із демонстраційним сертифікатом, тому -k пропускає перевірку, а коректна відповідь містить невеликий блок JSON із назвою кластера та версією. На сторінці встановлення OpenSearch також зазначено, що користувачам Docker Desktop слід виділити хосту щонайменше 4 GB пам’яті. Це дає змогу оцінити потреби процесу в ресурсах.

Як Loki залишається компактним: labels замість повнотекстового індексу

Loki зберігає один індекс за labels, а рядки журналу — у стиснених chunks. Спочатку запит вибирає streams, а потім фільтрує текст. {unit="ssh.service"} |= "Failed password" вибирає stream за його label, а потім шукає в цих chunks потрібний рядок. Вміст рядка не індексується, тому ingestion залишається недорогим, а inverted index не потрібно зберігати в пам’яті. Витрати переносяться на час виконання запитів. Це вигідний компроміс, якщо зазвичай відомо, який саме сервіс ви переглядаєте.

Документація Grafana відносить monolithic mode, тобто весь Loki в одному процесі з -target=all, до сценаріїв із невеликим обсягом читання та запису — приблизно до 20GB на день. Один VPS легко вкладається в цей обсяг.

Проблема — cardinality labels. Кожна унікальна комбінація значень labels утворює окремий stream, а кількість streams визначає обсяг пам’яті та розмір індексу Loki. label зі значенням IP-адреси клієнта або ідентифікатора запиту створює окремий stream для кожного значення. Тому завантажений web-сервер може створити десятки тисяч streams за день, і процес зростатиме, доки kernel не зупинить його. Використовуйте в labels лише значення, які можна порахувати на папері: unit, host, job, level. Змінні деталі зберігайте безпосередньо в рядку. Під час виконання запиту їх можна знайти за допомогою filter expression.

Встановіть Loki та Alloy на одному VPS

Два процеси виконують цю роботу. Loki зберігає журнали й обробляє запити. Grafana Alloy читає журнали та передає їх. Раніше для цього використовували Promtail, але 2 March 2026 він досяг кінця життєвого циклу, тому в нових інсталяціях використовують Alloy, а власний приклад Docker для Loki тепер містить конфігурацію Alloy.

wget https://raw.githubusercontent.com/grafana/loki/v3.7.0/cmd/loki/loki-local-config.yaml -O loki-config.yaml

Прочитайте цей файл перед використанням. Він налаштовує path_prefix: /tmp/loki зі chunks у /tmp/loki/chunks, що підходить для демонстрації, але не для сервера: нічого в /tmp контейнера не зберігається після його повторного створення, тому під час наступного оновлення image історія зникне. Вкажіть шлях, який ви монтуєте.

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: inmemory
docker 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/ready

Остання команда має вивести 200, оскільки /ready повертає HTTP 200, коли Loki готовий приймати network traffic. Будь-яке інше значення означає, що процес ще запускається або конфігурацію відхилено; docker logs loki покаже причину. Два параметри в команді запуску задано навмисно. Порт опубліковано лише на 127.0.0.1, оскільки приклад містить auth_enabled: false, а Loki не має власної автентифікації, тому будь-хто, хто може отримати доступ до порту 3100, зможе читати всі журнали та записувати підроблені. Залиште порт доступним лише через loopback або розмістіть Loki за VPN чи reverse proxy з автентифікацією. Named volume важливий, оскільки image запускається від імені користувача loki з UID 10001, а змонтований каталог хоста, власником якого є root, контейнер не зможе записувати.

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 alloy

Alloy читає /etc/alloy/config.alloy. Ця конфігурація отримує system journal і один набір файлів, а потім передає обидва джерела до локального 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]
}

Правило relabel копіює поле журналу __journal__systemd_unit у label з назвою unit. Саме це забезпечує роботу {unit="ssh.service"} надалі. Без цього правила назва unit зберігається всередині запису, а не в label, тому вибирати записи за нею неможливо й кожен запит має сканувати все.

sudo systemctl reload alloy
systemctl show -p User alloy
sudo journalctl -n 5
sudo -u alloy journalctl -n 5

Саме на цьому зупиняється більшість налаштувань. Alloy працює від імені власного service account, а не root. Для читання system journal цей обліковий запис має входити до групи systemd-journal. Файли в /var/log/nginx у Debian і Ubuntu належать групі adm. Підставте в останню команду обліковий запис, який вивела systemctl show. Якщо вона повертає значно менше записів, ніж запуск від root, цей обліковий запис не може читати system journal. У такому разі Loki залишатиметься порожнім, незалежно від правильності конфігурації. Додайте групи й перезапустіть сервіс за допомогою sudo usermod -aG systemd-journal,adm alloy, а потім 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'

Значення більше за 0 означає, що існують streams із цим label і вони містять записи. Значення 0 означає, що під цим label ще не надійшло жодного запису. Одне стандартне налаштування часто спричиняє помилкову тривогу: loki.source.journal встановлює max_age на 7h, тому після свіжого запуску Alloy читає останні сім годин journal і не читає старіші записи. Для вебінтерфейсу запустіть Grafana на тому самому сервері та вкажіть джерелом даних Loki http://127.0.0.1:3100.. Для container logs потрібне інше джерело: Alloy виявляє запущені Docker containers і читає їхні журнали, як у власному прикладі Loki для початкового налаштування. У кластері k3s з одним вузлом на VPS це завдання переміщується до каталогу журналів pod, у який kubelet записує дані.

Зберігання: визначте, коли журнали видаляти

Майже ніхто не визначає період зберігання, доки диск не заповниться. Тоді це доводиться робити о 3 годині ночі, коли сервіс уже не працює. Визначте його в перший день, відповівши на два запитання: наскільки далеко в минуле ви фактично переглядаєте журнали та що має залишитися під час розслідування інциденту наступного місяця. Для одного сервера період від 14 до 30 днів зазвичай відповідає обом вимогам.

Loki взагалі нічого не видаляє, доки ви не ввімкнете compactor. Зберігання за замовчуванням вимкнене. Це дивує користувачів, у яких том заповнився, хоча retention_period залишався в конфігурації та не виконував жодних дій.

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: filesystem

744h — це 31 день. Цей блок регулюють чотири документовані правила:

  • Зберігання застосовує compactor, і в документації Grafana зазначено, що compactor потрібно запускати як один екземпляр. На одному VPS це відбувається автоматично.
  • Мінімальний період зберігання становить 24h, а зберігання працює лише тоді, коли період індексу становить 24h. У прикладі schema_config уже використовується period: 24h, тому не змінюйте це значення.
  • delete_request_store потрібен, якщо retention_enabled має значення true. Він визначає сховище для запитів на видалення. Тому на одному вузлі з файловою системою він має відповідати object_store: filesystem, яке вже вказане в схемі.
  • Спочатку chunks позначаються, а потім видаляються після retention_delete_delay, тобто в цьому випадку через 2h. Тому вільне місце з’явиться пізніше, ніж передбачає політика. Не оцінюйте це налаштування за значенням df через п’ять хвилин після перезавантаження конфігурації.

OpenSearch видаляє цілі індекси, а не окремі рядки. Саме тому індекси журналів створюються щодня. Політика ISM переміщує індекс між станами та видаляє його, коли він досягає потрібного віку. ism_template прикріплює політику до нових індексів, тому вам не потрібно робити це вручну.

Політика ISM, яка видаляє індекси журналів через 14 днів
{
  "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 }
  }
}

Створіть її за допомогою PUT-запиту до _plugins/_ism/policies/logs-retention. Шаблон застосовується до індексів, створених після появи політики. Тому до даних, які вже зберігаються на диску, політику потрібно прикріпити вручну.

Незалежно від того, яку систему ви використовуєте, період зберігання ефективний лише за наявності контролю вільного місця. Видалення журналів через 14 днів не допоможе, якщо 10 днів журналів уже заповнюють том. Тому поєднайте політику з моніторингом стану диска на VPS і сповіщенням, яке спрацьовує при використанні 80% дискового простору.

Скільки диска потрібно на 1 GB журналів

Точна відповідь залежить від ваших рядків і полів, тому вимірюйте це на власних даних, а не покладайтеся на опубліковані коефіцієнти. Механізми достатньо різні, щоб спрогнозувати напрямок. OpenSearch і Elasticsearch створюють інвертований індекс для кожного індексованого поля разом зі збереженим документом. Тому обсяг даних на диску більший за розмір необробленого тексту, а кожна репліка збільшує його ще в кілька разів. На одному вузлі встановіть кількість реплік у 0, оскільки репліка шарда на тому самому вузлі не захистить дані, якщо цей вузол відмовить. Значення 1 подвоїть використання диска та назавжди залишить стан кластера жовтим. Loki записує стиснуті чанки та невеликий індекс міток, тому його обсяг переважно залежить від стиснутого розміру рядків.

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"

Запустіть відповідну команду протягом двох послідовних днів. Різниця покаже щоденний приріст. Помножте його на кількість днів зберігання та додайте приблизно 30% запасу для compaction і merges, а потім порівняйте результат з обсягом тому. Якщо дані не вміщуються, спочатку зменште період зберігання, а не купуйте додатковий диск. Більший том лише відтермінує ту саму проблему на кілька тижнів.

Що виходить з ладу першим на невеликому сервері

Спочатку закінчується пам’ять. OOM killer ядра (out of memory) вибирає великий процес, а найбільшим процесом на сервері журналювання зазвичай є JVM. journalctl -k | grep -i "killed process" показує завершення процесу, зазначаючи його ім’я в дужках. Жертвою не завжди стає стек журналювання: натомість може бути вибрано sshd або вашу базу даних. Так експеримент із журналюванням може вивести з ладу застосунок, для якого ви хотіли збирати журнали. Задайте для контейнерів явні обмеження, щоб збій стався саме там, де ви це передбачили. Саме для цього призначені обмеження пам’яті в Docker Compose.

Далі закінчується дисковий простір, і пошукові системи виходять з ладу характерним і передбачуваним способом. Elasticsearch і OpenSearch контролюють використання диска на кількох рівнях. Нижній поріг становить 85%, а верхній — 90%. На рівні flood stage 95% кожен індекс, що має shard на цьому вузлі, отримує блок index.blocks.read_only_allow_delete, після чого операції запису завершуються помилкою blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]. Блок знімається, коли використання диска знову опускається нижче верхнього порога. Спочатку звільніть місце, а потім видаліть блок вручну, лише якщо він не зникає.

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 виходить з ладу менш помітно. Він не переходить у режим лише для читання. Тому заповнений том проявляється як невдалі push-операції на стороні відправника та пропуски в результатах запитів, а проблема високої cardinality розвивається як повільне зростання використання пам’яті без помилки. Періодично контролюйте розмір каталогу chunks, а не лише після інциденту.

Останній тип збою — передавання неправильних даних. Система журналювання не є системою метрик: зберігати як текст завантаження CPU, виміряне кожні 10 секунд, дорого, а будувати за такими даними графіки незручно. Це завдання належить, наприклад, серверу моніторингу Zabbix на Ubuntu 24.04. Винятки застосунків потребують групування, усунення дублікатів і перегляду stack trace. Для цього призначений self-hosted трекер помилок. Визначення самого факту недоступності сайту — окреме завдання. Його вирішує система моніторингу доступності та сторінка статусу, наприклад Uptime Kuma. Використовуйте систему журналювання для текстових рядків, які читатиме людина.

FAQ

Чи потрібен Elasticsearch для пошуку в журналах мого сервера?

Ні, якщо у вас один або два сервери. journalctl уже фільтрує записи за unit, пріоритетом, завантаженням і часовим діапазоном, а ротовані файли відповідають на запити grep і zgrep. Кластер пошуку виправдовує використання пам’яті, коли у вас багато машин, коли потрібно одночасно виконувати повнотекстовий пошук на всіх них або коли кільком людям потрібен спільний інтерфейс. Якщо ці умови не виконуються, journald з обмеженням розміру та строком зберігання виконує ту саму роботу без додаткового використання RAM.

Скільки RAM потрібно для self-hosted керування журналами?

Використовуйте опубліковані показники кожного проєкту, а не загальне правило. Loki та Alloy — програми на Go, яким не потрібно заздалегідь резервувати heap, а Grafana вказує для монолітного Loki до приблизно 20GB на день. Приклад compose для OpenSearch задає 512 MB heap для демонстрації та 2 GB у production-прикладі, а Elastic зазначає, що heap має становити не більше 50% загального обсягу пам’яті. Отже, heap обсягом 2 GB означає машину з 4 GB пам’яті ще до встановлення Kibana. Документація Logstash рекомендує щонайменше 4GB heap лише для самого Logstash. Це задокументовані налаштування, а не результати бенчмарків, тому перед плануванням ресурсів виміряйте власне навантаження.

У чому полягає реальна різниця між Loki та OpenSearch для журналів?

У моделі індексації. Loki індексує лише labels і зберігає тіло журналу як стиснені chunks, які скануються під час виконання запиту. Тому запис даних дешевий, але широкі запити виконуються дорожче. OpenSearch індексує вміст полів, тому довільний повнотекстовий пошук працює швидко, а за індекс потрібно платити і пам’яттю, і дисковим простором. Обирайте Loki, коли знаєте потрібний сервіс і часовий діапазон. Обирайте OpenSearch, коли потрібно шукати текст, структуру якого неможливо передбачити заздалегідь.

Як довго слід зберігати журнали на VPS?

Визначте строк заздалегідь, а не після того, як це вирішить обсяг диска. Задайте його рівно в одному місці для кожної системи: MaxRetentionSec= і SystemMaxUse= для journald, retention_period з увімкненим compactor для Loki та політику ISM із min_index_age для OpenSearch. Для більшості конфігурацій з одним сервером 14–30 днів достатньо для налагодження й аналізу інцидентів. Дані, які потрібно зберігати довше, слід копіювати за межі сервера, оскільки журнал, що зберігався лише на сервері, який вийшов з ладу, не є надійним записом.

Чи залишається Promtail рекомендованим способом передавання журналів до Loki?

Ні. Promtail досяг кінця життєвого циклу 2 March 2026, і його замінює Grafana Alloy. У власному прикладі встановлення Loki для Docker тепер використовується конфігурація Alloy, а Grafana надає конвертер, який перетворює наявну конфігурацію Promtail у синтаксис Alloy. Наявне встановлення Promtail продовжить працювати, але більше не отримуватиме виправлень. Тому міграцію слід розглядати як технічне обслуговування, а не як оновлення, яке можна безстроково відкладати.

#logging#loki#opensearch#journald#monitoring