Вячеслав Смирнов. Профилирование JVM в Kubernetes

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

Вячеславу повезло использовать разные инструменты. И повезло найти разные дефекты микросервисов на основе JVM. Спикер проанализировал, разделил инструменты и дефекты микросервисов на группы, и хочет рассказать о:


  • подборе профайлеров и их настройках под задачу;
  • собранных рецептах профилирования JVM в Kubernetes;
  • моментах, когда профилирование вредит, а когда помогает.

Видео:


  • (Сергей Бойцов) Я бы хотел начать с легкого, разгоняющего вопроса. Микросервисы и Kubernetes — это наша новая реальность. Вы согласны с этим?


  • (Вячеслав Смирнов) Скорее всего, да. Микросервисы есть сейчас почти во всех проектах так или иначе. Их выделают, даже если это большие сервисы, т. е. макросервисы, упакованные в контейнеры и раскатанные с помощью Kubernetes, чтобы они лучше масштабировались. Все равно их называют микросервисами. Так что эта новая реальность.


  • (Владимир Ситников) Я вижу, что люди движутся и в одну, и другую сторону. Кто-то движется в сторону разделения, кто-то уже попробовал и движется в противоположную сторону, поэтому предсказывать, что будет через 5–10 лет я бы не стал. Я бы Вячеславу задал бы следующий вопрос. Когда был один монолит, то его как-то разработали, потом администраторы устанавливали и единороги-нагрузочники тестировали под нагрузкой. Когда делят на микросервисы, команды говорят, что мы сами с усами, все сами сделаем, все сами протестируем и не приставайте к нам. Слава, когда возникает Kubernetes, там нагрузочники вымирают или нет?


  • (Вячеслав Смирнов) Не совсем. Есть множество технологий, которые обещают, что тестирование производительности не понадобится, но тестирование производительности нужно и профилирование в том числе, как некий такой следующий шаг после того, как вы получили результаты и они вам не понравились. Этот этап все равно остается. И о нем я как раз хочу рассказать.


  • (Сергей Бойцов) Здорово! Давайте тогда послушаем. Очень интересно. Задавайте вопросы в Телеграм-чат, чтобы что-то узнать, чему-то научиться. Потому что самое плохое — это незаданный вопрос.


  • (Вячеслав Смирнов) Начнем.


hnkoy8r68jeazhjtozsvwrxx5me.jpeg

Я вам расскажу про профилирование JVM в Kubernetes. Я назвал этот доклад «Три больших шага». Меня зовут Вячеслав и я из ВТБ.

noyo14dncvmsz8etcbvlykvddqc.jpeg

В ВТБ занимаюсь тем, что исследую и создаю результаты нагрузки. Проект у меня называется ДБО. Если вам интересно, то зайдите на сайт vtbbo.ru.

Кроме работы занимаюсь развитием чата @qa_load, где помогаю инженерам. Помогаю я не один. Там мы делимся опытом. Отвечаем на вопросы тех, кто впервые начал заниматься тестированием производительности. У них вопросов много. Мы общаемся достаточно дружелюбно.

pacellg1cksokuumrupaltjwpmy.jpeg

Проект, который мы нагружаем, можно описать в двух словах. Это 100 Java Virtual Machines, работающие друг с другом и с базой данной. Это так на тестовом стенде.

На продуктивном стенде он гораздо мощнее, он геораспределенный. Там более 200 микросервисов. С учетом геораспределения можно сказать, что даже более 400, но они так или иначе совпадают с тем, что есть на тестовом стенде.

Доклад посвящен как раз профилированию на тестовом стенде, где вам можно в любой момент перезапустить поду, поменять какие-то настройки, поэкспериментировать, поэтому он пригодится в таком случае.

-8eute6bkrpgmrwly6qrungumwu.jpeg

Расскажу про особенности профилирования JVM в Kubernetes. Что же именно добавил Kubernetes?

fyvwqnlxidncyzyl5yf29pkh9zo.jpeg

Kubernetes добавил выделение ресурсов для нужд профилирования: что есть различные requests, есть различные лимиты и об этом надо помнить.

aqcmhi1x4ii3mja-gzsilztu8u0.jpeg

Расскажу о том, как выполнять анализ результатов, как перейти от анализа потоков к детализации что же в этом потоке выполняется и перейти на код.

at_dqghy94xma1hxtqnvrucxpdm.jpeg

Тут будет интересный момент, как оценить взаимодействие микросервисов. Например, есть медленно работающий запрос. Он работает медленно, потому что мы ждем ответа от другого сервиса.

bw9zv7quv5invqoahxjiwnxc5t0.jpeg

И расскажу, как стандартизировать процесс профилирования в большой команде. У нас в команде 6 человек. Ее уже можно назвать большой командой. Я не везде такие команды видел. Тут уже требуется масштабирование.

n6yb0i_cemtrulu9t-swaq7l7z0.jpeg

И следующий этап — это обмен знаниями, т. е. как масштабировать, как обменяться знаниями, как стандартизировать все, чтобы процесс мог выполнять любой человек на любом количестве контуров, пусть у вас даже 3 Kubernetes-кластеров.

jqfxlgdqjt-g6lu0tltbx1hshpw.jpeg

Доклад можно представить, как сложный пазл. И мы по кусочкам будем его собирать. Расскажу про мелкие детали и про крупные блоки.

rgdlwydjpktsp3qdjoe7zb2rob4.jpeg

Начнем с первого этапа. Какие есть в Kubernetes особенности при профилировании? Что он добавляет сам Kubernetes в весь процесс?

ueucvr86lmuossfg2c3zqfzdzry.jpeg

Раньше мы проводили два вида тестирования: нагрузочный и стабильности, т. е. находили максимум во время нагрузки. И во время стабильности подтверждаем, что в найденном максимуме 8–10 часов держится система и не падает.

А теперь, как минимум, нужно еще тестирование масштабируемости.

1f6lqbeawdsvhb31ql_xujtfioo.jpeg

После того, как вы провели 3 вида тестирования, во всем этом есть еще одна особенность. Заказчику интересно уже не только число, что мы достигаем такой-то производительности. Важен и второй фактор. Заказчик спрашивает: «Сколько реплик сервису нужно? Какие настройки нужны? Сколько ресурсов нам нужно на кластер, чтобы все это работало достаточно производительно». Т. е. появляются дополнительные вопросы.

rssggk75w04ddavxozg43awhzie.jpeg

Самый легкий — это вопрос настроек, т. е. мы ничего не масштабируем, а меняем настройки. Поменяли некоторые конфигурации и все стало быстрее.

emtxz2pxcgyzmabgmqv7h804zpw.jpeg

Если мы настройками справиться не можем и натыкаемся на то, что на тестовом стенде закончились ресурсы, то появляется интересная задача — ускорение всей этой системы.

Мне очень повезло. Я попал в команду ВТБ, когда как раз этих ресурсов не хватало, и команда занималась ускорением. Поэтому я набрался опыта по ускорению Kubernetes-кластера. И первое время занимался не столько рутинной работой по запуску тестов, сколько профилированием. И могу поделиться этим.

Когда вы начнете ускорение, сначала нужно посмотреть на результаты мониторинга, на логи. И, предположим, вы по результату мониторинга, по логам, по высокоуровневым метрикам не смогли найти способ оптимизировать систему. Условно, есть у нас кластер, 10 серверов. В него влезает 100 сервисов. Зафиксировали, промониторили и так ничего не смогли найти.

cicwr8ggfvoj9d1htdfhbksfrw8.jpeg

Тогда уже можно подключить профайлер. Можно не сильно масштабировать. И можно посмотреть изнутри JVM с помощью профайлера изменение каких настроек нам сможет помочь все это ускорить и сократить ресурсы.

-566vo__nnajd120lysm07mzn90.jpeg

Я несколько раз сказал уже о настройках. В Kubernetes принято увеличивать производительность системы очень просто, т. е. увеличивать количество реплик. Считается, что 2 реплики работают быстрее, чем одна. Но на самом деле нет.

Есть способ не увеличивать количество реплик, а увеличивать количество потоков внутри каждой реплики. Если мы говорим про JVM, то тут, как правило не однопоточное приложение, а многопоточное.

pzxyjoozcc7iqxdc7ilye3orus0.jpeg

Я написал несложный алгоритм, чтобы понять нужно ли нам сейчас реплики увеличивать или нет.

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

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

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

И когда мы уже уперлись в лимит нашей поды (можем создавать там 2–3–4 поды), то мы знаем, что для 50 потоков нужна такая конфигурация.

ebailuzkmgbw455pa4avsqrti3u.jpeg

При увеличении количества реплик аналогичная ситуация. Если мы изменяем количество реплик, и становится быстрее, и при этом у нас еще есть свободные ресурсы, то мы можем этот процесс наращивать, т. е. проводить тестирование масштабируемости.

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

69ckpkrzaa1bilzcohazqfzeeho.jpeg

Какие это могут быть инструменты? Это тоже хороший вопрос. В Kubernetes не все так однозначно. Иногда Kubernetes JVM профайлер не нужен.

ltmawxervogkegzhaz-guekjcms.jpeg


  1. Есть цикл тестирования, где мы сначала проводим регрессионный тест, т. е. просто сравниваем. Например, была версия 1, у нее производительность стала версия 2. И ее метрики в JMeter, Gatling сравниваем. И можем определить узкое место, если видим деградацию по той дельте изменений, которая была между версией 1 и версией 2. И даже можем определить место, которое сильнее всего тормозит.
  2. Если нужно копнуть глубже, то нужно посмотреть на статистику балансировщика нагрузки, детализацию по логам ошибок, если вдруг в новой версии появились ошибки.
  3. Можем посмотреть на бизнес-метрики производительности. Например, посмотреть в Яндекс.Метрике, Google Analytics, как там пользователь на prod«е чувствует себя. В нашем проекте есть встроенные метрики внутренней производительности.
  4. Кроме этих метрик, как правило, во всех Kubernetes-инсталляциях есть такие сервисы, как Zipkin, Jaeger, которые собирают статистику за счет трассировки запросов при передачи их от микросервиса к сервису. Это более детальная статистика, чем даже в самом JMeter.
  5. Кроме того, не стоит забывать про мониторинг системных метрик.
  6. Мониторинг более внутренних метрик, например, внутри JML, SQL
  7. И если после всего этого объема информации вам недостаточно, то тогда 7-ым этапом можно запрофилировать JVM.
  8. Профилировщик сможет найти не все. Иногда в моей практике пригождаются утилиты strace, perf и другие, которые позволяют найти то, что изнутри JVM не видно.

utypsyb5bqbndmilhkg7_yi-xnw.jpeg

Если это визуализировать как-то в картинку, то визуализацию я смогу для вас изобразить вот такую.

Вот у вас есть браузер, т. е. клиент. Этот браузер обращается к вашему сервису. Он представляет из себя 4 сервиса переднего уровня и там есть 100 микросервисов на бэке.

И нам нужно определить, какой микросервис самый медленный.

menleca841tcol15pzhtkoptijm.jpeg

Чтобы это определить, нам не обязательно использовать профилировщик. Нам хватит отчета по нагрузке. Т. е. такая статистика будет в JMeter, Gatling. А также бизнес-метрики или даже статистики по логам. В том же nginx можно настроить детальное логирование.

8b2rquowmcgk7zjs3f7ctqqip3g.jpeg

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

d00tfocuhv-mppwu4m0ry-ongtq.jpeg

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

z7r26b02hn-uawasgkfbssbsope.jpeg

Если бы у нас был монолит, мы бы тыкали в какую-нибудь url монолита и желали узнать, что внутри приложения тормозит, т. е. какой модуль, компонент, то, скорее всего, мы бы взяли профайлер.

fzcd8g9zai1ux-dch6rtt2wz5ms.jpeg

Мы бы профилировали этот монолит и сказали, что тормозит.

adb4o1q66jgx0afilusmault95w.jpeg

А с микросервисами не так все просто. Нет единого исполняемого файла. У нас целая россыпь микросервисов. Они общаются друг с другом. Каждый из них может самостоятельно ходить в базу и другие системы.

rlfjmvtlu7czo866k0reyekmpxq.jpeg

И тут очень полезными являются инструменты Zipkin и Jaeger, которые трассируют запросы между сервисами и предоставляют полезную статистику.

О чем я? О том, что профайлер нужен не всегда. Иногда могут пригодиться и другие инструменты, чтобы закрыть все ваши нужды.

r2zr_qyrsnbxforlbmsf6uvf09q.jpeg

Чуть ранее я упоминал о особенности Kubernetes, что при начальном масштабировании мы можем увеличивать количество потоков.

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

jxww4at_gvfph3dyxu5nvheoqum.jpeg

Самый частый способ профилирования, который использую я, это вычисление процента активности того или иного метода. Делается это, как правило семплированием.

В JFR, SJK, AsyncProfiler, JVisualVM и т. д. есть режим семплирования.

ezeobiclkpy1rtwsw7plzhmmfai.jpeg

Если вы не знаете, что это такое, то вы наверняка знаете, что такое Stack Trace. Видели это в логах или в различных инструментах. Это последовательность вызовов Java-методов.

xggsccv90e3ydb1_-wfsq0u1wfs.jpeg

И если накопить статистику по этим Stack Trace, то можно что-то понять. Например, вот у нас 4 раза метод putVal, 8 раз метод sample в строчке 613.

7sxma5tbbst7xvkh7z6ohyjctro.jpeg

Я накапливаю, накапливаю и получаю статистику, какие методы у меня более часто встречаются во время работы потока, какие менее часто.

hxeqiyh3u2li6ztyk9_seazgvyi.jpeg

И как на сбор этой статистики, т. е. на процесс семплирования, влияет количество потоков? Чтобы это определить, я собрал небольшой тестовый стенд. У меня был один сервис, принимающий запросы по http. В нем можно было менять количество потоков от 200 (по умолчанию) до 5. И использовать JMeter, который подавал нагрузку всего лишь тремя потоками. Т. е. чтобы со всеми этими запросами справится, сервису достаточно тоже всего лишь 3 потока у себя. Все остальные спали и ждали, когда к ним перепадет запрос и перепрофилироваться с помощью SJK.

0tfgbg5j69e6hkcsmwxofzb6m9k.jpeg

Какие результаты я получил? Получил такие результаты, что запросы в среднем выполнялись 100 миллисекунд. Самые тяжелые методы — 10 миллисекунд.

dwmyvz-0afchhl4zuousixldfh8.jpeg

И когда у меня было 120 лишних потоков, то SJK, профилируя все активные потоки, в том числе 120 спящих, собирал метрики с интенсивностью 90 раз в секунду. Получается, что в среднем каждые 11 миллисекунд он лишь собирал stack trace.

При такой интенсивности он мог легко пропустить Java-методы, которые работают даже в среднем быстрее.

zt9jdolj-2ob45jrzlxilqpjjtg.jpeg

Конечно, он ловит все http-запросы, т. е. кто их отправляет и как долго ждет, а Java-методы может легко пропустить.

v_crhqlaf2mkeqp3t4nfiydzxd8.jpeg

Если снизить количество потоков до 20, оставить только 20 спящих, то частота семплирования гораздо выше становилась. И уже интервал семплирования снижался до 3 миллисекунд. Это значит, что туда попадали даже те методы Java, которые быстрее, чем средние.

kozbz2nh_164ot54ppj1vzjgd5i.jpeg

Результаты можно представить на таком графике.

6abyturr-ucm3avblx9j2gjybog.jpeg

Я провел серию замеров.

ecdprxigdzbocka_ie5edcfwsdi.jpeg

Если снизить количество потоков со 120 до 20, то у нас интенсивность семплирования повышается в 3 раза.

hlchhadip95mvx6lde19gvo7c5s.jpeg

А по умолчанию tomcat.mat-threads — это 200 потоков.

Если постепенно снижаться с 200 до 5, то интенсивность повышается в 5 раз. Это о том, что не только увеличение количества потоков полезно. Для задач профилирования снижение — это тоже интересный момент, хоть и неочевидный.

dk3i9guxkkh1cde_zjbkdrbk7vu.jpeg

Какая получалась частота семплирования в SJK? Я измерял с помощью команды ssa, которая есть в SJK. Это Stack Analyzer. Выводил колонку интенсивности, которая получилась. И смотрел, что по интенсивности получается. В данном случае почти 60 раз в секунду.

5dijixusurfrqnkpk--rr4zv40w.jpeg

Следующей особенностью в Kubernetes является влияние CPU Limit на профилирование и на сам процесс профилирования.

d-tltwba3ewxip9b5ma82iog7wk.jpeg

Я использовал тот же самый стенд, но в нем ограничил возможности использования процессора данным сервисом до 1 ядра.

xenyfuuixezdfu-ijy16tivghyi.jpeg

Получились следующие результаты. Когда я не ограничивал, то я собирал интенсивность метрики 90 раз в секунду, а как ограничил, и весь сервис замедлился, то интенсивность профилирования снизилась. Стало всего лишь 7 раз в секунду. И с такой интенсивностью я не поймаю различные супербыстрые метрики. Т. е. при неправильной настроенном лимите и запуске профилирования можно хорошие метрики не собрать.

nr4k0gjy_cm5abykzmeh5cx1x94.jpeg

Примеры показали, что SJK на большом количестве потоков затрачивает где-то 0,36 ядра. Об этом надо помнить.

ac-abwzfp_kwlr-myugumevx6-k.jpeg

И если вы начали профилирование в Kubernetes, то чуть-чуть добавьте для накладных расходов самого профайлера CPU, т. е. поднимите лимит.

jejpvpqi6ba_kh5cktlhoqlk2nw.jpeg

Если вы не можете поднять, то можете просто снизить интенсивность семплирования. Это настраивается во всех профайлерах. В JVisualVM, например, интенсивность семплирования по умолчанию 100 миллисекунд. И такую же интенсивность можно задать во всех других профайлерах. Но в этом случае вам нужно будет профилировать чуть подольше. Не 10–30 секунд, а, например, 10 минут профилировать.

bz3d_xjbzgvmq5b1hdvjrcxadxm.jpeg

Кроме CPU Limit в Docker и Kubernetes есть Memory Limit. Он также влияет на профайлер, но уже на другие.

urzjprtm956dvkebbpdqmixc4da.jpeg

Здесь несколько иная задача профилирования. Задача посчитать не просто процент активности, а посчитать точную длительность и количество выполнений каждого метода. И это позволяют уже выполнить инструментирующие профайлеры.

Среди тех, которые я успешно пробовал, могу назвать JVisualVM. В режиме Startup Profiler его можно подключить и запрофилировать в таком инструментирующем виде. А также JProfiler и YourKit Java Profiler.

iwn8gzq1_-zikndj9kslg1zc7g8.jpeg

Что такое инструментация? Если ее описывать в виде схемы, то это непростой процесс при работе с JVM профайлером. Например, для C проектов инструментация есть на уровне компилятора. В JCC вы можете заинструментировать компилируемый код и получить там какие-то метрики. И все эти метрики будут на уровне исходников туда вшиты, и выполняться до запуска.

А при JVM инструментации более популярен подход на лету с помощью JVM-агентов, когда меняется JVM Byte-код. Это уникальная фишка JVM.

br_zqvbb1zfcxqojjp-glpnvwqo.jpeg

Когда ваше приложение запустилось, в нем уже 1 000 классов, различные объекты, и вы начинаете каждый класс перелопачивать, т. е. до запуска каждого метода вставлять некий быстрый, но все же код, то это все потребует дополнительных расходов памяти.

fkd5v53vfw535q-s6kcanqbhipw.jpeg

И сам процесс инструментации потребует дополнительных расходов процессора.

Мы сталкивались в команде с такими эффектами, что приложение падало из-за недостатка ресурсов памяти, если сделали вот такое инструментирующие профилирование.

ezsdqu_gsg5t2tv7rguza6tnx7y.jpeg

Тут общая рекомендация, что если вы запускаете еще инструментирующее профилирование с помощью JVM-агента, то добавьте себе в поду немножко памяти тоже.

Если вы явно не влияете в вашей поде на размер heap size, т. е. Xmx, а влияете только на лимит. Например, Xmx вычисляется с помощью MAX_MEM_RATIO, есть такая опция в некоторых контейнерах. И чтобы добавить 1 гигабайт, вам нужно лимит поднять на + 2 гигабайта.

mfmhagbahjpvucxqhhuhmdp3mom.jpeg

Так или иначе помните, что нужно добавлять ресурсы, если вы делаете тяжелое профилирование.

xgdwgwqbitjwambd7m5xey00nhw.jpeg

Почему это важно? Что это за такие ресурсы? Можно описать это двумя словами.

vpbbsfxd4iimvon-fumqlrdbpe0.jpeg

Вот здесь request, т. е. внутренняя граница контейнера. И есть лимит — внешняя граница контейнера.

Для контейнера можно задавать только request — это первая картинка. И делать его изначально маленьким.

Можно задавать маленький request и достаточно крупный лимит — это вторая картинка.

Некоторые выбирают стратегию, когда request практически равен лимиту и он изначально большой.

Некоторые выбирают, когда request огромный, а лимит не задан. Это для тяжелых гибких сервисов.

oc7ktyjyeruv_87sntqhaurbfag.jpeg

А рекомендация — накинуть 1 гигабайт и 1 гигабайт CPU.

oboknkoqudqd65v8ct-ra6gajke.jpeg

Что будет, если мы не накинем и у нас есть вот такая инсталляция, где есть профилируемые сервисы и в двух из них лимиты не заданы?

el7eog7bhmsoo6uqfpfae85uvf0.jpeg

Сначала они будут маленькими. Под нагрузкой, конечно, сервисы начнут потреблять больше ресурсов.

suwmcynefsoiton-igolgkiwsqk.jpeg

И вот они все выросли.

julsb7wg_ofa81vx_9ayoi9ujjg.jpeg

И первому центральному сервису стало не хватать ресурсов.

Если ему не хватает CPU, то ничего страшного не случится. Как я вам рассказал, интенсивность семплирования снизится, т. е. она застынет на какой-то планке. Возможно, это будет даже 7 sample в секунду, но ничего не случится.

А если так случилось, что в приложение выделено heap в 2 гигабайта, а у нас физически на кластере остался один гигабайт памяти, то JVM будет думать, что ей можно выделить столько памяти, а физически столько памяти нет. И контейнер, т. е. приложение упадет по out of memory error. В этом случае весь контейнер схлопнется и перезапустится. Там начнется все с начала.

xvcw5un_udsqedu0kc3gu0p5quq.jpeg

Но процесс профилирования прекратиться, т. е. что-то разломается, сессию с профайлером мы потеряем.

jfpyes6uch7_fhwdjkf6s84rbiw.jpeg

Второй случай, если у нас лимит задан, но не у всех. Сначала тоже будет все хорошо.

bljn9lh1kxy3d3zuvhueg8febwm.jpeg

Мы начнем подавать нагрузку.

yjdx2sbj7xwq3s6vitbc4l8m3ji.jpeg

Контейнеры начнут потреблять больше ресурсов.

vwrgz-ohzmw4plbkxmhdpzuqdpq.jpeg

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

Тут важный момент проявляется в особенностях самого Kubernetes.

wgnhk2qrqb4gb4a26lwnuejxfw8.jpeg

Если ресурсы можно выделить Kubernetes«у за счет того, чтобы погасить какие-либо сервисы, у которых ниже приоритет, чем у текущего, а если у нас лимит задан и приоритет выше, то Kubernetes так и поступит. Он возьмет другую поду, у которой лимит не задан и погасит ее, перезапустит ее.

gdtbsu9k3ufavyzkeb3cdxjmrsk.jpeg

В этом случае данная пода не сломается, она получит свои ресурсы, но, возможно, сломается что-то еще. Сервис контроля доступа и профилирование или весь процесс все равно разрушатся, потому что что-то пошло не так.

В случае, если у нас всех сервисов по одной штуке, тогда вообще бизнес-процесс остановится.

j7ijfurzgva2juo-vtwf8wdiatc.jpeg

И самый желательный вариант, когда везде лимиты заданы.

xka-e1xa1glk-yh7vymv9y1xmi0.jpeg

Мы начинаем нагрузку. Сервисы были маленькие.

mwyixvzc7ouauiyu-v3fsv5j5ve.jpeg

Вот они стали побольше.

bky83mem98kkdzv7zvwgl_huhww.jpeg

И вот им ресурсов не хватило.

Какой тут вариант? Мы знаем, что этому сервису нужно столько-то ресурсов.

esvyngwjwqij_fua2wtdkzehvjy.jpeg

И при профилировании накидываем чуть-чуть.

ciqltefefdlfb0fbqx8si-oaiu4.jpeg

Можно 0,3–0,4, можно +1. И все будет работать, как будто бы и не было никакого профилирования. Это такая особенность Kubernetes.

dgnplveff9n9r9hkxgpe3dqpbxs.jpeg

Какие есть особенности у самих профайлеров при работе с распределенными системами?

jyccef_fvympzg2kfup9r2ohp7k.jpeg

Можно все профайлеры и способы их подключения разделить на 2 больших типа:


  • Мы подключаемся удаленно, т. е. запускаем профайлер у себя на рабочей станции и подключаемся к поде или сервису;
  • Мы подключаемся локально.

0wmshbf1acm3uidnlupe6gc0tcg.jpeg

Самый привычный, когда вы запускаете профайлер с графической оболочкой у себя на рабочей станции. Это удаленный вариант.

В чем его особенности? Его особенности в том, что вам нужно пробросить сетевое подключение от вашей рабочей станции до профайлера, т. е. есть несколько фишек самого Kubernetes.

-59yg-kunlq-x0fudevwcprhn84.jpeg


  • Предположим, вам нужен доступ до поды лишь на время. Тогда воспользуйтесь kubectl и ее опцией PortForward с пробросом локального порта в поду.
    • Если вам нужен постоянный доступ, то проще зафиксировать выставление порта для профилирования из пода наружу, например, задав эти настройки уже в сервисе.
  • Очень удобно делать, если ваш сервис всегда заскейлен в 1. В этом случае вы всегда, начиная профилирование, будете попадать в профилируемую вами поду.
    • Это можно сделать через NodePort.
    • Можно сделать через LoadBalancer.
  • А если вы заскейлили сервис в две реплики и хотите сделать тоже самое через внешний порт, то, скорее всего, не получится.
    • Потому что ваш профайлер будет обращаться за результатами профилирования в поду. И будет попадать то на первую, то на вторую. И профилирование не получится. Или получится каким-то неправильным. Это единственное такое ограничение.

zujwl_cdori5hc-glk_viw41yfu.jpeg

Если вы выбрали такой способ, то две техники: с помощью Java-агента и с помощью технологии JSR 160 Standard, где вам нужно открыть два порта: JMX и RMI и пробросить их себе.

ybbvxexf5ssclntovjxgb49vrvi.jpeg

Расскажу сначала про этот вариант.

ad-0dq51cb2aap0zrpqkhzc-m0u.jpeg

Чтобы открыть эти порты JMX, RMI, вам понадобится в опции JVM запускаемого сервиса внутри докера добавить вот такой перечень опций. Как правило, я задаю такие опции в deployment-файле.

Вот вы открываете порт 9010. Можно и для JMX, и для RMI задать одинаковый порт. В данном случае это не критично. И с помощью kubectl или с помощью другого инструмента взаимодействия с Kubernetes CPI пробрасываете свой порт внутрь поды.

dflsg86nylelxahea1yz0uz9dn4.jpeg

Если вам нужно запрофилировать и пробросить порты и для сервиса аутентификации и для сервиса генерации отчетов, и для сервиса генерации отчета, то у вас получится такое сделать, если вы во всех подах задали одинаковый порт. Потому что, когда вы будете открывать соответствующий порт у себя на станции, kubectl скажет: «Я не могу дважды открыть порт 9010, он уже кем-то занят».

b7uspafftsfom6dramgi53vqd_u.jpeg

В этом случае вам в другом сервисе нужно отсылать в другой порт. Например, 9011, т. е. какой-то иной. Помните об этом. И нужный порт к нужному подсоединить.

7fxwdox6iwnqbwybojbt-hj0qri.jpeg

Если вы решили это делать не с помощью таких постоянных действий, как пробрасывание порта, можете зафиксировать порт через NodePort в разделе Service. В этом случае вам в опции JVM нужно прописать именно тот порт, который вы открываете в сервисе.

И тут последовательность несколько иная. Вы сначала в разделе Service говорите: «Я хочу, чтобы из данного сервиса наружу торчал порт 31111». Если такой порт не занят, то он открывается. Если вы убедились, что он открылся, он зафиксирован и вашему сервису принадлежит, то потом идете в настройки сервиса в раздел Deployment и там соответствующий порт задаете, т. е. в обратном порядке.

le-fxvieyupxz8avmd4xyuhbdua.jpeg

Если вы никогда работали с Kubernetes-дашбордом, то это Deployments и Service. Т. е. сначала во внешний раздел идете, там настраиваете, открываете порт. А потом в Deployment его указываете.

vvq429gy94jiieflrn5d58bqc3y.jpeg

Хорошую документацию, где есть инструкции с картинками, что это за порты и как их прокинуть именно из докер-контейнера, описал Алексей Рагозин в блоге компании BellSoft. Это один из разработчиков JVM. Называется «JVM in Linux containers, surviving the isolation». Всем рекомендую прочитать эту статью, если вы пользуетесь таким способом. Она доступна по ссылке, данная ссылочка кликабельная.

zvjcnjprxdrxqzi4xr8cubihyvw.jpeg

По такой технологии вы можете подключить различные инструменты: SJK, JMC, VisualVM.

jsfupouzcard_pajcjjsumtmque.jpeg

Один из самых простых инструментов — это SJK.

qe806x79_tdmgzhd4ueyczcyrx0.jpeg

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

jy1sp4mugndp0pbbguyslzr81xw.jpeg

Второй по простоте — это, наверное, JMC и технология Java Flight Recorder.

mysa2depg6p_cuv7uhldaiqnx5a.jpeg

С ней есть несколько особенностей. Она заключается в том, что технология JFR появилась в OpenJDK в 8-ой не сразу, а только с версией 272.

И если у вас версия 272, то открывайте JMX, RMI также, как для предыдущих инструментов и пользуетесь.

Если у вас старый 8-й OpenJDK, то у вас просто ничего не получится. Вам нужно или монтировать Java в контейнер, или не пользоваться этим способом.

Если у вас Oracle JDK, то там есть еще более тонкий момент. До build 161, даже до build 162 лицензия на Oracle JDK позволяла использовать JFR на тестовых стендах при включении опции UnlockCommercialFeatures и опции FLightRecorder.

Если у вас более старшая версия, то вам нужно будет купить лицензию. Она исчисляется стоимостью по ядрам. Сам я не использовал такой способ после смены лицензии.

j50utzpuq7qyw_ujwnqzqaiwdjm.jpeg

Если вы все настроили, то дальше в Java Mission Control создаете подключение.

8-zcjjmqxj0qy5oy2mft9ciase8.jpeg

Указываете порт, который вы открыли. Например, 9010.

k68fnymw8vhevaq5e0mzyt1h9f4.jpeg

Открываете JMX Console с помощью Java Mission Control.

6tpoaad8yroroy8bludua3vcbh4.jpeg

П

© Habrahabr.ru