Celery в нагруженных проектах: немного практики
В преддверии нашей Moscow Python Conf++ мы кратко поговорили с Олегом Чуркиным, техлидом финтех-стартапа, о его обширном опыте работы с Celery: полмиллионе фоновых задачах, багах и тестировании.
— Расскажи немного деталей о проекте, над которым ты сейчас работаешь?
В данный момент я занимаюсь финтех-стартапом Statusmoney, который анализирует пользовательские финансовые данные и позволяет клиентам сравнивать свои доходы и расходы с другими группами людей, выставлять лимиты по тратам, наблюдать, как растет или падает благосостояние на графиках. Пока проект ориентирован только на североамериканский рынок.
Для анализа финансовой информации мы скачиваем и храним все транзакции пользователей и интегрируемся с кредитными бюро, чтобы получить дополнительные данные по кредитной истории.
Сейчас у нас около 200 тыс пользователей и 1,5 терабайта различных финансовых данных от наших поставщиков. Одних транзакций около 100 млн.
— А каков технологический стек?
Стек текущего проекта — это Python 3.6, Django/Celery и Amazon Web Services. Мы активно используем RDS и Aurora для хранения реляционных данных, ElasticCache для кэша и для очередей сообщений, CloudWatch, Prometheus и Grafana — для алертинга и мониторинга. Ну и, конечно, S3 для хранения файлов.
Мы также очень активно используем Celery для различных бизнес-задач: рассылки уведомлений и массовых рассылок писем, массового обновления различных данных из внешних сервисов, асинхронного API и тому подобного.
На фронтенде у нас React, Redux и TypeScript.
— Какой основной характер нагрузок в вашем проекте и как вы с ними
справляетесь?
Основная нагрузка в проекте ложится на фоновые задачи, которые исполняет Celery. Ежедневно мы запускаем около полумиллиона различных задач, например, обновление и процессинг (ETL) финансовых данных пользователей из различных банков, кредитных бюро и инвестиционных институтов. Помимо этого отсылаем много уведомлений и рассчитываем множество параметров для каждого пользователя.
Еще у нас реализован асинхронный API, который «пулит» результаты из внешних источников и также генерирует множество задач.
В данный момент, после тюнинга инфраструктуры и Celery, справляемся без проблем, но раньше бывало всякое, обязательно расскажу об этом в своем докладе.
— Как вы это всё масштабируете и обеспечиваете отказоустойчивость?
Для масштабирования мы используем Auto Scaling Groups — инструментарий, который предоставляет наша облачная платформа AWS. Django и Celery хорошо масштабируются горизонтально, мы только немного настроили лимиты на максимальное количество памяти используемой воркерами uWSGI/Celery.
— А мониторите чем?
Для мониторинга cpu/memory usage и доступности самих систем используем Cloud Watch в AWS, различные метрики из приложения и из Celery-воркеров агрегируем с помощью Prometheus, а строим графики и отправляем алерты в Grafana. Для некоторых данных в Grafana мы используем ELK как источник.
— Ты упоминал асинхронный API. Расскажи чуть подробнее, как он у вас
устроен.
У наших пользователей есть возможность «прилинковать» свой банковский (или любой другой финансовый) аккаунт и предоставить нам доступ ко всем своим транзакциям. Процесс «линковки» и обработки транзакций мы отображаем динамически на сайте, для этого используется обычный пулинг текущих результатов с бекенда, а бекенд забирает данные, запуская ETL pipeline из нескольких повторяющихся задач.
— Celery — продукт с противоречивой репутацией. Как вам с ним живется?
По моим ощущениям, наши отношения с Celery сейчас находятся на стадии «Принятие» — мы разобрались как фреймворк работает внутри, подобрали для себя настройки, разобрались с деплоем, «обложились» мониторингом и написали несколько библиотек для автоматизации рутинных задач. Некоторой функциональности нам не хватило «из коробки», и мы дописали ее самостоятельно. К сожалению, на момент выбора стека технологий для проекта, у Celery было не так много конкурентов, и если бы мы использовали более простые решения, то нам пришлось бы дописать намного больше.
С багами именно в четвертой версии Celery мы ни разу не сталкивались. Большинство проблем было связано либо с нашим непониманием того, как это все работает, либо со сторонними факторами.
О некоторых написанных внутри нашего проекта библиотеках я расскажу в своем выступлении.
— Мой любимый вопрос. Как вы всю эту музыку тестируете?
Задачи Celery хорошо тестируются функциональными тестами. Интеграцию тестируем с помощью автотестов и ручного тестирования на QA-стендах и стейджинге. На данный момент мы еще не решили пару вопросов с тестированием периодических задач: как позволять тестировщикам их запускать и как проверять, что расписание у этих задач корректное (соответствует требованиям)?
— А тесты на фронтенд и вёрстку? Какое вообще соотношение ручного и
автоматизированного тестирования?
На фронте мы используем Jest и пишем только юнит-тесты на бизнес-логику. 55% бизнес-критикал кейсов у нас сейчас покрыты автотестами на Selenium, на данный момент у нас около 600 тестов в TestRail и 3000 тестов на бекенде.
— О чем будет твой доклад на Moscow Python Conf ++ ?
В докладе я подробно расскажу, для каких задач и как можно использовать Celery, и сравню его с существующими конкурентами. Опишу, как можно избежать различных граблей при проектировании сложной системы с большим количеством задач: какие настройки стоит указать сразу, а какие можно оставить на потом, как задеплоить новую версию кода так, чтобы не потерять задачи при переключении трафика, поделюсь написанными библиотеками для мониторинга задач и очередей.
Также затрону тему реализации ETL-пайплайнов на Celery и отвечу, как описать их красиво, какую использовать retry policy, как гранулярно ограничивать количество выполняемых задач в условиях ограниченных ресурсов. Плюс опишу, какие инструменты мы используем для реализации пакетной обработки задач, которая экономно расходует доступную память.
В общем, если вы жаждите деталей по всем вышеобозначенным пунктам, приходите. Надеюсь, мой доклад покажется вам полезным и интересным.