Комментарии 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 формате прометеусу? Тогда редис будет местом синхронизации метрик от всех процессов. Да, получится самописное решение, но вроде бы это сделать проще
А зачем в проекте и gunicron, и uvicorn? Я понимаю, что у gunicorn есть возможность запуска async-бэкенда через uvicorn, но всегда считал этот механизм больше для какой-то обратной совместимости.
Не смотрели в сторону UltraDict ? Он под капотом использует multiprocessing.shared_memory, т.е. как раз общую память для разных процессов.
Вообще в 12factor рекомендуется масштабироваться именно контейнерами, а не процессами внутри. Но вы отбросили отказ от мультипроцессинга почти сразу. Можете подсказать на каких объёмах использование ресурсов становится нерациональным? Просто у меня буквально 16 воркеров, и не уверен, что в моём случае стоит усложнять внутреннюю реализацию, а не масштабироваться подами с одним процессом.
В любом случае спасибо за рисёрч - тема очень интересная.
1. У gunicorn-а побольше настроек запуска и побольше возможностей. Например, конфигурация через python файл как в этой статье. Все зависит от потребностей.
2. Не смотрел. Нужно понимать, что воркер гипотетически может упасть, и нужны гарантии того, что ресурс в виде worker_id будет освобожден. Lock-файл с этим отлично справляется. При любом варианте завершения процесса lock будет релизиться. Как добиться таких же гарантий UltraDict-ом мне лично сложно сказать.
3. Это достаточно спорная тема. Если разговаривать в контексте того, что приложение запущено в kubernetes, то нужно помнить про выделенные квоты на cpu. Условно говоря, если поду будет выделено 0.5 cpu, это приведет к троттлингу. Троттлинг приводит к повышению времени обработки запроса. Увеличиваешь cpu, чтобы уменьшить задержки, можешь столкнуться с меньшей утилизаций, чем хотелось бы. И именно в этот момент может появиться желание запускать несколько процессов на одном ядре. Также если следовать рекомендации после некоторого количества запросов перезапускать воркер, то в случае если в поде будет один такой хромой воркер, во время перезапуска воркера этот под будет отдавать 503. В общем, я перечислил лишь часть причин, по которым вам могут понадобиться несколько процессов в поде. А какое решение выберете вы, зависит от ваших потребностей, нагрузки, возможностей и тд. Строго индивидуально. Ну и наконец для запуска небольшого приложения может вообще не понадобиться вся эта история с подами и kubernetes-ами. Кому-то нужно просто запустить приложение на виртуалке с 16-ью ядрами. И тут уж точно без мультипроцессинга не обойтись, иначе зачем столько ядер нужно.
Особенности сбора метрик. Запуск приложения gunicorn-ом в режиме мультипроцессинга