Full-stack мониторинг на примере Java приложений

Kate

Administrator
Команда форума
Всем привет! За последние пять лет мы достаточно часто сталкивались с запросами на мониторинг Java приложений. Собрав основные моменты по мониторингу Java, мы решили написать данный пост.

Сегодня мы с вами рассмотрим, что такое full-stack мониторинг и чем он отличается от привычного «уху» понятию мониторинга, нюансы full-stack мониторинга для Java и сложности мониторинга микросервисных приложений на Java. Расскажем, как мы реализуем full-stack мониторинг с помощью open-source стандартов и платной платформы.

5f1484ceaa77bed9c2ee7b436ae72b5b.png

Full-stack мониторинг​

Давайте определимся, что мы называем full-stack мониторингом?

Full-stack мониторинг — это подход в мониторинге производительности приложений, который подразумевает под собой мониторинг всего стека, что включает в себя:

  • Мониторинг приложений — сбор метрик приложения, сбор трейсов транзакций, обеспечение видимости на уровне кода и т.д.
  • Мониторинг инфраструктуры — метрики хостов, процессов, контейнеров и т.д.
  • Мониторинг конечных пользователей — сбор метрик с браузера пользователя, мобильного приложения, синтетические проверки и т.д.
Основное отличие full-stack мониторинга от привычного мониторинга заключается в том, что, применяя full-stack мониторинг, мы получаем реальное представление о том, как себя "чувствует” наше приложение и как его работа влияет на пользовательский опыт, так как видим не просто метрики приложения, а влияние всех инфраструктурных компонентов на приложение и то, как это сказывается на опыте наших пользователей.

Зачастую под full-stack мониторингом подразумевают продукт/решение, который предоставляет единый сервис для сбора, хранения и анализа собираемых данных с приложения, инфраструктуры и пользователей. Но в нашей статье мы поговорим именно про подход и расскажем про использование как open-source стандартов, так и платной платформы для реализации full-stack мониторинга Java.

По результатам нашего опыта можем сказать, что применение full-stack подхода к мониторингу позволяет заметно ускорить процесс root cause анализа инцидентов и действительно снизить MTTR.

Нюансы full-stack мониторинга Java​

Чтобы понимать что нужно мониторить в Java приложениях, давайте для начала разберемся что собой представляет Java. Во-первых, Java — это приложения, написанные не только на языке программирования Java, но и на других языках, которые основываются на Java – Kotlin, Scala, JRuby, и другие.

Во-вторых, Java — это еще и среда для чтения, понимания и выполнения байт-кода Java. Приложения, написанные на Java языке или на языках, базирующихся на Java, компилируются в байткод и запускаются в определенной среде исполнения – JVM (Java Virtual Machine).

Поэтому нам нужно мониторить как само приложение на Java, так и среду, в которой оно запускается.

В целом full-stack мониторинг Java будет выглядеть следующим образом:

  1. Сбор Java и JVM метрик.
  2. Сбор распределенных трассировок транзакций.
  3. Профилирование Java кода.
  4. Мониторинг конечных пользователей.
Давайте подробнее пройдемся по всем компонентам full-stack мониторинга Java.

Сбор метрик Java и Java Virtual Machine​

Под сбором метрик Java подразумевается сбор уникальных метрик с используемых фреймворков Java (SpringBoot, DropWizzard и т.д.) и applications серверов (JBoss/Wildfly, Websphere и т.д.), например:

  • количество HTTP Requests для SpringBoot Framework;
  • количество WebDeployments и количество их активных сессий для JBoss;
  • количество запросов и среднее время отклика servlets для Websphere.
Получить метрики Java Virtual Machine (JVM) можно с помощью имеющейся у Java технологии JMX (Java Management Extensions), которая представляет информацию о состоянии самой JVM, garbage collection и других внутренних элементов.

Какие метрики JVM нас интересуют в первую очередь:

  • Memory Usage
    Общий объем памяти, используемый самой JVM.
    Получим из java.lang.Runtime#totalMemory
  • Threads
    Количество тредов, находящихся в статусах: new, runnable, timed-waiting, waiting или blocked.
    • Текущие идентификаторы тредов получим из java.lang.management.ThreadMXBean#getAllThreadIds
    • Статус тредов получим из ThreadMXBean#getThreadInfo
  • Heap Memory
    Общее использование heap memory самой JVM.
    • Текущее значение получим из java.lang.Runtime#totalMemory - java.lang.Runtime#freeMemory
    • Максимальное значение получим из java.lang.Runtime#maxMemory
  • Memory Pools
    Использование memory pools
    • Информацию о пуле получим из ManagementFactory#getMemoryPoolMXBeans
    • Значение использования памяти пулом получим из java.lang.management.MemoryUsage
    • Максимальное значение получим из getMax, начальное значение получим из getInit, и текущее значение получим из getUsage.
  • Garbage Collection
    Значение garbage collection и время его выполнения
    • Информацию о garbage collection получим из ManagementFactory#getGarbageCollectorMXBeans
    • Значение для каждого коллектора получим из java.lang.management.GarbageCollectorMXBean
    • Garbage collection runtime получим из getCollectionTime
Во время разработки приложений, самыми популярными инструментами для сбора и анализа метрик являются VisualVM и JDK Mission Control.

157375899c388ea918f980325f9b8281.png

Помимо стандартного набора метрик, можно описать свои собственные (кастомные) метрики и здесь нам помогут такие инструменты, как DropWizard, Micrometer, StatsD и Prometheus.

На нашей практике, чаще всего используется Prometheus для описания кастомных метрик. Prometheus предоставляет большой набор интеграций и оберток для большинства популярных фреймворков, основанных на Java. Благодаря чему, Prometheus достаточно просто и быстро интегрируется с приложением.

У всех вышеупомянутых инструментов есть один большой недостаток – метрики изолированы друг от друга из-за чего отсутствует контекст данных, который нам необходим для расследования инцидентов, особенно если у нас микросервисная среда. Но к этому мы вернемся чуть позже, а пока перейдем к трейсингу запросов.

Сбор распределенных трассировок (distributed tracing)​

Чтобы обеспечить быстрое выявление и решение проблем с Java приложениями, одного сбора метрик будет недостаточно, необходимо видеть, как все наши сервисы взаимодействуют между собой, то есть собирать информацию о наших транзакциях. Это возможно сделать с помощью end-to-end трейсинга всех вызовов в нашем Java приложении.

Наиболее распространенный путь в получении end-to-end трейсинга — это использование open-source стандартов, например, таких как, OpenTelemetry и OpenTracing.

216ef0c10ef389a36fa3a9d658d7de2d.png

Пример видимости транзакции в одном из самых распространенных open-source инструментов для end-to-end трейсинга – Jaeger

Но важно учитывать, что, используя разные open source решения - одно для сбора метрик (например, Prometheus), другое для сбора транзакций (Jaeger, Zipkin и т.п.) мы столкнемся с необходимостью ручной корреляции метрик и трейсов между собой. Связанно это с тем, что все open source решения по-разному записывают и хранят данные. И если инцидент затронул много сервисов, что часто происходит в микросервисных инфраструктурах, то при использовании такой связки, как например Prometheus + Jaeger, процесс root-cause анализа инцидента наоборот может усложниться и в результате увеличиться MTTR.

Поэтому для сбора метрик, транзакций и их корреляции между собой в микросервисных инфраструктур, мы используем готовые продукты для full-stack мониторинга, ниже расскажем про их применение.

Профилирование Java кода​

Чтобы понять причину, почему наше Java приложение потребляет много ресурсов, нам нужно видеть, какой именно метод приложения вызывает проблемы с производительностью. Поэтому нужно производить профилирование Java кода.

Профилировщики существуют уже очень давно. Первые профилировщики использовались только на этапе отладки приложения, в тестовой среде, из-за их высокого потребления CPU и Memory ресурсов. Но сейчас существуют профилировщики, которые не несут такой высокой нагрузки, и мы можем их запускать уже в продакшен среде.

1fd7d3ff4fbab3ab29b379a2b4c245b8.jpg

На нашей практике, для профилирования приложения в основном используются такие инструменты, как VisualVM, jProfiler и JavaMissionControl в сочетании с Flight Recorder.

Мониторинг конечных пользователей (End user мониторинг/Real user мониторинг)​

Как мы писали выше, одной из важных частей full-stack мониторинга является мониторинг наших конечных пользователей. Это обусловлено тем, что работа нашего приложения напрямую влияет на пользовательский опыт. Пользователи взаимодействуют с приложением через веб-сайт или мобильное приложение и понимание того какие запросы от пользователей уходят на бэкенд, т.е. запросы к Java приложению, обеспечивают сквозную видимость запроса, от «клика» пользователя в браузере или мобильном приложение до конкретной транзакции на бэкенде.

Таким образом, мы видим влияние скорости работы и ошибок в Java приложение на пользовательский опыт.

Мы решили сейчас много не писать на тему end user мониторинга так как мы ранее уже достаточно подробно раскрыли эту тему в одном из наших постов:

Сложности мониторинга микросервисных Java приложений​

Не могли не затронуть, хотя бы вскользь, тему мониторинга микросервисных Java приложений.

Основная сложность мониторинга таких приложений заключается в том, что увеличивается количество подконтрольных компонентов – контейнеры, оркестраторы (k8s, docker swarm, openshift и т.д). Контейнеры с приложениями постоянно скейлятся, могут запускать только на короткий срок времени. Все это экспоненциально усложняет процесс мониторинга.

Динамичность микросервисной архитектуры Java приложения приводит нас к тому, что для обеспечения full-stack мониторинга нам необходимо проводить постоянную инвентаризацию инфраструктуры c целью получения актуальных данных о нашей инфраструктуре. Это возможно реализовать с помощью autodiscovery подхода .

Под автодискаверингом принято понимать процесс непрерывного, автоматического обнаружения компонентов инфраструктуры с целью сбора ключевых данных о каждом из компонентов. В зависимости от типа компонента – приложение, база данных, балансировщик, очередь и т.д., данные будут собираться разные, но важно, чтобы они связывались между собой.

Визуализация стека компонентов с их ключевыми метриками в продукте Instana
Визуализация стека компонентов с их ключевыми метриками в продукте Instana
Отсутствие автодискаверинга как неотъемлемой части full-stack мониторинга микросервисных приложениях приводит к тому, что вновь запущенные компоненты, могут быть не «замониторены» и в результате у нас появятся слепые зоны в инфраструктуре. Мы не будем иметь представления о том, как себя действительно «чувствует» тот или иной компонент инфраструктуры, не сможем корректно оценить состояние приложения.

Реализовать подход автодискаверинга вручную, достаточно трудоемкий процесс, поэтому в наших проектах мы используем готовое решение – Instana.

Full-stack мониторинг как продукт​

Выше мы с вами рассмотрели full-stack мониторинг как подход, здесь же мы поговорим о full-tack мониторинге как готовом к использованию продукте.

На рынке существуют различные решения для full-stack мониторинга - Dynatrace, DataDog, New Relic, AppDynamics, Instana и т.д. Но мы расскажем именно про Instana, так она на данный момент объединяет в себе объемный функционал, необходимый для full-stack мониторинга как классических инфраструктур, так и микросервисных и выгодно отличается по цене от конкурентов.

С помощью Instana мы получаем full-stack мониторинг в том числе и для Java приложений без необходимости ручной настройки.

Instana постоянно и автоматически собирает ключевые данные для поддерживаемых технологий, относительно JVM обнаруживает все что находится внутри нее:

  • тип JVM;
  • версия JVM;
  • используемый framework;
  • коннекторы к базам данным;
  • Upstream и Downstream сервисы;
  • и многое другое.
Причем для агента Instana не важно в какой инфраструктуре запущено приложение – в Docker, Kubernetes, OpenShift, Linux или Windows. После обнаружения JVM агент подключается к самому процессу и анализирует архитектуру сервиса, какой Framework используется (Spring Boot, DropWizzard и так далее), сервер приложений (Jboss/Wildfly, Websphere, и тд) и коннекторы к базам данных. Далее приложение автоматически инструментируется и агентом начинают собираться ключевые метрики и трейсы приложения. Очень важно, что при этом не требуется перезапуск приложения.

Понимание топологии приложения позволяет Instana построить логическую модель всего стека, обнаружить сервисы и их зависимости.

Давайте посмотрим на пример топологической модели Spring Boot приложения:

  • Sping Boot приложение, это часть ->
  • Java процесса, который представляет собой Java приложение, запущенное в ->
  • JVM, которая запущена в ->
  • Docker контейнере, который запущен в ->
  • процессе операционной системы, который запущен на ->
  • Linux хосте.
Представив топологию в виде вертикального стека зависимостей компонентов инфраструктуры, с каждого уровня необходимо собрать уникальные метрики. И Instana собирает метрики с каждого уровня и визуализирует весь стек.

2d00314153ced86f160755a79c2626d7.png

Но как мы писали выше, метрик недостаточно для полноценного мониторинга Java приложений. Приложения предоставляют собой сервисы, которые между собой как-то взаимодействуют. Instana обнаруживает все вызовы между сервисами и создает распределенные трейсы, показывая end-to-end карту вызовов.

dbe1b403e6ff8c13debe1b28d71fcec1.png

Посмотрев на карту вызовов, возникает необходимость проанализировать трейсы проблемного сервиса. И мы можем более детально проанализировать каждый из вызовов. Instana не сэмплирует вызовы, что позволяет анализировать абсолютно каждый из них.

dc9f68c5c507e58e9453e3cc44ed58db.png

Важной частью мониторинга Java приложений является профилирование кода. У нас могут быть метрики всех компонентов приложения и видимость всех вызовов, и мы можем понять связана ли эта проблема с использованием ресурсов и какой сервис именно пострадал. Но мы не узнаем, какой именно метод в работающем сервисе приложения вызывал проблемы с производительностью. И тут нам поможет постоянно работающий Java профилировщик. Профилировщик покажет точный метод(ы), которые перегружают CPU или имеют большой wait time.

e34cf91bc1c100aa438f543d7f2f4229.png

Подводя итоги​

Актуальность full-stack мониторинга будет только расти и это обусловлено, в том числе, переходом на микросервисную архитектуру, которая экспоненциально усложняет мониторинг по причине постоянного увеличения подконтрольных компонентов инфраструктуры.

Full-stack мониторинг позволяет охватить весь наш стек – приложения, инфраструктуру, пользователей, и значительно ускорить root-cause анализ и снизить MTTR.

Имплементировать full-stack мониторинг возможно и как с помощью подхода “сделай сам”, используя open source стандарты (Prometheus/statsD для сбора метрик, Jaeger/Zipkin для сбора трейсов, jProfiler/Java VisualVM для профилирования Java кода), так и с помощью готовых продуктов, которых на рынке сейчас уже не мало.


Источник статьи: https://habr.com/ru/company/proto/blog/554530/
 
Сверху