Здравей, Хабър! Последните месеци преживяхме интересна ситуация и искам да споделя историята ни за мащабирането на инфраструктурата. През това време СберМаркет увеличи поръчките си четворно и стартира услугата в 17 нови града. Внезапният ръст на търсенето на доставка на продукти изискваше от нас мащабиране на инфраструктурата. Прочетете под кат за най-интересните и полезни изводи.

Казвам се Дима Бобилев и съм технически директор на СберМаркет. Тъй като това е първият пост в блога ни, бих искал да кажа няколко думи за себе си и за компанията. Миналата есен участвам в конкурса за млади лидери на Рунета. За състезанието аз за това как в СберМаркет виждаме вътрешната култура и подхода към развитието на услугата. И макар че не успях да спечеля конкурса, формулирах основните принципи за развитие на ИТ-екосистемата.
При управлението на екип е важно да се разбере и намери баланс между необходимостите на бизнеса и нуждите на всеки отделен разработчик. В момента СберМаркет расте 13 пъти на година и това влияе на продукта, изисквайки постоянен ръст на обемите и темповете на разработка. Въпреки това, ние отделяме достатъчно време на разработчиците за предварителен анализ и качествено писане на код. Сформираният подход помага не само за създаването на работещ продукт, но и за неговото по-нататъшно мащабиране и развитие. В резултат на този ръст СберМаркет вече е лидер сред услугите за доставка на продукти: ежедневно доставяме около 18 хиляди поръчки, докато в началото на февруари те бяха около 3500.

Един ден клиент помоли куриера на СберМаркет да достави продуктите му безконтактно — директно на балкона
Но давайте перейдем к специфике. В последние несколько месяцев мы активно расширяли инфраструктуру нашей компании. Эта необходимость вызвана как внешними, так и внутренними факторами. Параллельно с увеличением клиентской базы количество подключенных магазинов возросло с 90 в начале года до более чем 200 к середине мая. Мы, безусловно, подготовились, зарезервировав основную инфраструктуру и рассчитывая на возможность вертикального и горизонтального масштабирования всех виртуальных машин, размещенных в облаке Яндекса. Однако практика показала: «Все, что может пойти не так, пойдет не так». И сегодня я хочу поделиться самыми интересными ситуациями, произошедшими за эти недели. Надеюсь, наш опыт будет полезен для вас.
Slave готов к работе
Еще до начала пандемии мы столкнулись с увеличением количества запросов на наши серверы backend. Тенденция заказывать продукты с доставкой на дом начала набирать популярность, а с введением первых мер самоизоляции из-за COVID-19 нагрузка значительно увеличивалась в течение всего дня. Появилась необходимость оперативно разгрузить master-серверы основной БД и перенести часть запросов на чтение на серверы-реплики (slave).
Мы заранее подготовились к этому шагу, и для такой операции были запущены 2 slave-сервера. На них в основном выполнялись batch-задачи по генерации информационных фидов для обмена данными с партнерами. Эти процессы создавали лишнюю нагрузку и вполне справедливо были исключены из работы пару месяцев назад.
Поскольку на Slave происходила репликация, мы придерживались концепции, что приложения могут работать с ними только в режиме только для чтения. План восстановления после катастрофы подразумевал, что в случае аварии мы сможем просто подключить Slave вместо Master и перенаправить все запросы на запись и чтение на Slave. Однако мы также хотели использовать реплики для нужд отдела аналитики, поэтому серверы не были полностью переведены в статус только для чтения, и на каждом хосте был свой набор пользователей, некоторые из которых имели права записи для сохранения промежуточных результатов расчетов.
До определеното ниво на натоварване ни стигаше и за запис, и за четене при обработка на http-запроси. В средата на март, точно когато Сбермаркет реши да премине изцяло на дистанционна работа, започна значителен растеж на RPS. Все повече от нашите клиенти преминаваха на самоизолация или работа от дома, което се отрази на показателите за натоварване.
Производителността на «мастера» спря да бъде достатъчна, затова започнахме да пренасочваме част от най-тежките заявки за четене към реплика. За прозрачното направление на заявките за запис към мастер и четене към слейв, използвахме ruby gem «». Създадохме специален потребител с постфикс _readonly без права за запис. Но заради грешка в конфигурацията на един от хостовете, част от заявките за запис бяха изпратени на slave-сервер от името на потребител, който имаше съответните права.
Проблемът не се прояви веднага, тъй като увеличеното натоварване увеличи закъснението на слейвовете. Неконсистентността на данните беше открита сутринта, когато след нощните импорти, слейвовете не „догониха“ мастер. Приписахме това на виското натоварване на самия сервис и импорта, свързан със стартирането на нови магазини. Но предоставянето на данни с многочасово закъснение беше недопустимо, и ние преместихме процесите на втория аналитичен слейв, тъй като той имаше боо̀лши ресурси и не беше натоварен с заявки за четене (с което си обяснихме липсата на закъснение на репликацията).
Когато се разбрахме с причините за „разпластяването“ на основния слейв, аналитичният вече беше спрял по същата причина. Въпреки наличието на два допълнителни сървъра, на които планирахме да прехвърлим натоварването в случай на срив на мастера, заради досадна грешка се оказа, че в критичния момент нямаме нито един.
Но тъй като правехме не само dump на БД (ресторът по това време отнемаше около 5 часа), но и snapshot на мастер-сървъра, успяхме да стартираме репликата в рамките на 2 часа. Истината е, че след това ни очакваше налагане на лог репликацията в продължителен период от време (понеже процесът протича в еднопоточен режим, но това вече е съвсем друга история).
Изход: След като се случи този инцидент, стана ясно, че е необходимо да се откажем от практиката за ограничаване на записките за потребителите и да обявим целия сървър за readonly. При подобен подход можем да сме сигурни, че репликите ще бъдат достъпни в критичния момент.
Оптимизацията дори на една трудна заявка може да 'върне към живот' базата данни.
Въпреки че постоянно обновяваме каталога на сайта, заявките, които преместихме на Slave-сървърите, допускаха малко закъснение в сравнение с Master. Времето, през което открихме и разрешихме проблема с 'внезапно изчезналите' слейвове, беше по-дълго от 'психологическата граница' (през това време цените можеха да се сменят, а клиентите да виждат остарели данни), и бяхме принудени да насочим всички заявки към основния сървър на базата данни. В резултат на това сайтът работеше бавно… но поне работеше. Докато Slave-възстановяваше, не ни оставаше друг избор освен оптимизация.
Докато Slave-сървърите се възстановяваха, минутите минаваха бавно, Master оставаше претоварен, а ние насочихме всичките си усилия към оптимизиране на активните задачи по 'Правилото на Парето': избрахме ТОП-заявки, които генерираха най-голямо натоварване и започнахме с настройките. Това се правеше в движение.
Интересен ефект беше, че MySQL, натоварен до края, реагира на дори незначителни подобрения в процесите. Оптимизацията на няколко заявки, които даваха само 5% от общото натоварване, вече показа осезаемо облекчение на CPU. В резултат на това успяхме да осигурим приемлив резерв от ресурси за работа на Master с базата данни и да получим необходимо време за възстановяване на репликите.
Изход: Дори и малка оптимизация позволява да 'оцелеем' при претоварване през няколко часа. Това ни беше напълно достатъчно за времето на възстановяване на сървърите с реплики. Между другото, техническата страна на оптимизацията на заявките ще обсъдим в един от следващите постове. Така че се абонирайте за нашия блог, ако това може да ви е полезно.
Организирайте мониторинг на работоспособността на партньорските услуги.
Ние обработваме поръчките от клиентите и затова нашите услуги постоянно взаимодействат с външни API - това са шлюзове за изпращане на SMS, платежни платформи, маршрутизиращи системи, геокодери, услуги на данъчната служба и много други системи. И когато натоварването започна да нараства стремително, започнахме да се сблъскваме с ограниченията на API на нашите партньори, за които не бяхме и подозирали.
Неочакваното надвишаване на квотите на партньорските услуги може да доведе до нестабилност на вашата собствена система. Много API блокират клиенти, които надвишават лимитите, а в някои случаи излишъкът от заявки може да претовари продукцията на партньора.
Например, в момент на растеж на броя доставки, съпътстващите услуги не се справяха със задачите по тяхното разпределение и определяне на маршрути. В резултат на това се оказваше, че поръчките са направени, но услугата, която създава маршрут, не работи. Трябва да кажа, че нашите логисти направиха почти невъзможното в тези условия, а ясната координация на екипа помогна да се компенсират времевите откази на услугите. Но такъв обем заявки не може да се обработва ръчно постоянно и след известно време щяхме да се сблъскаме с неприемлив разрив между поръчките и тяхното изпълнение.
Бяха приети редица организационни мерки и слажената работа на колектива помогна да спечелим време, докато договаряхме нови условия и чакахме модернизацията на услугите от страна на някои партньори. Има и други API, които радват с висока издръжливост и небожески тарифи при висок трафик. Например, в началото използвахме известен картографски API за определяне на адреса на доставка. Но в края на месеца получихме сметка почти от 2 милиона рубли. След това решихме спешно да го заменим. Няма да правя реклама, но ще кажа, че разходите ни значително намаляха.

Изход: Задължително е да се следят условията на работа на всички партньорски услуги и да ги имате предвид. Дори днес да изглежда, че те имат "голям запас", това не означава, че утре няма да станат пречка за растежа. И, разбира се, е по-добре да се договорите за финансовите условия на увеличените заявки към услугата предварително.
Понякога се оказва, че "" (с) не помага
Привикнали сме към проблемите в основната база данни или на сървърите на приложенията, но при мащабиране неприятностите могат да се появят на места, където не сме очаквали. За пълнотекстово търсене на сайта използваме движка Apache Solr. С увеличаването на натоварването забелязахме, че времето за отговор намалява, а натоварването на процесора на сървъра вече достига 100%. Какво може да е по-просто — просто даваме на контейнера със Solr повече ресурси.
Вместо очакваното увеличение на производителността, сървърът просто „умря“. Той започна да се натоварва на 100% и да отговаря още по-бавно. Първоначално имахме 2 ядра и 2 ГБ RAM. Решихме да направим това, което обикновено помага — дадохме на сървъра 8 ядра и 32 ГБ. Всичко стана много по-лошо (как точно и защо — ще разкажем в отделен пост).
След няколко дни успяхме да се запознаем в детайли с въпроса и постигнахме оптимална производителност при 8 ядра и 32 ГБ. Тази конфигурация ни позволява и днес да продължаваме да увеличаваме натоварването, което е много важно, тъй като растежът идва не само от клиентите, но и от броя на свързаните магазини — за 2 месеца техният брой се е удвоил.
Изход: Стандартните методи като „добавяне на повече желязо“ не винаги работят. Така че при мащабиране на всяка услуга е необходимо да разберем как използва ресурсите и предварително да тестваме работата й в нови условия.
Stateless — ключът към простото хоризонтално мащабиране
Като цяло нашият екип следва известния подход: услугите не трябва да имат вътрешно състояние (stateless) и трябва да бъдат независими от средата на изпълнение. Това ни позволи да понасяме увеличението на натоварването чрез просто хоризонтално мащабиране. Но имахме едно изключение – обработчик на дълги фонови задачи. Той се занимаваше с изпращане на имейли и SMS, обработка на събития, генериране на фийдове, импорт на цени и наличности, обработка на изображения. Получи се така, че той зависеше от локално файлово хранилище и съществуваше в единствен екземпляр.
Когато броят на задачите в опашката на обработвача се увеличи (а това естествено се случи с увеличаването на поръчките), производителността на хоста, на който бяха разположени обработвачът и файловото хранилище, стана лимитиращ фактор. В резултат спря обновлението на асортимента и цените, изпращането на нотификации до потребителите и много други критични функции, които заседнаха в опашката. Екипът Ops бързо мигрира файловото хранилище в S3-подобно мрежово хранилище, което ни позволи да включим няколко мощни машини, за да мащабираме обработвача на фонови задачи.
Изход: Правило Stateless трябва да се спазва за всички компоненти без изключение, дори ако изглежда, че "тук със сигурност няма да се сблъскаме с проблеми". По-добре е да се отдели малко време за правилна организация на работата на всички системи, отколкото после в бързина да се пренаписва кода и да се поправя услугата, която изпитва претоварване.
7 принципа за интензивен растеж
Въпреки наличието на допълнителни ресурси, по време на растежа ние се сблъскахме с няколко проблема. През това време броят на поръчките се увеличи повече от 4 пъти. Вече доставяме над 17 000 поръчки на ден в 62 града и планираме още по-широко разширение — през първото полугодие на 2020 г. се очаква стартиране на услугата в цяла Русия. За да се справим с нарастващото натоварване, имайки предвид вече направените грешки, ние формулирахме 7 основни принципа за работа при постоянен растеж:
- Инцидентен мениджмънт. Създадохме дъска в Jira, където всеки инцидент се отразява под формата на тикет. Това ще помогне за приоритизиране и изпълнение на свързаните с инцидента задачи. В същността си не е страшно да грешиш — страшно е да грешиш два пъти по един и същи повод. За случаите, когато инцидентите се повтарят преди да успеем да поправим причината, трябва да има готова инструкция за действие, защото по време на голямо натоварване е важно да реагираме мигновено.
- Мониторинг необходим за всички елементи на инфраструктурата без изключение. Именно благодаря нему мы могли прогнозировать рост нагрузки и правильно выбирать «бутылочные горлышки» для приоритезации устранения. Скорее всего, при высокой нагрузке сломается или начнет тормозить все, о чем вы и не думали. Поэтому новите алерти най-добре е да се създадат веднага след първите инциденти, за да могат да се следят и предвиждат.
- Правилните алерти са абсолютно необходими при рязък ръст на натоварването. На първо място, те трябва да уведомяват какво точно е счупено. На второ място, не трябва да има много алерти, защото изобилието от некритични алерти води до игнориране на всички предупреждения напълно.
- Приложенията трябва да бъдат stateless. Убедихме се, че за това правило не трябва да има изключения. Необходима е пълна независимост от средата на изпълнение. За това можете да съхранявате споделени данни в база данни или, например, директно в S3. А още по-добре е да се следват правилата. По време на рязък ръст времето за оптимизиране на кода е недостатъчно и справянето с натоварването е необходимо чрез директно увеличаване на изчислителните ресурси и хоризонтално мащабиране.
- Квоти и производителност на външните услуги. При бърз растеж проблеми могат да възникнат не само във вашата инфраструктура, но и във външната услуга. Най-разочароващото е, когато това се случва не поради повреда, а заради достигане на квоти или лимити. Така че външните услуги трябва да се масштабрат толкова добре, колкото и вие самите.
- Разделяйте процесите и опашките. Това много помага, когато на един от шлюзовете възникне запушване. Ние нямаше да се сблъскваме с забавяния в предаването на данни, ако запълнените опашки за изпращане на SMS не пречеха на обмена на уведомления между информационните системи. И беше по-лесно да се увеличи броят на работниците, ако те работеха отделно.
- Финансови реалности. Когато се наблюдава експлозивен растеж на потокa от данни, няма време да се мисли за тарифи и абонаменти. Но трябва да се помни за тях, особено ако сте малка компания. Голямата сметка може да бъде отправена от собственика на който и да е API, както и от вашия хостинг доставчик. Затова е важно да четете внимателно договорите.
Заключение
Не без потерь, но ние преодолявахме този етап и днес се стараем да спазваме всички намерени принципи, а всяка машина има възможност за лесно увеличаване на производителността х4, за да се справи с неочаквани ситуации.
В следващите постове ще споделим опита си за разследване на спада на производителността в Apache Solr и ще разкажем за оптимизацията на запитванията и как взаимодействието с ФНС помага на компанията да спести пари. Следвайте блога ни, за да не пропуснете нищо, и споделяйте в коментарите дали сте имали подобни неприятности по време на ръста на трафика.

Само регистрирани потребители могат да участват в анкетата. , моля.
Случвало ли ви се е забавяне/падане на услугата при рязък ръст на натоварването поради:
55,6%Невъзможност за бързо добавяне на изчислителни ресурси10
16,7%Ограничения на инфраструктурата на хостинг доставчика3
33,3%Ограничения на външни API6
27,8%Нарушаване на принципите на stateless на собствените си приложения5
88,9%Неоптималност на кода на собствените услуги16
Гласували 18 потребители. Въздържали се 6 потребители.
Източник: habr.com
