От приложения до кластера: как план на 18 месяцев сжали до трёх сервисов
Разработчик Эйосиас Негаш собирался строить «флагманскую платформу» с Postgres, Redis и service mesh — и передумал. История Ivyk8: от форка Android-приложения Ivy Wallet+ до трёх stateless-сервисов на Kubernetes и пяти записанных решений, которые объясняют, почему базы данных тут нет.
«Примерно 18 месяцев работы для целой команды» — такая оценка, полученная на этапе проектирования, оказалась полезнее любого кода: она убила в проекте всё лишнее ровно в тот момент, когда это было бесплатно.
Порог входа в персональные проекты сейчас смехотворно низкий. Промпт, полчаса — и у тебя на руках архитектурная схема с Istio, Argo CD и шиной сообщений. Проблема начинается следующей секундой, когда понимаешь, что под эту схему нужно нанять команду. Именно это произошло с проектом Ivyk8 — и разбор того, как из него вырезали половину технологий, стоит прочитать даже тем, кто до Kubernetes не дотянется никогда.
Зачем вообще всё это: деньги, телефон и Google Drive
Точка отсчёта — Android, а не серверная. Ivy Wallet — открытый менеджер личных финансов, который 5 ноября 2024 года его владелец заархивировал. Разработчик Эйосиас Негаш решил пользоваться дальше и сделал форк — Ivy Wallet+ со своим пакетным именем и логотипом.
Нововведений набралось с полдесятка: фото- и аудиовложения к операциям, координаты и название места с миграцией базы Room и кнопкой открытия карт, «баланс на дату» по тапу в истории, перенос фильтра отчётов без горизонтальной прокрутки. И отдельная история — авто-заполнение по SMS: приложение слушает выбранный SIM-слот, вытаскивает из банковских сообщений сумму, баланс, дату и описание по регулярным выражениям для каждого банка и показывает уведомление с готовой операцией. Правила импортируются из JSON; правила написаны для четырёх банков и одного мобильного кошелька.
Важно тут не количество фич, а эффект для данных. Телефон стал захватывать заметно больше информации, а изучать годы операций на шести дюймах — занятие мазохиста. Тем более что Ivy Wallet+ уже пишет резервные копии в Google Drive, и этот файл — весь интерфейс между телефоном и внешним миром.
Отсюда и идея Ivyk8 (Ivy + k8s): десктопный read-only дашборд, который читает бэкап из Google Drive и рисует то, чего экран телефона не нарисует: тренды, средние, прогнозы расходов и поступлений. Задача по меркам индустрии — сугубо бытовая. Но именно поэтому интересно, во что её умудрились раздуть.
План, который назвали бы «флагманским»
Стартовал проект с просьбы к Claude Code собрать «топ-1% флагманский проект». Схема вышла богатой: Kotlin-клиент синхронизации и Go-API, отдельный сервис валют и воркер отчётов, Postgres с read-репликами, Redis в роли и кэша, и очереди, плюс Istio, Argo CD, Argo Rollouts, KEDA, SOPS и полный набор observability.
Дальше — лучший ход во всей этой истории. Попросили ту же модель раскритиковать схему до того, как написана хоть строчка кода. Ответ вышел отрезвляющим: это примерно 18 месяцев работы команды. Звучит как анекдот, но дальше шли конкретные придирки, а они ценнее любой оценки сроков.
Разберём их, потому что это квинтэссенция типичных ошибок начинающего backend-проекта.
- Redis в роли кэша и очереди одновременно — это противоречие. Кэшу нужна политика
allkeys-lru(выбрасывай что угодно под давлением памяти), очереди —noeviction(никогда не теряй задачу). Один инстанс не может быть тем и другим. - Read-реплики ломают гарантию read-your-writes. Синхронизирующий клиент загрузил данные, тут же их читает — и получает старую версию с реплики. Отладить такое в одиночку — квест с подвохом.
- Самописный StatefulSet для Postgres — признак незрелого суждения в Kubernetes. Либо используй оператор, либо не запускай базу вовсе.
- Нет слоя идентификации. В проекте никто не отвечал на вопрос, кто пользователь.
- Cosign без проверки подписи при admission — это театр безопасности. Подписанные образы ничего не значат, пока кластер не проверяет эти подписи.
Последний пункт особенно хорош: критика назвала дыру ещё до постройки, и дыра эта, признано, существует до сих пор. Релизный workflow образы подписывает, а вот кластер их по-прежнему не проверяет. Список дел не закрыт.
Отдельная оговорка: «18 месяцев работы команды» — оценка языковой модели, а не живого архитектора, и относиться к ней стоит как к ориентиру по объёму, а не как к точному сроку. Но направление удара верное: масштаб планирования оказался на порядок больше масштаба задачи.
Резать в два захода
Первое сокращение — промпт на MVP. Из него ушли GitOps, канареечные релизы, observability, KEDA и подпись образов. Оставили Kubernetes и базовый CI со статическим анализом.
Второй, окончательный рез произошёл 1 октября и зафиксирован пятью ADR — короткими файлами, которые фиксируют решение и то, что оно заменило.
- ADR-0001: три stateless-сервиса на TypeScript —
web-dashboard,drive-sync-service(публичный бэкенд дашборда) иanalytics-service(внутренний). Без Postgres и без Redis. - ADR-0002: сессии живут в зашифрованной cookie, поэтому сервисы не хранят состояние сессий вообще.
- ADR-0003: доступ к Google Drive ограничен scope’ом
drive.readonly— приложение физически не может изменить бэкап, даже если его скомпрометируют. - ADR-0004: Kubernetes — единственный путь доставки: k3d локально и в CI, никакого docker-compose.
- ADR-0005: курсы валют — от Европейского центрального банка, с запасным источником для валют, которых у ЕЦБ нет.
Ключевое — рассуждение под первым пунктом. Аналитика считается в памяти из одного файла, переопределения часового пояса и курса передаются параметрами запроса. Сохранять между запросами нечего — значит, и базе данных здесь нечего делать. При промахе кэша drive-sync-service просто перезагружает бэкап в аналитику. Это не костыль, а свойство: stateless-сервисы можно убивать, реплицировать и катить обновления без ритуальных плясок. Через несколько недель от этой стройности будет конкретная практическая отдача — но об этом в следующих главах серии.
А зачем вообще Kubernetes для одного человека
Честный вопрос, и ответ в репозитории сформулирован одной фразой: проект должен быть построен, защищён, протестирован и доставлен так, как это делали бы с настоящим продуктовым продуктом. Дашборд — для одного пользователя. Точка не в дашборде, а в практике: тесты, которые режут merge, сканирование образов, подписанные релизы, выкатка на реальный кластер.
В этом весь смысл затеи: учебный стенд, выдаваемый за продакшен, не даёт ни одного навыка продакшена. Решение тренироваться на собственном проекте — валидное, если не врать себе о масштабе.
Уроки, которые переживут проект
Итог короткий, но его стоит проговорить вслух.
- Проси критику до кода. Час того, как тебе говорят, что план — театр, дешевле, чем 18 месяцев открытия этого же факта в бою.
- Statelessness оплачивается один раз. Отказ от базы данных убрал целые классы работ: миграции, бэкапы, реплики, failover.
- Записывай решение, пока оно свежее. Именно ADR позволяют через несколько недель объяснить, почему в проекте нет Redis — без пересмотра всей архитектуры с чистого листа.
Есть и четвёртый, который из исходника не вытекает напрямую, но лежит на поверхности. Автоматическая критика работает только там, где её привлекают. Схему могли спокойно реализовывать как есть — и получили бы годами строящуюся инфраструктуру для приложения, читающего один файл из Google Drive.
Что дальше
Дальше в серии — первые настоящие функции дашборда и переезд с локального k3d-кластера на три домашние машины. Переезд, по признанию автора, прошёл далеко не гладко и стал источником самых странных постов серии — там, где инфраструктура ломается, обычно и появляются настоящие инженерные истории.
А главный вывод этой главы простой и немного обидный для индустрии: для дашборда, который показывает вам ваши же траты, хватает трёх сервисов, памяти и одного файла. Всё остальное — театр, за который, если не прислушаться к критике вовремя, кто-то заплатит восемнадцатью месяцами.