Skalierbare Serverless-EdTech mit AWS Lambda
5 Min. LesezeitMohammad Shaker

Skalierbare Serverless-EdTech mit AWS Lambda

Alphazed betreibt sein Backend für 95.000+ Schüler mit AWS Lambda. Architektur: Flask auf Lambda, MySQL 8 auf RDS, Analytics-See für Lernerfolgstracking.

Engineering

Schnelle Antwort

Alphazed betreibt sein Backend für 95.000+ Schüler mit AWS Lambda. Architektur: Flask auf Lambda, MySQL 8 auf RDS, Analytics-See für Lernerfolgstracking.

Alphazed betreibt sein gesamtes Backend — und bedient damit über 95.000 Schüler in mehr als 50 Ländern — mit AWS Lambda und dem Serverless Framework. Die Architektur nutzt Flask auf Lambda hinter API Gateway, MySQL 8 auf RDS, S3 für die Inhaltsbereitstellung und einen maßgeschneiderten Analytik-See (SQS → Kinesis Firehose → S3 → Glue → Athena). Dünne Lambda-Handler optimieren die Kaltstartlatenz, und das System bedient über 7 Apps von einer einzigen Codebasis mit Laufzeiteinstellungsänderungen.

Warum Serverless für EdTech?

Bildungs-Apps haben unvorhersehbare Nutzungsmuster:

  • Werktagmorgen: Eltern laden App vor dem Schulbesuch ihres Kindes herunter (Traffic-Spitze)
  • Nachmittags: Nachschulische Übungssitzungen (anhaltende Last)
  • Wochenenden: Intensive Marathonsitzungen (2-3x normale Last)
  • Während Ramadan: Nutzung am Abend explodiert (Familien-Koran-Sitzungen)
  • Schulferien: Gänzlich anderes Muster

Vorteile von Serverless:

  • Pay-per-Request-Preismodell: Man zahlt nur für tatsächliche Nutzung. Bei 10 Nutzern, die die API aufrufen, zahlt man 10 Aufrufe. Wenn 100.000 in einem viralen Moment zugreifen, skaliert alles sofort.
  • Keine Kaltstarts für hochfrequentierte Endpunkte: Wir verwenden "immer warme" Lambda-Layer für häufig aufgerufene Endpunkte
  • Auto-Scaling: 10 gleichzeitige Nutzer oder 10.000 bewältigen ohne Infrastrukturänderungen
  • Keine Serverwartung: Das Team konzentriert sich auf Curriculum und KI, nicht auf Kubernetes-Cluster oder Load Balancer

Architektur Deep-Dive

API Gateway → Lambda → RDS

[Client-App] (iOS, Android, Web)
    ↓
[API Gateway] (HTTP-Routing, Ratenbegrenzung)
    ↓
[Lambda-Handler] (Flask-App, 512MB Speicher, 28s Timeout)
    ├── App-Routen: /app/* (mobile Endpunkte)
    ├── Nutzer-Routen: /user/* (autorisierte Endpunkte)
    └── Admin-Routen: /boss/* (Admin-Dashboard)
    ↓
[MySQL 8 auf RDS] (Persistente Daten)
    ↓
[Antwort] (JSON zurück an den Client)

Dünne Lambdas für Geschwindigkeit

Die meisten Lambdas sind bewusst minimal:

# Dünner Handler (~100KB)
import json
import pymysql

def get_user_progress(event, context):
    user_id = event['pathParameters']['user_id']
    
    # Direkte DB-Verbindung (kein ORM-Overhead)
    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])
    }

Kein Flask-Import, kein SQLAlchemy ORM, keine Middleware. Ergebnis: ~500ms Kaltstart vs. 5-10s für vollständige Flask-App.

Schwere Endpunkte (Inhaltsgenerierung, Analytik-Verarbeitung) nutzen vollständiges Flask:

# Schwerer Handler (~30MB mit 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():
    # Komplexe Logik, die ORM erfordert
    user = UserMemory.query.filter_by(user_id=request.json['user_id']).first()
    # ... personalisierte Sitzung erzeugen ...
    return jsonify(session_data)

Trade-off: Kaltstarts sind langsamer, diese werden jedoch seltener aufgerufen.

Pro-App-Tabellenpräfixierung

Eine RDS-Instanz bedient über 7 Apps mit datenbankseitiger Isolation:

-- 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 (...)

-- Andere Apps: qais_*, kiDelite_*, etc.

Zum Bereitstellungszeitpunkt wählt die APP_NAME-Umgebungsvariable das Präfix aus:

app_name = os.getenv('APP_NAME', 'amal')  # 'amal', 'thurayya', 'qais', etc.

# Abfragen verwenden dynamisch Präfix
table_name = f'{app_name}_users'
cursor.execute(f'SELECT * FROM {table_name} WHERE id = %s', (user_id,))

Der Analytics-See

Problem: Direkte Datenbankabfragen für Analytik verlangsamen die Produktion. Berichte blockieren Tabellen.

Lösung: Asynchrone Analytik-Pipeline

[Mobile App]
    ↓ (senden Ereignis)
[API-Endpunkt] → [SQS-Queue] (asynchron)
    ↓ (antwortet sofort auf App)
    ↓ (wartet nicht auf Analytik)
[Kinesis Firehose] (bündelt Ereignisse alle 5 Minuten oder wenn 100 MB erreicht)
    ↓
[S3] (partitioniert: s3://analytics-lake/amal/2026/03/28/events.parquet)
    ↓
[AWS Glue] (durchsucht S3, ermittelt Schema)
    ↓
[Athena] (SQL-Abfragen über Presto-Engine)
    ↓
[Dashboard] (zeigt Echtzeiteinblicke)

Dead Letter Queue (DLQ) Muster

Wenn die Analytik fehlschlägt:

SQS → [Firehose schlägt fehl]
  ↓
  [DLQ empfängt fehlgeschlagene Nachrichten]
  ↓
  [Alarm an Betrieb gesendet]
  ↓
  [Produktions-API ist nicht betroffen]

Analytik blockiert niemals Benutzeranforderungen. Kinder können lernen, selbst wenn die Analytik-Pipeline ausfällt.

Kostenoptimierungsstrategien

Strategie 1: Dünne Lambdas für hochfrequentierte Endpunkte

  • Typische mobile App führt 10-20 API-Aufrufe pro Sitzung durch
  • 95.000 aktive Nutzer × 3 Sitzungen/Tag × 15 Aufrufe/Sitzung = 4,275M Aufrufe/Tag
  • Wenn jeder Aufruf $0.0000002 kostet (Lambda-Preisgestaltung), sind das $0.86/Tag
  • Reduzierung der Kaltstartzeit um 10s spart ~500 USD/Monat

Strategie 2: RDS Reservierte Instanzen

  • 3-jährige Reservierungsverpflichtung: ~60% Rabatt im Vergleich zu bedarfsgerecht
  • Wir verwenden `db.r6i.xlarge` (4 vCPU, 32GB RAM): $2,800/Monat reserviert vs. $6,500/Monat bedarfsgerecht
  • Jahresersparnis: ~50.000 USD

Strategie 3: Caching

  • Häufig aufgerufene Daten (Curriculum, Inhaltsbytes) im ElastiCache (Redis) zwischengespeichert
  • Reduziert RDS-Abfragen um 70%
  • Kosten: $800/Monat für Cache, spart $2.000/Monat in RDS

Betreiben von 7+ Apps aus einer Codebasis

App Präfix DB-Tabellen Lambda-Stack Status
Amal `amal_` 40+ Tabellen Gemeinsam genutzt Produktion
Thurayya `thurayya_` 40+ Tabellen Gemeinsam genutzt Produktion
Qais `qais_` 35+ Tabellen Gemeinsam genutzt Beta
KidElite `kidelite_` 40+ Tabellen Gemeinsam genutzt Produktion
Alphazed School `school_` 50+ Tabellen Gemeinsam genutzt Beta
Alphazed Montessori `montessori_` 45+ Tabellen Gemeinsam genutzt Intern

Ein Backend, eine Bereitstellungspipeline, 6 gleichzeitige Apps. Neue App-Entwicklung: Wochen statt Monate.

FAQ

F: Hat Lambda nicht ein 15-Minuten-Timeout-Limit? A: Lambda hat ein 15-Minuten-Maximum-Timeout, aber wir benötigen selten langlaufende Anforderungen. Schwerwiegende Workloads (Inhaltsgenerierung, große Exporte) nutzen asynchrone Jobs mit SQS + Step Functions.

F: Was, wenn die Datenbank ausfällt? A: RDS hat Multi-AZ-Failover (primär + Standby-Replikat). Das Failover erfolgt automatisch und dauert ~60 Sekunden. Die Clients erleben kurze Zeitüberschreitungen, aber die Wiederherstellung erfolgt schnell.

F: Wie handhabt ihr die Datenbankverbindungs-Pooling mit zustandslosen Lambda? A: Jede Lambda-Instanz behält einen Verbindungs-Pool bei (wird über warme Aufrufe wiederverwendet). Kaltstarts erhalten neue Verbindungen. RDS Proxy sitzt zwischen Lambda und RDS, um Verbindungsgrenzen zu verwalten.

Verwandte Artikel