OpenObserve: self-hosted платформа для сбора и анализа логов. Разбор и развертывание в Docker
Недавно я развернул у себя OpenObserve: открытую self-hosted платформу для сбора и анализа логов, метрик и трейсов. До этого логи жили в разных директориях разных сервисов: где-то в /var/log, где-то в journalctl, что-то в docker logs.
Пока всё работает, разбросанные логи не мешают. Боль начинает проявляться в момент аварии: искать причину приходится по нескольким разным местам, а часть истории после рестартов уже потеряна. При этом нужны были не только логи с сервисов: хотелось видеть и метрики, чтобы находить узкие места системы заранее, а не по факту отказа, и наблюдать динамику по графикам, как это обычно делается в Grafana.
Вариантов я перебирал три. ELK с Elasticsearch закрывает логи, но прожорлив и тяжёл в обслуживании: тащить такую связку ради логов нескольких сервисов не хотелось. Связка Grafana, Loki и Prometheus хороша знакомым интерфейсом метрик, но это несколько отдельных сервисов, у каждого своё хранилище и свой конфиг, и сводить их в единый контур хлопотно. Облачные платформы: подключился и забыл, но это деньги за каждый гигабайт, и все данные уезжают наружу.
Перед выбором я прикинул масштаб, чтобы решение не было абстрактным. Мои сервисы суммарно это порядка 340 тысяч пользователей в месяц. Пусть даже каждый активный пользователь делает за визит от пятнадцати до двадцати запросов, в сумме набегают миллионы запросов в месяц: в среднем единицы запросов в секунду и десятки в пиках. Несколько строк логов и метрик на каждый запрос превращаются в несколько гигабайт сырых данных в месяц, а после колоночного сжатия это уже сотни мегабайт. Никаких терабайтов, тяжёлая инфраструктура тут не нужна.
Взвесив всё это, я выбрал OpenObserve: логи, метрики и трейсы в одном сервисе, приём по OpenTelemetry, открытые форматы на диске и никаких облачных счетов. В этой статье расскажу, что у неё внутри, почему для логов я принципиально выбираю self-hosted, и дам полную инструкцию по развертыванию через Docker, включая грабли, на которые я уже наступил.
Что такое OpenObserve
Если коротко, OpenObserve это платформа наблюдаемости, написанная на Rust и собранная в один бинарник. Внутри неё сходятся все три «столпа»: логи, метрики и распределённые трейсы. Плюс дашборды, алерты, пайплайны обработки данных, мониторинг фронтенда (RUM) и даже наблюдаемость LLM-приложений.
Хранение устроено на колоночных Parquet-файлах, а в качестве долговременного слоя может выступать S3-совместимое хранилище: локальный диск, MinIO, бакет в облаке. Данные лежат в открытом формате, поэтому при желании их можно читать напрямую через DuckDB или Spark, даже без самой OpenObserve.
Запросы пишутся на знакомых языках: SQL для логов и трейсов, PromQL для метрик. Собственного проприетарного языка нет, и для меня это отдельный плюс: не нужно учить очередной диалект только ради поиска по строкам.
Совместимость с OpenTelemetry нативная: любой OTel Collector может отправлять данные по OTLP. Рядом есть точки входа, совместимые с Elasticsearch, Loki, Prometheus remote write и Splunk, так что уже развёрнутые Fluent Bit, Vector или Filebeat менять не обязательно.
Проект развивается активно: на GitHub больше 22 тысяч звёзд, релиз 1.0 вышел 11 сентября 2026 года, актуальная версия на момент статьи 1.0.4. Лицензия AGPL-3.0: открытая версия полнофункциональная, в Enterprise уходят только корпоративные вещи вроде SSO и тонкой ролевой модели.
При этом проект остаётся молодым: 1.0 это осень 2026 года, и сообщество пока меньше, чем у ELK. Это стоит держать в голове, если вы выбираете инструмент на десятилетие.
Главное обещание разработчиков, из-за которого о проекте вообще говорят: до 140 раз ниже стоимость хранения, чем у Elasticsearch, и заметно меньше требований к железу. Цифра маркетинговая, получена на их бенчмарках. Но механика за ней реальная: колоночное сжатие Parquet вместо инвертированных индексов, которые Elasticsearch держит к тому же поверх самих данных.
Почему self-hosted здесь принципиален
Логи это, пожалуй, самый чувствительный тип данных в инфраструктуре. В них регулярно попадают токены, e-mail и id пользователей, внутренние адреса, куски конфигов, а иногда и секреты в стектрейсах. Отправлять всё это в чужое облако только чтобы получить удобный поиск я не готов: в self-hosted логи не покидают мой периметр.
Второй аргумент, деньги. Облачные платформы для observability берут плату за счёт объёма: у той же OpenObserve Cloud прайс на момент статьи начинается от $0.50 за гигабайт приёма и $0.01 за гигабайт запросов. Звучит недорого, пока объёмы маленькие. Но телеметрия растёт быстрее почти всего остального в инфраструктуре, и счета за наблюдаемость у команд регулярно догоняют счета за само железо. В публичных кейсах миграций на OpenObserve экономия против Datadog и подобных SaaS выходит в разы: например, DevZero переехали меньше чем за час и посчитали снижение стоимости в 4 раза. В self-hosted себестоимость другая: железо, которое у вас уже есть, плюс диск.
Третий момент, сроки хранения. Централизованный сбор логов в облаке обычно упирается в тариф: базовый retention короткий, за длительное хранение платишь отдельно. В self-hosted срок хранения логов вы назначаете сами. У меня это 60 дней одной переменной в конфиге, а захочу год, поставлю год. Возможность дешёвого долгого хранения для меня главный практический выигрыш: инциденты всплывают сильно позже, чем происходят, и ценность логов со временем не исчезает.
Четвёртый, независимость. Данные в открытом формате, приём по стандарту OpenTelemetry, запросы на SQL. Мигрировать при необходимости можно без переписывания инструментации. Заодно исчезает класс рисков вроде «подняли цены», «сменили лимиты» или «сервис перестал принимать карты».
Логически это тот же выбор, который я описывал в статье про пароли: «Как надёжно хранить пароли и почему лучше не доверять облачным решениям». Данные, по которым можно восстановить картину вашей инфраструктуры, лучше держать у себя.
Развертываем в Docker
Теперь практика. Всё, что ниже, я проделал на своём сервере: подойдёт любой Linux-хост с Docker и плагином Compose.
Создаём каталог и файл docker-compose.yml:
Пара пояснений к конфигу. Root-логин и пароль читаются только при первом старте, потом меняются через UI. Переменная ZO_COMPACT_DATA_RETENTION_DAYS задаёт глобальный срок хранения, а для отдельных потоков его можно переопределить в настройках. Блок с капками памяти важен, если сервер не выделенный: по умолчанию кэши OpenObserve готовы занять до половины RAM хоста на каждый пул, а эти переменные ограничивают аппетиты примерно четырьмя гигабайтами суммарно.
Запускаем:
Открываем http://<адрес-сервера>:5080 и логинимся под кредами из конфига. Инстанс готов принимать данные.
Отправим первый лог, чтобы убедиться, что конвейер живой:
Запись уйдёт в поток (stream) myapp организации default. Ищем её в UI во вкладке Logs обычным SQL: select * from myapp. Тот же запрос доступен и по API: POST /api/default/_search с телом {"query":{"sql":"select * from myapp","start_time":...,"end_time":...,"from":0,"size":10}}, времена в микросекундах Unix, авторизация через Basic.
Дальше подключаем реальные источники. Если у вас уже есть OpenTelemetry, направляем OTel Collector на http://<сервер>:5080/api/default по OTLP/HTTP или на 5081 по OTLP/gRPC, и логи, метрики и трейсы пойдут по стандартному протоколу. Если нет, но есть инфраструктура сбора на Vector или Fluent Bit, OpenObserve понимает совместимые точки входа, и коллекторы можно оставить как есть.
Обновление это смена тега в compose и:
Все данные лежат в каталоге ./data и переживают перезапуск и обновление. Бэкап тоже простой: остановить контейнер и заархивировать data привычным tar.
Грабли, на которые я наступил
Первое и самое неочевидное: образ собран на scratch-базе. Внутри нет ни sh, ни curl, поэтому docker exec в контейнере не работает, а healthcheck в compose не добавить, проверять нечем. Диагностика только через docker logs openobserve, а живость через тот самый /healthz снаружи.
Второй сюрприз ждал с памятью. Про капки выше я написал не случайно: без них кэши и memtable могут занять до половины RAM на пул. На выделенном сервере это терпимо. На общем, где рядом живут другие сервисы, легко получить неприятный сюрприз.
Дальше, вложенные контейнеры. Мой OpenObserve живёт в LXC, и Docker там не может применить лимиты cpu и memory: контроллеры cgroup хосту не делегированы, при старте вылезают ошибки вида memory.max: no such file or directory. Единственный барьер остаётся на уровне переменных ZO_*, о чём стоит помнить при планировании ресурсов.
Про SIMD-сборку. Если возьмёте тег latest-simd, проверьте, что процессор поддерживает AVX-512. На старых Xeon он не заведётся, и тогда выбор простой: обычный образ.
И отдельно про Parquet. Данные доступны для поиска мгновенно, но файлы на диске дозаписываются асинхронно, через WAL и compaction. Пустой каталог потока в первые минуты это норма, не паникуйте и не переустанавливайте ничего.
Что учесть перед выбором
Чтобы обзор не получился односторонним, минусы, которые я вижу.
Движок PromQL в OpenObserve собственный, и он пока не полный: по отчётам мигрировавших команд часть функций (например, histogram_count) и модификатор @ не реализованы. Если планируете перетащить готовые дашборды Grafana, проверьте их заранее.
Формат хранения Vortex, который продвигают разработчики, пока сырой и по умолчанию не включён. Для продакшена берите Parquet, он обкатанный и дефолтный.
В открытой версии нет SSO и тонкого RBAC, это уровни Enterprise. Для личного контура это неважно, а командам стоит держать в голове.
И честная деталь из публичных бенчмарков: на локальном диске Parquet-файлы крупнее, чем сжатые временные ряды Prometheus на том же объёме метрик. Экономия включается в полную силу, когда долговременным слоем выступает объектное хранилище, то есть S3 или MinIO.
Итог
OpenObserve у меня закрывает ровно ту задачу, ради которой ставился: все логи сервисов теперь в одном месте, ищутся SQL-запросами и хранятся столько, сколько я сам решил. Для сценария «хочу наблюдаемость без облачных счетов и без ELK» это лучший баланс из того, что я пробовал.
Если у вас есть свой сервер и хотя бы пара сервисов, чьи логи хочется видеть вместе, присмотритесь: развертывание в Docker займёт полчаса вместе с чтением этой статьи, а железа такой объём нагрузки потребует скромно.
А если вы уже платите коммерческой платформе, проверьте её арифметику: отправьте часть трафика на свой сервер и сравните счета через месяц. Цифры обычно всё объясняют сами.