Все мы привыкли к настройке cron-заданий в терминале:
ssh user@your_server_ip crontab -e Alt + / (пролистать в конец файла в nano) 30 2 * * * /usr/bin/python3 /home/user/my_script.py (добавить еще одно задание) Ctrl + X Y Enter
В какой-то момент заданий становится много, появляются сложные зависимости между скриптами, когда результат выполнение одного важен для начала работы другого скрипта. Балансировать такую историю доводится сис.админу, DevOps'у или разработчику. С этого и начинается наш рассказ.
Привет, Хабр! Меня зовут Степан Чельцов, руковожу компанией «Первый Интернет Проект», в которой занимаюсь внедрением и поддержкой системы Планфикс, а так-же разноплановой web-разработкой. В этой статье проведу вас по истории, как мы сделали свой сервис управления cron-заданиями.
Завязка
На одном из проектов, интернет-магазин с очень большим каталогом (до 1 млн. sku), часть бизнес-процесса торговли составляют самые разные скрипты выгрузки данных на сторонние ресурсы, в том числе маркетплейсы.
Выгрузка подразумевает, что мы формируем тяжелый файл, кладем в папку для робота маркетплейса, который к определенному часу придет за файлом, подтянет в свою базу и обновит карточки товаров, цены, наличие и прочие нюансы.
Если файл старый, отсутствует или битый, он не подтягивается, на стороннем ресурсе остается прежняя версия. А значит, какие-то цены перестают быть актуальными, продаются отсутствующие товары, растет нагрузка на менеджеров, чтобы согласовать косяк с покупателем. А согласование бывает сложным, ведь закон о защите прав потребителей обязывает продать то, что было куплено по заявленной цене, даже если там очевидно ошибка оформления. Надо было что-то менять в работе.
Первые шаги
Сначала мы собрали все задания в список, настроили плановую диаграмму Ганта и проверили проблемы по логам, почему идут проблемы.
В crontab данного проекта накопилось много подобных заданий (52, если быть точным), который были связаны друг с другом логически: выгрузка из одного ресурса была нужна для следующего скрипта. Самые тяжелые из заданий выполняются по 12 часов.
Благодаря росту продаж в целом, и по неправильным ценам и остаткам в частности, а так же регулярных DDOS-атакам, задания не успевали выполняться ко времени — приходилось постоянно сдвигать последующие задания или вручную запускать задания после окончания одного из сложных заданий. А так же, запускать их заново, если задание выпало с ошибкой:
изменения в базе;
изменения в скриптах;
исчерпание ресурсов сервера;
перезагрузки сервера.
Усталость инженера накапливалась, менеджеры недовольны, все вокруг ходят грустные, надо было что-то предпринимать.
Поиск решений
Параллельно начали искать решения, которые работают вокруг cron-заданий на сервере и изучать их возможности. Что нашли:
Cronitor — облачное решение, которое позволяет получать уведомления об успехе и неудаче выполнения задания в разные каналы. Частично помочь мог, но разбираться с проблемами по его уведомлениям всеравно нам. А логов никаких не дает. Отмели.
HealthCkecks — облачное и on-premise решение, похожее на Cronitor, только не из США, а из Латвии.
Cronhub — аналог HealthChecks из Армении, не помог нам продвинуться в решении задачи.
Cronicle — красивая мощная система с локальным развертыванием, у которой оказалась отличная идея запуска задания по вебхуку.
Kubernetes — слишком сложно и мощно для проекта, избыточно. И не только лишь все смогут его настраивать.
Jenkins — так же тяжелый и сложный для проекта, перекраивать весь проект было нерациональным решением.
ISP Manager — оказалось, что в этой панели управления есть решения для настройки заданий, но инфраструктура клиента была развернута у провайдера, который дорого сердцу генерального директора.
Redis, Celery, dcron, SystemD Timers etc. — конечно, рассматривали, но это другое. Тут идет постоянная нагрузка на CPU, который итак загружен. Да еще за самим сервисом придется следить.
Были и прочие решения уровня observe, но после Cronicle уже казалось, что поиск нет смысла продолжать. Но оказалось, не был учтен один фактор поиска. На презентации вариантов, генеральный директор обозначил, что решение не должно быть open source, тем более иностранным. Что бизнесу важно платить и понимать, с кого требовать, если что-то пойдет не так.
Делаем свой Cron-manager
Разработчики посидели и подумали, что решение мы и сами напишем. Что решили делать:
node-centric — управление в рамках конкретных серверов без распределенности;
alerts — доставка сообщений по разным каналам, особенно на вебхук (создать задачу для решения или в телеграмм-канал);
webhook-start — запуска заданий по вебхуку, чтобы решать задачи из третьих систем силами их специалистов без отвлечения наших ресурсов;
authorname — указывать конкретного автора изменения, чтобы с третьими сторонами разбираться было проще;
limits — указание лимитов по сроку и ресурсам, чтобы задание не выжирало все ресурсы и могло упасть раньше сервера;
webUI — чтобы не разрабочик решал проблемы, а менеджер мог понимать происходящее, посмотреть логи, спросить ИИ и сформулировать задачу разработчику;
Диаграмма Ганта с понятными названиями — для наглядности происходяшего и контроля очередности заданий по требованиям бизнеса;
job2job — назначение окончания одной задачи как старт для следующей;
easytoprod — сделать установку максимально быстрой, провести импорт текущих заданий и начать с ними работу;
easyrecovery — в случае перезагрузки сервера, прерванные задания должны начать свою работу заново;
Да, хотели попроще, а оказалось нам все это надо сразу, иначе смысла нет.
Что по итогу
В мифический "человеко-месяц" мы, конечно же, не уложились. Разработка заняла более четырех месяцев прямой работы, шлифовка, доработки и нюансы продолжаются до сих пор.
Было использовано много сторонних решений (128 лицензий в списке) :
MIT: 97 пакетов;
BSD-3-Clause: 28 пакетов;
BSD-3-Clause, GPL-2.0-only, GPL-3.0-only: 2 пакета (многолицензионные пакеты, где можно выбрать одну из них, обычно используют BSD);
Apache-2.0: 1 пакет.
Интерфейс сделали простым:






Все пишется в базу данных, с блокировками и распуханием которой мы сталкивались в ходе эксплуатации. Решали классическими методами: write a headlock, транзакции, удаление лишнего.
Несколько месяцев заняла работа по подготовке и включению в реестр Минцифры продукта, чтобы наш заказчик был доволен по полной, и мы могли найти других, еще более требовательных клиентов.
В части работы разработчика обнаружилась отдельная ситуация, что галочка "Одновременный запуск" позволяет сосредоточиться на самом скрипте, а исполнение взял на себя сервис. А вебхуки позволяют запускать задания с телефона при помощи приложения "HTTP Shortcuts" без захода в браузер.
Мнение первого заказчика
На проекте уже несколько лет как тишина и спокойствие в части корректности остатков, цен, выявления виновных и планирования распродаж, планирование работ вокруг разработки, перезагрузки серверов и иных операций в системе. Продукт решил главные проблемы на тот момент, сейчас вспоминаем как страшный сон.
Как сам оцениваю результат
Всегда хотелось делать свой продукт, но далеко не всегда была возможность его доделать до конца. Моя команда предпринимала много попыток и разных решений у нас накопилось достаточно. Проблема всегда одна — ресурсы! Пока отвлекаешь разработчика от клиентской задачи, мы перестаем получать деньги, подъедаем запасы, делаем что-то. А потом опять начинаем работать на клиента, откладывая проекты.
Это первый проект, который мы полностью довели до конца, зарегистрировали в Реестре Минцифры, обкатали с клиентами и пользуемся сами.
Горжусь ребятами, потому что они гордятся собой и продуктом, который помогает каждый день.
Сейчас в бэклоге стоят несколько крупных задач, которые диктует бизнес заказчика и наши новые клиенты, у которых возникают свои требования. Для нас важно запустить продукт в разные отрасли бизнеса, получать сложные запросы и проводить доработку под клиента. Для меня и команды это сейчас важный этап перехода от заказной веб-разработки к продуктовой.
