Как стать автором
Обновить

Особенности сбора метрик. Запуск приложения gunicorn-ом в режиме мультипроцессинга

Уровень сложностиСредний
Время на прочтение20 мин
Количество просмотров5.2K
Всего голосов 29: ↑29 и ↓0+29
Комментарии12

Комментарии 12

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

Стоит упомянуть, что в случае асинхронного кода в приложениях, прометиус клиент использует под капотом синхронный open и позволяет качественно сломать себе копчик в самых неожиданных местах
https://prometheus-async.readthedocs.io/en/stable/index.html

Все верно. О существовании этой проблемы коротко упомянуто в разделе "Влияние файлов метрик на общую производительность".

К слову, библиотека prometheus-async никак не решает эту проблему.

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

Зависит от того, что понимать под стандартом. Конечно, существуют библиотеки, которые работают асинхронно с файлами. Из популярного на моей памяти aiofiles https://github.com/Tinche/aiofiles. Однако качественную оценку дать не могу, так как не доводилось применять в проектах.

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

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

Думаю, на эту тему будет вторая статья.

Подскажите, а в варианте "каждый воркер на порту" есть возможность на стороне прометеуса агрегировать значения? Какие-то метрики просуммировать, какие-то усреднить? Или это делается руками формулами в прометеусе или графане?

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

В моем представлении если идти по пути экспортирования метрик на разных портах, необходимо, чтобы во всех отдаваемых метриках были соответствующие label-ы, по типу port="8001". И уже в той же графане учитывать факт их наличия и строить соответсвующие запросы к прометеусу.

Да, скорее всего как вы говорите. В лейблах присутствует порт и я сегодня это использовал в графане. Там есть функция группировки по полю имени (не знаю точной графановской терминологии). Но мне нужна логика посложнее (надо вывести топ view по максимальному среднему времени их выполнения), придется читать доки, а не как обычно с наскоку))

А что если просто складывать метрики в условный редис и потом просто отдавать из редиса метрики в exposition формате прометеусу? Тогда редис будет местом синхронизации метрик от всех процессов. Да, получится самописное решение, но вроде бы это сделать проще

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

  1. А зачем в проекте и gunicron, и uvicorn? Я понимаю, что у gunicorn есть возможность запуска async-бэкенда через uvicorn, но всегда считал этот механизм больше для какой-то обратной совместимости.

  2. Не смотрели в сторону UltraDict ? Он под капотом использует multiprocessing.shared_memory, т.е. как раз общую память для разных процессов.

  3. Вообще в 12factor рекомендуется масштабироваться именно контейнерами, а не процессами внутри. Но вы отбросили отказ от мультипроцессинга почти сразу. Можете подсказать на каких объёмах использование ресурсов становится нерациональным? Просто у меня буквально 16 воркеров, и не уверен, что в моём случае стоит усложнять внутреннюю реализацию, а не масштабироваться подами с одним процессом.

В любом случае спасибо за рисёрч - тема очень интересная.

1. У gunicorn-а побольше настроек запуска и побольше возможностей. Например, конфигурация через python файл как в этой статье. Все зависит от потребностей.

2. Не смотрел. Нужно понимать, что воркер гипотетически может упасть, и нужны гарантии того, что ресурс в виде worker_id будет освобожден. Lock-файл с этим отлично справляется. При любом варианте завершения процесса lock будет релизиться. Как добиться таких же гарантий UltraDict-ом мне лично сложно сказать.

3. Это достаточно спорная тема. Если разговаривать в контексте того, что приложение запущено в kubernetes, то нужно помнить про выделенные квоты на cpu. Условно говоря, если поду будет выделено 0.5 cpu, это приведет к троттлингу. Троттлинг приводит к повышению времени обработки запроса. Увеличиваешь cpu, чтобы уменьшить задержки, можешь столкнуться с меньшей утилизаций, чем хотелось бы. И именно в этот момент может появиться желание запускать несколько процессов на одном ядре. Также если следовать рекомендации после некоторого количества запросов перезапускать воркер, то в случае если в поде будет один такой хромой воркер, во время перезапуска воркера этот под будет отдавать 503. В общем, я перечислил лишь часть причин, по которым вам могут понадобиться несколько процессов в поде. А какое решение выберете вы, зависит от ваших потребностей, нагрузки, возможностей и тд. Строго индивидуально. Ну и наконец для запуска небольшого приложения может вообще не понадобиться вся эта история с подами и kubernetes-ами. Кому-то нужно просто запустить приложение на виртуалке с 16-ью ядрами. И тут уж точно без мультипроцессинга не обойтись, иначе зачем столько ядер нужно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий