·
7 Min Lesezeit
·
Geschrieben von Tomáš Mikeš
Millionen Fotos von IoT-Geräten auf Azure ingestieren: von der Kamera zur Cloud
Für Fotopast.cloud verarbeiten wir Fotos aus Tausenden Wildkameras. Die Pipeline, die skaliert: Event-Grid-Trigger, Blob-Storage-Tiering, Deduplikation und Kostenoptimierung für Bulk-Uploads.
Ein IoT-Gerät sendet alle Stunde ein Foto. Klingt nach wenig. Mal 5 000 Geräten im Betrieb ergibt das 120 000 Fotos pro Tag. Ein wesentlicher Teil kommt als Batch-Upload einmal täglich (schlafende Tiere fotografiert die Wildkamera nicht, aber bei Abenddämmerungsaktivität sendet sie 40-60 Fotos auf einmal). Dieses Volumen ohne Drops und zu vernünftigen Kosten zu ingestieren, braucht mehr als „einen API-Endpoint, der in S3 schreibt“.
Das ist die Architektur, die wir für Fotopast.cloud gebaut haben und die seit ~18 Monaten ohne Eingriffe außer Scaling läuft.
Phase 1: Ingestion — ein HTTP-Endpoint, der nichts verliert
Das Gerät sendet einen POST mit Multipart — Foto plus JSON-Metadaten (GPS, Zeit, Device-ID, Akku). Der Endpoint muss:
- Innerhalb von 5 Sekunden antworten, sonst retry des Geräts — was Daten verdoppelt
- Auch leicht falsch formatiertes JSON akzeptieren (ältere Firmware) und aufholen
- Auth-Token prüfen — aber Ingestion nicht auf langsame Auth-Checks warten lassen (Pre-Validierung am Edge)
Unsere Lösung: Azure Functions auf Consumption-Plan, HTTP-Trigger. Die Funktion schreibt nur den Blob in einen Landing-Container und gibt 202 Accepted zurück. Keine Business-Validierung, kein DB-Write. Durchschnittliche Latenz 120 ms, P99 800 ms.
Business-Logic läuft später gegen den Landing-Bucket. Wenn Ingestion schlechte Daten toleriert, ist Drop-Rate = 0.
Phase 2: Deduplikation — das gleiche Foto nicht zweimal
Firmware-Edge-Cases senden das gleiche Foto mehrfach. Unsere Lösung: Landing-Blob mit Namen {deviceId}/{photoHash}.jpg. SHA-256 des Binärinhalts. Existiert ein Blob mit diesem Namen, ist Overwrite ein No-op.
Der Hash wird in der Processing-Phase berechnet (nicht im Ingestion — dort zählt Geschwindigkeit), aber jedes Foto wird in der DB nach Hash indiziert. Zwei Einträge desselben Fotos landen nach Hashing auf derselben DB-Zeile und Duplikate verschwinden.
Phase 3: Event Grid — Processing ohne Polling
Event Grid feuert bei jedem Blob-Upload ein Event. Eine zweite Azure Function wird vom Event ausgelöst, nicht durch Polling — „alle 10 s neue Dateien prüfen“ wäre langsam und teuer.
Die Processing-Function:
- Berechnet den Hash, legt DB-Eintrag an (idempotent)
- Erzeugt 3 Thumbnail-Größen (400 px, 1200 px, full) und speichert sie in den Hot-Container
- Extrahiert EXIF-Metadaten (GPS, Zeit)
- Sendet Push-Notification an den Nutzer (falls aktiv)
- Verschiebt den Original-Blob in den Cold-Container (Archiv, günstigerer Tier)
Warum Thumbnails vorab statt on-the-fly? Ein Foto wird ~50-200× gelesen (Listenansicht, Detail, Sharing). Thumbnails einmal erstellen und für immer cachen ist 10-50× günstiger als Server-side-Resize bei jedem Read.
Phase 4: Blob-Storage-Tiering — Kosten fallen um Faktor 3-5
Azure Blob hat drei Tiers:
- Hot: ~$0,018/GB/Monat, Reads sofort. Wir nutzen das für Thumbnails.
- Cool: ~$0,010/GB/Monat, Reads nach ~1 min Wakeup. Volle Fotos < 30 Tage alt.
- Archive: ~$0,002/GB/Monat, 1-15 Stunden Rehydration. Fotos > 1 Jahr alt.
Automatische Lifecycle-Policy: jedes Foto geht nach 30 Tagen nach Cool, nach 12 Monaten ins Archive. Business-Logik: 90 % der Nutzer schauen nur auf die letzten 30 Tage, 99 % schauen nicht auf Dinge älter als ein Jahr — aber wir MÜSSEN sie speichern für Compliance / Beweise.
Für Fotopast hat das die Storage-Kosten nach dem ersten Betriebsjahr um 60 % gesenkt — von ~$1 200/Monat auf ~$480/Monat.
Phase 5: Backpressure und Rate-Limiting
Ein einzelnes Gerät mit fehlerhafter Firmware beginnt 100 Fotos pro Minute zu senden. Ohne Schutz würde das die Queue füllen und Processing für alle anderen verlangsamen. Device-Level-Rate-Limit:
- Redis-Counter pro deviceId am Ingestion-Endpoint
- Limit 200 Fotos / 5 min. Überschreitung = 429 Too Many Requests zurück (Gerät versucht später erneut)
- Alert, wenn 10+ Geräte das Limit überschreiten (möglicher fleet-weiter Firmware-Bug)
Die Ergebnisse in Zahlen
Nach 18 Monaten Fotopast-Betrieb:
- ~45 Mio. Fotos im System
- ~120 TB gesamt (Großteil im Archive-Tier)
- Ingestion-Durchsatz Peak 500 Fotos/Minute
- P99-Latenz API-End-to-End: 950 ms
- Azure-Kosten ~$600/Monat (inkl. Compute und Storage)
- 0 verlorene Fotos in 18 Monaten
Was wir anders machen würden
Drei Lehren für das nächste IoT-Projekt:
- Dead-Letter-Queue vom ersten Tag. Wir haben sie nach einem Monat hinzugefügt, nachdem wir erste Edge-Case-Daten sahen.
- Lifecycle-Policy an Tag 1, nicht Tag 60. Einen Backlog bestehender Fotos in Cool-Tier zu verschieben ist teurer als upfront richtig einzustellen.
- Per-Device-Metriken monitoren, nicht nur System-Level. Aggregat „500 Fotos/Min“ ist ein gesunder Durchschnitt. Aber 10 Geräte je 50 Fotos/Min = fehlerhafte Firmware, die Sie nur per-Device finden.
Sonst — 2 Monate Lieferung und 18 Monate Betrieb ohne Eingriff — ist das eine Architektur, die sich nicht geändert, sondern nur skaliert hat.
Arbeiten Sie an etwas Ähnlichem?
Vereinbaren Sie ein 30-minütiges technisches Gespräch. Kein Vertriebsprozess — direktes architektonisches Feedback.
Unser Service:
Systeme bauen, die skalieren — ohne Engpässe →