Безсерверная масштабируемость: запуск EdTech для изучения арабского на AWS Lambda
Alphazed полностью обслуживает бэкенд — более 95 000 учеников из 50+ стран — используя AWS Lambda с Serverless Framework. Архитектура включает Flask на Lambda за API Gateway, MySQL 8 на RDS, S3 для доставки контента и собственное аналитическое озеро (SQS → Kinesis Firehose → S3 → Glue → Athena). Тонкие обработчики Lambda уменьшают задержки холодного старта, и система обслуживает более 7 приложений из одного кода с переключением конфигураций во время выполнения.
Почему безсерверная архитектура подходит для EdTech?
Образовательные приложения имеют непредсказуемую нагрузку:
- Утром в будни: родители скачивают приложение перед школой детей (всплеск трафика)
- Днем в будни: занятия после школы (постоянная нагрузка)
- В выходные: интенсивные марафонские сессии (2-3х обычной нагрузки)
- Во время Рамадана: вечерний пик (семейное чтение Корана)
- Школьные каникулы: совсем другой шаблон
Преимущества безсерверности:
- Оплата за вызов: платите только за фактические запросы. 10 вызовов — 10 оплат, 100,000 — мгновенное масштабирование.
- Нулевые задержки холодного старта для часто вызываемых функций — используем «всегда горячие» слои Lambda.
- Автомасштабирование: обслуживаем от 10 до 10 000 одновременно без изменений инфраструктуры.
- Отсутствие поддержки серверов: команда фокусируется на учебных планах и ИИ, а не на Kubernetes или балансировщиках нагрузки.
Глубокий обзор архитектуры
API Gateway → Lambda → RDS
[Клиентское приложение] (iOS, Android, Web)
↓
[API Gateway] (маршрутизация HTTP, ограничение запросов)
↓
[Lambda-обработчики] (Flask, 512MB, timeout 28с)
├── Маршруты приложения: /app/* (мобильные)
├── Пользовательские: /user/* (с авторизацией)
└── Админские: /boss/* (админ-панель)
↓
[MySQL 8 на RDS] (устойчивое хранилище)
↓
[Ответ] (JSON клиенту)
Тонкие Lambda для скорости
Большинство Lambda намеренно минимальны:
# Тонкий обработчик (~100Кб)
import json
import pymysql
def get_user_progress(event, context):
user_id = event['pathParameters']['user_id']
# Прямое подключение к БД (без ORM)
conn = pymysql.connect(host='rds.aws.com', user='app', password='...', database='amal')
cursor = conn.cursor()
cursor.execute(
'SELECT concept_id, accuracy FROM user_memory WHERE user_id = %s',
(user_id,)
)
rows = cursor.fetchall()
conn.close()
return {
'statusCode': 200,
'body': json.dumps([{'concept': r[0], 'accuracy': r[1]} for r in rows])
}
Без Flask, без SQLAlchemy, без middleware — холодный старт около 500 мс против 5-10 с для полного Flask приложения.
Тяжелые эндпоинты (генерация контента, аналитика) используют полный Flask:
# Тяжёлый обработчик (~30MB с Flask, SQLAlchemy, numpy)
from flask import Flask, jsonify
from models import UserMemory
import numpy as np
app = Flask(name)
@app.route('/content_duo/generate', methods=['POST'])
def generate_content_duo():
# Сложная логика с ORM
user = UserMemory.query.filter_by(user_id=request.json['user_id']).first()
# ... генерация персонализированной сессии ...
return jsonify(session_data)
Компромисс: медленнее холодные старты, но такие вызовы менее частые.
Префикс таблиц для каждого приложения
Один инстанс RDS обслуживает 7+ приложений с изоляцией на уровне базы:
-- Amal app
CREATE TABLE amal_users (...)
CREATE TABLE amal_content_bytes (...)
CREATE TABLE amal_user_memory (...)
-- Thurayya app
CREATE TABLE thurayya_users (...)
CREATE TABLE thurayya_content_bytes (...)
CREATE TABLE thurayya_user_memory (...)
-- Другие приложения: qais_, kidelite_, и др.
При деплое переменная окружения APP_NAME выбирает префикс:
app_name = os.getenv('APP_NAME', 'amal') # 'amal', 'thurayya', 'qais' и т.п.
Запросы динамически используют префикс
table_name = f'{app_name}_users'
cursor.execute(f'SELECT * FROM {table_name} WHERE id = %s', (user_id,))
Аналитическое озеро
Проблема: Прямые запросы в базу для аналитики замедляют производство, отчёты блокируют таблицы.
Решение: Асинхронный аналитический конвейер
[Мобильное приложение]
↓ (отправка события)
[API Endpoint] → [SQS очередь] (асинхронно)
↓ (быстрый ответ приложению)
↓ (аналитика в фоне)
[Kinesis Firehose] (пакетирует события каждые 5 мин или по 100Мб)
↓
[S3] (разбиение: s3://analytics-lake/amal/2026/03/28/events.parquet)
↓
[AWS Glue] (сканирует S3, определяет схему)
↓
[Athena] (SQL-запросы на Presto)
↓
[Дашборд] (показывает аналитику в реальном времени)
Обработка ошибок (Dead Letter Queue)
Если обработка аналитики падает:
SQS → [Firehose error]
↓
[DLQ принимает ошибки]
↓
[Оповещение команде]
↓
[Основной API не влияет]
Аналитика не блокирует пользователей — дети могут учиться даже при сбое аналитики.
Стратегии оптимизации затрат
Стратегия 1: Тонкие Lambda для часто используемых эндпоинтов
- Обычно мобильное приложение делает 10-20 вызовов за сессию
- 95,000 активных пользователей × 3 сессии в день × 15 вызовов = 4,275,000 вызовов в день
- При цене $0.0000002 за вызов — около $0.86 в день
- Сокращение задержки холодного старта на 10 секунд экономит ~$500 в месяц
Стратегия 2: Резервирование RDS
- 3-летнее резервирование даёт ~60% скидку по сравнению с по требованию
- Используем db.r6i.xlarge (4 vCPU, 32GB RAM): $2,800/мес. по резерву против $6,500/мес. on-demand
- Годовая экономия около $50,000
Стратегия 3: Кэширование
- Кэшируем часто запрашиваемые данные (учебный план, контент) в ElastiCache (Redis)
- Уменьшает количество запросов к RDS на 70%
- Стоимость $800/мес. за кэш, экономия $2,000/мес. на RDS
Обслуживание 7+ приложений из одного кода
| Приложение | Префикс | Таблицы базы | Lambda стек | Статус |
|---|---|---|---|---|
| Amal | amal_ | 40+ таблиц | Общий | В продакшене |
| Thurayya | thurayya_ | 40+ таблиц | Общий | В продакшене |
| Qais | qais_ | 35+ таблиц | Общий | Бета |
| KidElite | kidelite_ | 40+ таблиц | Общий | В продакшене |
| Alphazed School | school_ | 50+ таблиц | Общий | Бета |
| Alphazed Montessori | montessori_ | 45+ таблиц | Общий | Внутренний |
Один бэкенд, одна конвейерная сборка, 6 одновременных приложений. Запуск нового приложения занимает недели, а не месяцы.
Часто задаваемые вопросы
Вопрос: Не ограничивает ли Lambda таймаут в 15 минут?
Ответ: Да, 15 минут — максимум, но мы редко запускаем такие долгие запросы. Тяжелые задачи (генерация контента, крупные экспорты) выполняются асинхронно через SQS и Step Functions.
Вопрос: Что если БД упадёт?
Ответ: RDS использует Multi-AZ с автоматическим переключением на резервную копию за ~60 секунд. Клиенты видят кратковременные таймауты, но восстановление быстрое.
Вопрос: Как управляется пул подключений к базе с безсостоянием Lambda?
Ответ: Каждый экземпляр Lambda поддерживает пул подключений, который переиспользуется при «тёплых» вызовах. Холодные старты получают свежие подключения. RDS Proxy стоит между Lambda и БД, чтобы контролировать лимиты подключений.



