- PVSM.RU - https://www.pvsm.ru -
Бытует мнение, что бесконечно можно смотреть на огонь, воду и то, как другие работают, но есть и ещё кое-что! Мы уверены, что можно бесконечно говорить с Сашей goldshtn [1] Гольдштейном о перформансе. Мы уже брали у Саши интервью [2] перед JPoint 2017, но тогда разговор касался конкретно BPF, которому был посвящен доклад Саши.
На этот раз мы решили копнуть глубже и узнать фундаментальные проблемы мониторинга производительности и варианты их решения.
— В прошлый раз мы довольно подробно поговорили про BPF и вскользь обсудили проблемы мониторинга производительности Java под Linux. В этот раз хотелось бы сконцентрироваться не на конкретном инструменте, а на проблемах и поиске решений. Первый вопрос, довольно банальный, — как понять, что с производительностью есть проблемы? Следует ли думать об этом, если пользователь не жалуется?
Саша Гольдштейн: Если начинать думать о производительности только в момент, когда ваши пользователи жалуются, с вами они будут недолго. Для многих инженерия производительности — это траблшутинг и crisis mode. Телефоны звонят, свет мигает, система упала, клавиатура горит — обычные трудовый будни перформанс-инженера. В реальности же они проводят большую часть своего времени, планируя, проектируя, мониторя и предотвращая кризисы.
Для начала, capacity planning — оценка ожидаемой нагрузки системы и использования ресурсов; проектирование масштабируемости поможет избежать узких мест и получить значительные увеличения в нагрузке; инструментирование и мониторинг жизненно необходимы для понимания того, что происходит внутри системы, чтобы не копаться вслепую; благодаря установке автоматического оповещения вы точно будете знать о любых возникающих проблемах, как правило ещё до того, как пользователи начнут жаловаться; ну и конечно же будут единичные кризисы, решать которые придётся в стрессовых условиях.
Стоит отметить, что тулы постоянно меняются, но сам процесс остаётся неизменным. Приведу пару конкретных примеров: capacity planning вы можете сделать и на бумажке на коленке; можно использовать APM-решения (как New Relic или Plumbr) для end-to-end инструментирования и мониторинга, AB и JMeter для быстрого нагрузочного тестирования и так далее. Чтобы узнать больше, можно почитать книгу Брендана Грегга «Systems Performance» [3] — это отличный источник по теме жизненного цикла и методологии перформанса, а «Site Reliability Engineering» [4] от Google освещает тему установки характеристик производительности (Service Level Objectives) и их мониторинга.
— Допустим, мы поняли, что проблема есть: с чего начинать? Мне часто кажется, что многие (особенно не профессиональные перформанс-инженеры) сразу готовы расчехлять JMH, переписывать всё на unsafe и «хакать компиляторы». Потом смотреть, что получилось. Но ведь в реальности лучше не с этого начинать?
Саша Гольдштейн: Это довольно распространённая практика, когда при написании кода и проведении базовых тестов профайлера возникают проблемы с перформансом, которые можно легко исправить, поменяв код или «хакнув компиляторы». Однако на проде, исходя из моего опыта, это делается не так часто. Многие проблемы присущи только какой-то одной среде, вызваны меняющимися паттернами рабочей нагрузки или связаны с узкими местами вне кода вашего приложения, и лишь малую часть можно замикробенчмаркать и улучшить на уровне исходного кода «умными» хаками.
Вот пара примеров для иллюстрации:
Я понимаю, что часто гораздо проще сфокусироваться на вещах, которыми ты можешь управлять, например, хаки на уровне приложений. Чисто психологически это понятно, не требует глубоких знаний системы или среды и почему-то считается «круче» в некоторых культурах. Но обращаться к этому в первую очередь — неправильно.
— Наверняка следует сначала посмотреть, как приложение/сервис работает в продакшне. Какие инструменты ты для этого рекомендуешь, а какие — не очень?
Саша Гольдштейн: Мониторинг и профайлинг на проде — это набор инструментов и техник.
Начинаем с метрик высокоуровневого перформанса, фокусируясь на использовании ресурсов (процессор, память, диск, сеть) и характеристике нагрузки (# запросов, ошибок, типов запросов, # запросы к базе данных). Есть стандартные тулы для получения этих данных для каждой операции и времени выполнения. К примеру, на Linux обычно используют инструменты вроде vmstat, iostat, sar, ifconfig, pidstat; для JVM используют JMX-based тулы или jstat. Это метрики, которые можно непрерывно собирать в базу данных, возможно с 5-ти или 30-ти секундным интервалом, чтобы можно было проанализировать скачки и при необходимости вернуться в прошлое, чтобы скоррелировать предыдущие операции по развёртыванию, релизы, мировые события или изменения рабочей нагрузки. Важно, что многие фокусируются на собирании только средних показателей; они хоть и хороши, но, по определению, не представляют полное распределение того, что вы измеряете. Гораздо лучше собирать процентили, а по возможности даже и гистограммы.
Следующий уровень — это операционные метрики, которые обычно нельзя непрерывно собирать или хранить долгое время. Они включают в себя: лог сбора мусора, запросы сети, запросы к базе данных, классовые нагрузки и так далее. Разобраться в этих данных после того, как их где-то хранили, иногда гораздо труднее, чем, собственно, их собирать. Это позволяет, однако, задавать вопросы, вроде «какие запросы работали, пока нагрузка ЦП базы данных повышалась до 100%» или «какими были IOPS дисков и время отклика во время выполнения этого запроса». Одни лишь числа, особенно в виде средних показателей, не позволят вам провести подобного рода исследование.
И наконец, «хардкорный» уровень: SSH в сервере (или удаленный запуск тулов) для сбора большего количества внутренних метрик, которые нельзя хранить во время штатной работы сервиса. Это инструменты, которые обычно называют «профайлерами».
Для профайлинга Java-продакшна существует множество жутких тулов, которые не только дают большой оверхэд и задержки, но могут ещё и лгать вам. Несмотря на то, что экосистеме уже около 20 лет, есть лишь несколько надёжных профайлинговых техник с низким оверхэдом для JVM-приложений. Я могу порекомендовать Honest Profiler [5] Ричарда Ворбертона, async-profiler [6] Андрея Паньгина и, конечно, моего любимчика — perf [7].
Кстати, много тулов фокусируются на профайлинге процессора, разбираясь в том, какой путь выполнения кода вызывает высокую загрузку ЦП. Это здорово, но часто проблема не в этом; нужны инструменты, которые могут показывать пути выполнения кода, ответственные за распределение памяти (async-profiler теперь может делать и это), ошибки отсутствия страницы, непопадание в кэш, доступы к диску, запросы сети, запросы к базе данных и другие события. Меня в этой области привлекла именно проблема поиска правильных перформанс-тулов для исследования работающей среды.
— Я слышал, что под Java/Linux стек есть куча проблем с достоверностью замеров. Наверняка с этим можно как-то бороться. Как ты это делаешь?
Саша Гольдштейн: Да, это печально. Вот как выглядит текущая ситуация: у вас есть быстрая конвейерная линия с огромным количеством разных частей, которые нужно протестировать, чтобы найти дефекты и понять скорость приложения/сервиса. Вы не можете проверить абсолютно каждую часть, поэтому ваша основная стратегия — это проверять 1 часть в секунду и смотреть, всё ли в порядке, и вам нужно это делать через «крохотное окно» над этой «лентой», потому что подходить ближе уже опасно. Вроде неплохо, не так ли? Но потом оказывается, что когда вы пытаетесь в него посмотреть, оно показывает вам не то, что происходит на конвейере прямо сейчас; оно ждёт, пока конвейер перейдёт в волшебный «безопасный» режим, и только после этого даёт вам всё увидеть. Также оказывается, что многих частей вы не увидите никогда, потому что конвейер не может войти в свой «безопасный» режим, пока они рядом; и ещё оказывается, что процесс поиска дефекта в окошке занимает целых 5 секунд, так что делать это каждую секунду невозможно.
Примерно в таком состоянии сейчас находится множество профайлеров в мире JVM. YourKit, jstack, JProfiler, VisualVM — у всех у них одинаковый подход к профайлингу ЦП: они используют семплинг потоков в безопасном состоянии. Это значит, что они используют документированный API, чтобы приостановить все JVM-треды и взять их стек-трейсы, которые затем собирают для отчёта с самыми горячими методами и стеками.
Проблема такой приостановки процесса заключается в следующем: треды не останавливаются сразу же, рантайм ждёт, пока они не дойдут до безопасного состояния, которое может находиться после множества инструкций и даже методов. В результате вы получаете необъективную картину работы приложения, а разные профайлеры могут ещё и не соглашаться друг с другом!
Есть исследование, показывающее, насколько это плохо, когда у каждого профайлера своя точка зрения по поводу самого горячего метода в одной и той же рабочей нагрузке (Миткович и соавт., «Evaluating the Accuracy of Java Profilers»). Более того, если у вас есть 1000 тредов в сложном стеке вызовов Spring, часто собирать стек-трейсы не получится. Возможно, не чаще 10 раз в секунду. В результате ваши данные по стекам будут отличаться от фактической рабочей нагрузки ещё больше!
Решать такие проблемы непросто, но вкладываться стоит: некоторые рабочие нагрузки на проде невозможно профилировать, используя «традиционные» тулы вроде перечисленных выше.
Существует два раздельных подхода и один гибридный:
Любая из этих опций гораздо лучше, чем safepoint-biased профайлеры с точки зрения точности, ресурсопотребления и частоты отчётов. Они могут быть грубоватыми, но, думаю, точный профайлинг продакшна с низким оверхедом потребует ещё больших усилий.
— К слову об окружениях, сейчас модно всё паковать в контейнеры, здесь есть какие-то особенности? О чём следует помнить, работая с контейнеризованными приложениями?
Саша Гольдштейн: С контейнерами возникают интересные проблемы, которые многие тулы полностью игнорируют и в результате вообще перестают работать.
Вкратце напомню, что контейнеры Linux построены вокруг двух ключевых технологий: группы контроля и пространства имён. Группы контроля позволяют увеличить квоту ресурсов для процесса или для группы процессов: CPU time caps, лимит памяти, IOPS хранилища и так далее. Пространства имён делают возможной изоляцию контейнера: mount namespace предоставляет каждому контейнеру свою собственную точку монтирования (фактически, отдельную файловую систему), PID namespace — собственные идентификаторы процессов, network namespace даёт каждому контейнеру собственный сетевой интерфейс и так далее. Из-за пространства имён множеству тулов сложно правильно обмениваться данными с контейнезированными JVM-приложениями (хотя некоторые из этих проблем свойственны не только JVM).
Прежде чем обсудить конкретные вопросы, будет лучше, если мы вкратце расскажем о разных видах observability тулов для JVM. Если вы не слышали о некоторых из них, самое время освежить ваши знания:
Вот некоторые проблемы, возникающие при профайлинге и мониторинге контейнеров из хоста, и способы их решения (в будущем постараюсь рассказать об этом подробнее):
Тут можно подумать: а если мне просто положить перформанс-тулы в контейнер, чтобы все эти проблемы с изоляцией возникали не из-за меня? Хоть идея неплохая, множество тулов не будут работать и с такой конфигурацией из-за seccomp. Docker, к примеру, отклоняет системный вызов perf_event_open, необходимый для профайлинга с perf и async-profiler; он также отклоняет системный вызов ptrace, который используется большим количеством тулов для чтения объёма памяти JVM-процесса. Изменение политики seccomp для принятия этих системных вызовов ставит хост под угрозу. Также, помещая тулы профайлинга в контейнер, вы увеличиваете его поверхность атаки.
Совсем скоро Саша приедет в Санкт-Петербург, чтобы провести тренинг по профилированию JVM-приложений в продакшне [17] и выступить на конференции Joker 2017 [18] с докладом про BPF [19], поэтому, если вы хотите погрузиться в тему глубже – у вас есть все шансы встретиться с Сашей лично.
Автор: ValeriaKhokha
Источник [20]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/264585
Ссылки в тексте:
[1] goldshtn: https://habrahabr.ru/users/goldshtn/
[2] интервью: https://habrahabr.ru/company/jugru/blog/320620/
[3] «Systems Performance»: http://www.brendangregg.com/sysperfbook.html
[4] «Site Reliability Engineering»: https://landing.google.com/sre/book.html
[5] Honest Profiler: https://github.com/jvm-profiling-tools/honest-profiler
[6] async-profiler: https://github.com/jvm-profiling-tools/async-profiler
[7] perf: https://perf.wiki.kernel.org/index.php/Main_Page
[8] jps: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jps.html
[9] jinfo: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jinfo.html
[10] jstack: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jstack.html
[11] jmap: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jmap.html
[12] jcmd: https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jcmd.html
[13] jstat: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html
[14] Serviceability Agent: http://openjdk.java.net/groups/hotspot/docs/Serviceability.html
[15] JMX: http://www.oracle.com/technetwork/articles/java/javamanagement-140525.html
[16] JVMTI: https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html
[17] тренинг по профилированию JVM-приложений в продакшне: https://jokerconf.com/2017/trainings/68vqefxcugkwc0wsk6gic/
[18] Joker 2017: https://jokerconf.com
[19] докладом про BPF: https://jokerconf.com/2017/talks/4xupwfvjh6w8oksi4gu0i2/
[20] Источник: https://habrahabr.ru/post/338928/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.