Василий, CTO Ecwid, написал еще одну историю из жизни компании (заодно прочитайте предыдущую). Публикуем слово в слово.

***

Всем привет! Опять хочу рассказать историю. В этот раз я напишу про один из наших провалов. Читать про факапы всегда интереснее, чем про успехи, как мне кажется.

У нас в Ecwid довольно большая, сложная инфраструктура, которую мы целиком держим в амазоновских облаках (AWS). Есть немного legacy, немного костылей и подпорок, но это обусловлено «исторической загогулиной развития продукта». Мы постепенно с ними боремся, особо прочные костыли переименовываем в «особенности реализации» и больших проблем не испытываем.

Из существенных особенностей нашего продукта — мы используем микросервисную архитектуру и подход, называемый Immutable Infrastructure.

Микросервисная архитектура это, грубо говоря, когда ваш продукт поделен на небольшие, независимые сервисы, которые работают изолированно друг от друга. Работают, обновляются, падают — всё независимо. Это деление исключительно внутреннее, техническое и для пользователей никак не проявляется. Сейчас в Ecwid около 50 разных сервисов (размазанных по 150 серверам) и, пока я пишу этот текст, количество может измениться в любую сторону. Есть сервис отвечающий за обработку картинок, есть сервис-хранитель всей информации об аккаунтах пользователей, сервисы для интеграции с различными внешними системами доставки и платёжками, есть даже сервис для генерации баркодов :) Сервисы-сервисы-сервисы…

Immutable infrastructure мой внутренний prompt переводит как «неизменяемая инфраструктура». Переводит и плачет, но пусть будет так. На пальцах это значит, что на уже работающих серверах вы не меняете ничего. Есть сервер, он запущен, настроен, на нем работает какая-то версия вашего сервиса. Если вы хотите что-либо поменять (запатчить ядро, установить какую-нибудь утилиту, обновить сам сервис) — вы стартуете новый сервер, где эти изменения уже есть — новое ядро, обновленные утилиты, другая версия сервиса. Новый сервер подключаете к работе, а старый убиваете. Любые изменения делаются через поднятие нового сервера, старый никогда не меняется. Live fast, die young.

Учитывая эти две особенности, наши релизы обычно протекают так — мы стартуем новые сервера, переводим на них трафик и, если всё хорошо, убиваем старые сервера. Если всё не очень хорошо (например, не прошли автоматические пост-релизные тесты) мы возвращаем трафик на старые сервера и убиваем новые. Если карты легли так, что надо обновить весь кластер (а бывает и такое) — значит, придется поднять ~150 новых серверов и убить столько же (плюс/минус) старых.

И вот, в один прекрасный день мы решили отрелизить сервис, отвечающий у нас за биллинг. Ecwid — SaaS продукт, работающий по подписке. Каждый месяц мы автоматически снимаем с карты клиента определенную сумму в зависимости от его плана, страны, валюты, установленных приложений из нашего Ecwid AppMarket'а и прочее. Логика в некоторых случаях бывает довольно сложна (одних планов у нас больше тысячи) и за неё у нас отвечает отдельный сервис — биллинг.

Работает он довольно просто — раз в несколько минут биллинг запускает периодическую задачу, которая находит пачку клиентов, у которых закончилась подписка, и пытается снять деньги с карты. Через несколько минут находит следующую пачку клиентов, снимает с них деньги и так далее.

Релиз-инженер запустил новую версию биллинга, прогнал тесты, потыкал тут, там, убедился что всё хорошо и закончил с релизом. В те времена наши релизы ещё не были автоматизированы полностью, поэтому то, что случилось дальше мы до сих пор вспоминаем со стыдом.
Инженер запустил новый сервер с биллингом, но забыл убить старый. Забыл и ушел домой. Всю ночь до утра у нас работало два биллинга параллельно.

Эти два запущенных биллинга посмотрели друг на друга и приступили к работе. Первый биллинг взял пачку клиентов с просроченной подпиской из базы и снял с них деньги. Второй биллинг поступил точно также — взял ту же самую пачку клиентов и снял с них деньги. Повторим? Повторим! Ещё разок? Спрашиваешь!

Этот финансовый марафон, это денежное безумство продолжалось до утра. Всю ночь два биллинга исправно брали по две одинаковые пачки клиентов и снимали с них деньги. Раз в пару минут. Всю, бл***, ночь.

Я инженер-оптимист и даже тут нахожу повод для гордости — у нас не сломалось ничего. Не упал сервер с базой данных, не вылез внезапный баг в биллинге, не выскочило необработанное исключение в сервисе. Наш код работал, как часы. Не вылезло ни одной проблемы, которая могла бы остановить весь сервис или хотя бы эту периодическую задачу (когда это было так надо). И даже NTP не подвел — время на обоих серверах было синхронизировано просто замечательно. Задачи на двух независимых машинах стартовали практически одновременно (слава NTP!). Это была ночь идеального аптайма и прекрасных финансовых показателей.

Про показатели я не шучу. Утром наш финотдел пришел на работу, посмотрел отчеты и поразился проделанной за ночь работе — выручка удвоилась! Не 20%, не 30%, а сразу в два раза! Весело расстреливая друг друга пробками от шампанского, они всё же осторожно поинтересовались — что мы все такого сделали и нельзя ли это повторить? Может, новая, особо удачная рекламная компания? Может отличный блог-пост, который перепечатали крупные СМИ и клиенты хлынули потоком? Релиз долгожданной фичи? Новый крупный партнёр? ЧТО?!

Поискали новых партнеров — нет таких. Новые фичи — тоже нет, причина не в этом. Блог? Наш блог прекрасен, но, увы, не то. Примерно в это же время, в момент наших поисков, насколько я помню, проснулись клиенты и начали спрашивать «Почему с меня сняли деньги дважды?». Эти вопросы, невиданные финансовые показатели, а так же вчерашний релиз биллинга сложились в одну нерадостную картину.

FACEPALM.

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

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

***

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

Тестовое для разработчиков: github.com/ecwid/new-job; вакансия QA-инженера ulyanovsk.hh.ru/vacancy/22564883

По всем вопросам пишите в комментариях или на join@ecwid.com

Комментарии (0)