Как мы организовали работу высоконагруженного сервиса для ресторанного бизнеса и что пришлось пережить

d637e8aa6474b0619eb4921d5b7870a1.jpg

Ресторан, такой теплый и ламповый с точки зрения гостя, на самом деле представл\яет собой сложнейшую структуру со множеством бизнес-процессов. Это производство полного цикла плюс торговля и клиентский сервис, и при этом общепит еще обязан соблюдать массу требований со стороны государства. Мы с 2005 года разрабатываем систему автоматизации ресторанного бизнеса и в этом блоге будем рассказывать о своем опыте — от супер-прорывных достижений до грабель, по которым пришлось потоптаться.

iiko — популярное решение: сейчас мы подключаем около 2 000 новых точек в месяц. Среди пользователей iiko есть как традиционные рестораны с обслуживанием в зале, так и доставочные сервисы, клубы, гастромаркеты и прочие разноформатные предприятия, которые кормят людей. Все это мы рассказываем не хвастовства ради, а чтобы было понятно, почему iiko — это высоконагруженный сервис. В нашем облаке развернуто несколько независимых крупных проектов, в совокупности составляющих то, что пользователи называют iiko:

·        серверная часть, в которой хранятся уникальные данные каждого ресторана;

·        система лояльности для гостей;

·        бекофисное приложение для ресторанов;

·        единый облачный API.

В этой статье расскажем о том, как нам удалось организовать работу самого большого проекта — серверной части с данными ресторанов. Эта важнейшая часть нашего облака содержит в себе все справочники, меню, прайс-листы и техкарты, а также все транзакции. Это корневая система, которая регулярно синхронизируется как с кассами, так и с различными внешними и внутренними приложениями. Думаю, что теперь понятно, почему этот проект обрабатывает от 50 000 запросов в секунду в обычном режиме, а в часы пик нагрузка может резко возрастать в 10 раз — до 500 000! При этом наши клиенты крайне чувствительны к работе этой части iiko, так как ее недоступность может нарушить нормальный цикл работы ресторана. Ее простой влияет на такие процессы, как начисление зарплат сотрудникам, заказ продуктов у поставщиков и многое другое.

Тернистый путь из AS IS в TO BE или все надо переделать

Итак, условия задачи выглядели следующим образом: необходимо создать устойчивый сервис, который способен выдерживать резкие увеличения нагрузок на порядок, быстро масштабироваться для подключения новых клиентов и все это с сохранением доступной цены для пользователей. Также мы должны были учитывать, что продукт непрерывно развивается и команда разработки выпускает как регулярные обновления каждые 2 недели, так и патчи для уже вышедших версий. Эти обновления невозможно было бы организовать без настроенного процесса CI/CD.  Забегая вперёд, следует сказать, что у нас получилось. За несколько лет нами проделан сложный путь, на котором было много ошибок. Но мы много работали и исправляли их, улучшая буквально всё — инфраструктуру, архитектуру, процессы и наш собственный взгляд на вещи.

С технической точки зрения проект, о котором мы говорим в этой статье — это веб-приложение, написанное на Java, которое принимает HTTP-запросы от различных сервисов. Основной массив данных хранится в реляционной СУБД. Вначале запуск клиентских сервисов осуществлялся в среде, которая копирует самые частые условия — Windows Server и Microsoft SQL Server. Но такая схема не была стабильной. Нам приходилось оркестрировать клиентские приложения и управлять базами данных с помощью множества самописных скриптов на PowerShell. Проведение сервисных операций было делом сложным и запутанным. Тем не менее, нам удалось добиться удовлетворительной стабильности и относительного удобства. Проблемы начались тогда, когда началось быстрое увеличение количества клиентов. Из-за тяжести систем мы не могли одновременно обеспечивать качественную производительность для клиентов и оптимальное использование ресурсов серверов. Мы поняли, что нужно капитально модернизировать всё.

Как мы решали задачу

Первым делом мы решили найти то, что является причиной наибольшего количества инцидентов и что нам сложнее всего администрировать. Самым слабым звеном оказалась Windows-платформа в целом и SQL Server в частности. Сервера на Windows практически невозможно администрировать без простоев, причины проблем в большинстве случае неочевидны, а развёртывание новых контуров всегда занимает очень много времени и требует ручной настройки. Кроме того, требуется заботиться о лицензиях. Поэтому мы приняли решение переводить платформу на Linux. Для однородности и воспроизводимости серверы решили разворачивать исключительно через Ansible, а в качестве СУБД использовать PostgreSQL. Поскольку проблемы с СУБД были наиболее острыми, приняли решение начать именно с неё.

В течение двух месяцев мы создали систему резервного копирования с квотами и правилами хранения и успешно перешли на схему с master-slave репликацией на двух серверах, в качестве управляющего ПО для которой был выбран Patroni.  Сейчас развёртывание кластера от получения машин до готовности к вводу в работу занимает около 30 минут. В результате мы в несколько раз уменьшили количество инцидентов, связанных с базами данных, вдвое увеличили плотность размещения клиентов без потери производительности и при этом отказались от дорогого программного обеспечения. 

Следующим пунктом в списке было само приложение. Нам не нравилось оркестрировать приложения под Windows Server через набор самописных скриптов. С переходом на Linux эта задача стала выполняться быстрее, проще и надёжнее. В качестве эксперимента было решено попробовать запустить приложение в контейнере. Мы проверили, и это сработало. Кроме того, контейнеры позволяют вкладывать все необходимые версии библиотек и плагинов, поэтому мы смогли создавать персонализированные образы для клиентов. Идея запуска приложения в контейнере всем понравилась и было принято решение попробовать перевести часть контура на новую архитектуру. В роли оркестратора мы сразу выбрали Kubernetes и это оказалось удивительно эффективным решением. Большую часть тех задач, для которых мы постоянно писали скрипты, Kubernetes решал изначально. Например, миграция сервисов при сбое ноды стала простой как никогда раньше, а встроенные абстракции вроде Service и Ingress позволили сразу решить проблему с поиском эндпоинтов и применением правил доступа. Мы смогли быстро развернуть кластер со всем необходимым и бесшовно включить его в часть тестового контура. Спустя несколько недель теста начался аккуратный перевод части Production-серверов в новый контур. Получая положительные отзывы, мы становились опытнее и все смелее переводили клиентов на новую платформу. 

Работа с такой распределённой системой невозможна без постоянного мониторинга. За основу мы взяли Prometheus с его TSDB для сбора метрик приложения и систем, а для сбора статистики из логов применили стек ELK. Эта комбинация позволила удобно хранить и обрабатывать данные, контролируя как всю систему в целом, так и отдельных клиентов. Чтобы справляться с потоком метрик, мониторинг построен по иерархической модели: на каждой площадке стоит свой Prometheus, который собирает «сырые» данные и предварительно обрабатывает их. Далее основной Prometheus собирает обработанные данные с базовых узлов площадок, снова обрабатывает их и уже на основе этих данных мы строим дашборды в Grafana, настраиваем алерты и анализируем ситуацию. После внедрения Consul мы начали использовать его как один из источников данных об инфраструктуре для Prometheus, что позволило автоматизировать обновление целей для мониторинга. 

Как справляться с быстрым ростом

По мере роста приходилось масштабировать не только кластеры, но и сами площадки. Мы сделали это отчасти из-за того, что наше приложение оперирует большими объёмами данных и чувствительно к плохим каналам связи. При этом, кроме прямого улучшения пользовательского опыта, мы смогли воспользоваться и преимуществами схемы с несколькими площадками. Мы настроили распределённое хранение резервных копий таким образом, что даже при сбое целого региона у нас теперь есть возможность восстановить работу приложений клиентов в приемлемое время, распределив нагрузку по другим площадкам. Кроме этого, была усовершенствована система мониторинга, а тесты доступности клиентов теперь проводятся из других доступных регионов, точнее определяя наличие проблем связности. На данный момент, имея опыт развёртывания в ЦОД и набор плейбуков, мы можем запустить новую площадку в течение считанных часов, большая часть времени при этом уйдёт на подготовку серверов перед установкой и контроль уже готовой площадки перед вводом в строй. 

Изначально мы пытались сделать по одному большому кластеру на площадке. Раз за разом, проходя очередные несколько тысяч подов в нём, мы встречались с новыми проблемами. С каждым разом решать их становилось всё труднее, требовалось идти на компромиссы. Было допущено достаточно много болезненных ошибок. В конечном итоге приняли решение не ставить рекорды по количеству запущенных подов в кластере, а формировать кластер под 5000 клиентов по регламенту. Таким образом вместо одного огромного кластера на 30000 подов мы получили 6 кластеров по 5000. Жить стало намного проще — все системы кластеров справлялись с нагрузкой без особого труда, а конфигурация оставалась практически стандартной, не считая нескольких настроек, ускоряющих работу APIserver для нашей специфики. Теперь горизонтальное масштабирование ограничивалось только пулами адресов и физическими размерами ЦОД, но появилась сложность с обнаружением. Требовалось правильно находить запущенные сервисы и вносить изменения в конфигурацию, чтобы при добавлении новых узлов вся система могла перестроиться и перебалансироваться. В частности, это касается узлов, проксирующих входящий и исходящий трафик. В этом очень помог Consul, который замечательно показал себя как сервис обнаружения и редактирования конфигураций. С его помощью удалось полностью автоматизировать рутинную работу по ручному прописыванию сервисов в системы балансировки. 

15e8eb641ed9df940184c73d59940b49.gif

Что получилось

В итоге переход на новую платформу решил поставленные задачи — сервис стал быстрее, удобнее и надёжнее. Переход на контейнеры позволил выпускать релизы быстрее и качественнее. Мы начали использовать несколько CI/CD процессов, которые помогают нам постоянно работать над улучшением сервисов. Kubernetes запускает контейнеры и заботится об их жизненном цикле — будь то клиентское приложение или какая-то внутренняя служба. Consul выполняет обнаружение новых сервисов, и информация об этом становится доступна во всём облаке. Prometheus и ELK собирают и обрабатывают данные об использовании, а Grafana затем их визуализирует. Новая архитектура позволяет неограниченно масштабировать наш сервис, улучшая его надёжность. 

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

© Habrahabr.ru