·
7 min čtení
·
Napsal Tomáš Mikeš
Ingestion milionů fotek z IoT zařízení na Azure: od kamery po cloud
Pro Fotopast.cloud zpracováváme fotky z tisíců fotopastí. Popis pipeline, která škáluje: Event Grid triggery, blob storage tiering, deduplikace a cost optimization pro bulk upload.
IoT zařízení posílá fotku jednou za hodinu. Zní jako malý objem. Vynásobeno 5 000 zařízeními v provozu to je 120 000 fotek za den. Podstatná část pošle celý dávkový upload 1× denně (spícího zvířete neví fotopast nafotit, ale při podvečerní aktivitě posílá 40-60 fotek najednou). Pipe ingestion takového objemu bez drop zpráv a s rozumnými náklady vyžaduje víc než „API endpoint, který zapisuje do S3“.
Tohle je architektura, kterou jsme postavili pro Fotopast.cloud a která běží ~18 měsíců bez zásahu kromě scalingu.
Fáze 1: Příjem — HTTP endpoint, který neztrácí
Zařízení posílá POST s multipart bodu obsahujícím fotku + JSON metadata (GPS, čas, device ID, baterie). Endpoint musí:
- Odpovědět do 5 sekund, jinak zařízení retry — což zdvojuje data
- Akceptovat i špatně formovaný JSON (starší firmware verze) a dohoně
- Ověřit auth token — ale neblokovat ingestion na pomalé auth check (pre-validace na edge)
Naše řešení: Azure Functions s Consumption plánem, HTTP trigger. Funkce jen zapíše blob do landing containeru a vrátí 202 Accepted. Žádná validace business logiky, žádný DB write. Průměrná latence 120 ms, P99 800 ms.
Business logika se dělá později z landing bucketu. Když ingestion zvládne i špatná data, drop rate = 0.
Fáze 2: Deduplikace — stejná fotka nemá být 2×
Firmware zařízení v edge cases odešle stejnou fotku vícekrát. Naše řešení: landing blob pojmenovaný jako {deviceId}/{photoHash}.jpg. SHA-256 z binárního obsahu fotky. Pokud blob s tím jménem existuje, overwrite se projeví jako no-op.
Hash se počítá až v processing fázi (ne v ingestion — tam je rychlost key), ale v databázi každou fotku indexujeme podle hashe. Dva záznamy stejné fotky se po hashování dostanou na stejný řádek DB a duplicita zmizí.
Fáze 3: Event Grid — processing bez polling
Event Grid emituje event při každém blob upload. Druhá Azure Function se spouští eventem, nikoliv polováním — „v 10 s intervalu zkontroluj, co je nové“ by bylo drahé a pomalé.
Processing function:
- Spočítá hash, vytvoří DB záznam (idempotentně)
- Vygeneruje 3 thumbnail velikosti (400 px, 1200 px, full) a uloží do hot containeru
- Extrahuje EXIF metadata (GPS, čas)
- Posílá push notifikaci uživateli (pokud má zapnutou)
- Přesune původní blob do cold containeru (archiv, levnější storage tier)
Proč thumbnaily ne on-the-fly? Protože jedna fotka se čte ~50-200× (app listing, detail, sdílení). Vytvořit thumbnaily jednou, cachovat navždy, je 10-50× levnější než server-side resize při každém čtení.
Fáze 4: Blob storage tiering — cena klesne 3-5×
Azure Blob má tři tiery:
- Hot: ~$0.018/GB/měs, čtení instantně. Používáme pro thumbnaily.
- Cool: ~$0.010/GB/měs, čtení po ~1 minutě wakeup. Původní plné fotky mladší 30 dní.
- Archive: ~$0.002/GB/měs, rehydration 1-15 hodin. Fotky starší 1 roku.
Automatický lifecycle policy: každá fotka jde po 30 dnech do Cool, po 12 měsících do Archive. Business logic: 90 % uživatelů se dívá jen na posledních 30 dní, 99 % se nedívá na věci starší roku — ale ukládat MUSÍME pro compliance / dokazování.
Pro Fotopast to snížilo storage náklady o 60 % po prvním roce provozu — z ~$1 200/měs na ~$480/měs.
Fáze 5: Backpressure a rate limiting
Jedno zařízení s buggy firmware začne posílat 100 fotek za minutu. Bez ochrany by to zaplnilo queue a zpomalilo processing pro ostatní. Řešení na device-level rate limit:
- Na ingestion endpointu Redis counter per deviceId
- Limit 200 fotek / 5 min. Překročení = vratím 429 Too Many Requests (zařízení retry později)
- Alert na monitoring, když 10+ device překročí limit (možná firmware bug v fleetu)
Výsledná čísla
Po 18 měsících provozu (Fotopast):
- ~45 M fotek v systému
- ~120 TB celkem (většina v Archive tieru)
- Ingestion throughput peaky 500 fotek/minutu
- P99 latence na API end-to-end: 950 ms
- Azure cost ~$600/měs (včetně compute a storage)
- 0 ztracených fotek za 18 měsíců
Co bychom udělali jinak
Tři poučení, která si odnáším do dalšího IoT projektu:
- Queue s dead-letter od prvního dne. My jsme ho přidali po měsíci provozu, když jsme viděli první edge case data.
- Lifecycle policy nastavená den 1, ne den 60. Move do Cool tieru backlog existujících fotek je dražší než správně nastavit napoprvé.
- Monitoring na device-level metriky, ne jen system-level.Agregát „500 fotek/min“ je zdravý průměr. Ale 10 devices posílajících 50 fotek/min každé = buggy firmware, který najdeš jen per-device.
Jinak — za 2 měsíce dodávky a 18 měsíců provozu bez zásahu — je to architektura, která se za celou dobu nezměnila, jen škálovala.
Řešíš něco podobného?
Domluvme si 30min technický call. Bez obchodních procesů — přímá architekturní zpětná vazba.
Naše služba:
Systémy, které škálují — bez bottlenecků →