Журнал разработчиков №1: Представляем грузовой корабль

Журнал разработчиков №1: Представляем грузовой корабль

24 октября 2023 г.

Некоторое время я хотел создать контент типа «Журнал разработки» для проекта, над которым я работал последние два года, под названием Freighter. Freighter – это платформа для размещения игровых серверов, предлагающая тарифный план в зависимости от использования.

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

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

Какова цель грузовых перевозок?

Проблема, которую Freighter пытается решить, — это стоимость входа в серверный хостинг. Использование трех первых результатов при поиске «Платформы хостинга серверов Minecraft» (BisectHosting, хостинг Apex и Shockbyte), вы заметите что все планы оплаты являются ежемесячными и становятся дороже, чем больше памяти включает в себя план.

Для случайной игры (особенно для родителей, которые платят за детские серверы) платить 10 долларов плюс месяц за сервер, у которого 80% времени простоя, кажется плохой инвестицией. Из трех перечисленных платформ самый дешевый вариант по-прежнему стоит 2,50 доллара в месяц за сервер с одним гигабайтом оперативной памяти, который не сможет обслуживать более пары игроков в маленьком мире.

Как Freighter решает эту проблему? Freighter предоставляет пользователю возможность выключить свой игровой сервер по требованию, что освобождает ресурсы на хост-компьютере и позволяет разместить больше игровых серверов на одном компьютере.

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

Используя план оплаты по факту использования, вы сэкономите деньги, поскольку вам не придется платить за все время простоя.

Как появились грузовые перевозки.

За свою историю компания Freighter претерпела множество изменений, начиная с проекта, который группа из нас реализовала для студентов колледжа.

Происхождение

В последнем семестре колледжа я прошел курс, предназначенный для моделирования среды Agile-разработки. Один из членов группы был назначен менеджером проекта и разбивал работу на эпики и спринты.

Созданный нами проект стал прародителем Freighter. Мы использовали Azure для инфраструктуры и виртуальные машины для размещения серверов.

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

Это оказалось очень неэффективным и трудным в управлении.

Итерация 1

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

На этот раз мы будем использовать AWS для управления инфраструктурой и EC2 для управления игровыми серверами. Первоначально этот проект имел большой прогресс, но в конечном итоге мы столкнулись с трудностями. Мы не могли программно создавать экземпляры EC2 с помощью лямбда-выражений, что заставило бы нас предварительно генерировать экземпляр EC2 и создавать скрипт для создания новых игровых серверов внутри этого экземпляра.

По сути, на этом первоначальный групповой проект и завершился.

Итерация 2

Несколько месяцев спустя я придумал еще один подход, который хотел попробовать. Я начал эту итерацию в одиночку, и план состоял как минимум из двух серверов. Первый сервер будет обрабатывать веб-приложение, а второй будет использоваться для автоматизации Docker Engine.

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

Это увеличило сложность инфраструктуры, особенно при попытках масштабирования. Одной из моих главных проблем в этой итерации было потребление памяти API хост-сервера. Я хотел зарезервировать как можно больше памяти для игровых серверов, поэтому не мог использовать такую ​​структуру, как .NET.

Я остановился на создании API с использованием Rocket.rs. Хотя я знаком с Rust и могу создавать простые приложения, это оказалось сложно. Добавление конечных точек или новых функций в API занимало несколько часов, и я часто сталкивался с проблемами.

Многие из этих проблем были специфичны для фреймворка, но отсутствие глубокого понимания Rust не помогло. Само собой разумеется, что этот подход потерпел неудачу вскоре после запуска.

Итерация 3

Третья итерация проекта прошла успешно. После провала последней итерации я узнал о PocketBase. PocketBase — это серверная часть как услуга (Baas), которая предоставляет базу данных (с использованием SQLite), API и аутентификацию в одном исполняемом файле.

Если вам не нужна готовая версия PocketBase, вы также можете использовать ее в качестве платформы для расширения ее базовой функциональности с помощью Go или JavaScript. Чтобы изменить PocketBase под нужды Freighter, я расширил функциональность с помощью Go.

Благодаря тому, что PocketBase предоставил большую часть функций веб-приложений «из коробки», я смог больше сосредоточиться на хостинге игр.

В этой итерации по-прежнему будут использоваться два сервера, но вместо использования API для взаимодействия между серверами будет использоваться SSH. Когда пользователь делает запрос на изменение онлайн-статуса сервера в базе данных, я сначала пытаюсь подключиться по SSH к хост-серверу и запустить контейнер.

В случае успеха я обновлю базу данных и верну результат.

Следующей вехой в проекте было создание образа Docker, который при получении сигнала SITGERM корректно отключал игровой сервер, чтобы гарантировать, что прогресс не будет потерян. Для меня это был полезный опыт, поскольку у меня было мало знаний о создании собственных изображений.

Пришлось немного повозиться, но в конечном итоге мне удалось уловить сигнал SITGERM, а затем запустить команду остановки в терминале сервера Minecraft.

После этого я получил полностью функциональный продукт. PocketBase управлял моим веб-приложением, он связывался с хост-серверами через SSH, и хост-сервер мог корректно запускать и останавливать серверы Minecraft. Теперь осталось только выяснить стоимость.

Как я упоминал ранее, здесь используется решение с несколькими серверами. Это означает, что мне придется заплатить как за сервер веб-приложений, так и за сами хостинг-серверы. Это может оказаться сложнее, чем ожидалось, поскольку PocketBase представляет собой монолитную структуру и плохо масштабируется.

SQLite не имеет возможности горизонтального масштабирования, а также нет возможности сегментировать или реплицировать данные без выполнения какого-либо задания синхронизации между серверами, на которых размещено приложение PocketBase. Даже при выполнении задания синхронизации все экземпляры PocketBase будут расти синхронно.

В конце концов, два терабайта данных, которые может обработать SQLite, будут заполнены, и все экземпляры PocketBase отключатся. Это довольно большая проблема, которую нельзя игнорировать.

Текущая итерация

Это подводит нас к сегодняшнему дню. Чтобы решить эту проблему масштабирования, я сейчас работаю над переносом функций PocketBase на AWS. Благодаря моему опыту использования AWS для первой версии Freighter, а также для других проектов, я научился создавать шаблоны SAM, которые позволяют мне определять мою инфраструктуру как код. Эти шаблоны SAM экономят мне много времени при разработке проекта.

Инфраструктура довольно стандартная. Он использует DynamoDB для базы данных, два шлюза API с использованием Python Lambdas и Secret Manager для хранения конфиденциальной информации, такой как ключи подписи JWT.

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

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

Что теперь?

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

На момент написания этой статьи я выполнил эту задачу. Этот журнал разработки должен появиться в течение следующей недели или двух.

Спасибо за внимание, увидимся в следующем выпуске.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE