Jak jsme programovali pro tisíce uživatelů bez vlastní infrastruktury

Na IHNEDu jsme loni za dva týdny napsali moderní řešení pro zobrazování volebních výsledků – od serverů, přes databáze, po poslední pixel. A hlavně o serverech bude tento článek.
8. 4. 2014

Sdílet

Při úvodní specifikaci, co si vlastně od naší aplikace slibujeme, vyplynuly dvě priority: za prvé, volby budou o prodlouženém víkendu, spousta lidí tedy bude sledovat výsledky na mobilních telefonech. A za druhé, v roce 2013 by už uživatel neměl být nucen neustále mačkat Obnovit, aby dostal aktuální informace.

Mobile-first, precaching článků a různé optimalizace jsou záležitost pro jiný článek, zde se budu věnovat obousměrné komunikaci s velkým množstvím klientů. Kvůli ní jsme zvolili server node.js, hlavně pro knihovnu socket.io, která zajišťuje bezproblémové realtime doručování zpráv mezi serverem a klienty. Jako databáze posloužil redis – dat z voleb je poměrně málo, ale často se mění a ještě častěji jsme k nim chtěli přistupovat. Navíc se dá škálovat jedním řádkem v konfiguraci. A všechno to mělo běžet na Linuxu.

Teď jsme začali řešit, na jakém železe to vlastně celé spustíme. Nejjednodušší možnost byla využít naši infrastrukturu, to ale mělo jeden obrovský háček: nikdo z nás do té doby nevyvíjel websocketovou aplikaci, která by zvládla tisíce současných spojení. Neměli jsme tedy tušení, kolik bude skutečně potřeba výkonu – síťově, procesorově ani pamětí. Nemohli jsme vyloučit, že napíšeme něco, na co naše infrastruktura nebude stačit. A protože tou dobou byly asi 3 týdny do voleb, které kvůli nám neposunou, bylo jasné, že na nějaké přehnané ladění a testování čas nebude.

Začali jsme tedy hledat jednoduše škálovatelné řešení v cloudu. Nejsme kovaní serveráři, spíš bastlíme, co je zrovna potřeba, takže naše primární požadavky byly snadnost obsluhy a ideálně integrovaný loadbalancer. Původně jsme měli v hledáčku Amazon Elastic Compute Cloud, protože jsme na něm počítali dojezdy MHD a věděli jsme, že bez problémů zvládá i stovku paralelně běžících instancí.

O pár dní později jsme se ale náhodou bavili s nejmenovaným evangelizátorem Microsoftích technologií, který nám řekl, že Windows Azure nabízí prakticky totéž, za podobné ceny a s hezčí administrací. Ale že si na rozdíl od Amazonu drží v ČR řadu odborníků, takže pokud se dostaneme do nesnází, tak budeme mít komu zavolat a zeptat se, kudy dál. Jeho ujištění a představa, jak můžu v předvolební pátek – nadávat – radit se s někým, kdo chápe můj problém, mluví mateřštinou a nesedí v bombajském callcentru bylo to, co nás nakonec přesvědčilo o použití cloudu z Redmondu.

Střih, za dva týdny, sobota 26. října, kolem druhé odpoledne, tedy před zavřením volebních místností. Máme téměř dopsáno. Architektura počítá s minimálně třemi instancemi – jedna na sbírání dat z webu ČSÚ, další dvě budou sloužit jako frontendy (a mít vlastní mirror redisové databáze). Frontendy jsou v jedné Cloud Service, tedy za jednou IP adresou a loadbalancerem. Potěšilo nás, že můžeme kdykoliv škálovát dál – prostě spustíme další instanci, nainstalujeme node.js a redis, naklonujeme z gitu a v administraci zvolíme add to load balanced set. Na elegantnější řešení v podobě předpřipravené image bohužel nezbyl čas – mít tak den navíc, říkal jsem si.

Bohužel, jako žádný plán, ani tento nepřežil střet s realitou. Nutné úpravy kódu si berou daň v podobě zpoždění s instalací druhé instance. Asi půl hodiny se tedy jediný server pere s více než dvěma tisíci uživateli. Navíc se kvůli chybě, kterou jsem si uvědomil až po víkendu, stahují články do offline cache i uživatelům dospělého webu, nejen mobilní verze – což zvyšuje zátěž serverů téměř o řád. Přesto small instance drží a pouští do světa odhadem stomegabit po párkilobajtových balíčcích. Nemít plné ruce práce s instalováním, děkuju na střídačku architektům node.js a Azure datacenter.

Ale naše starosti neskončily, ani když jsme konečně spustili druhý frontend, naopak. Počítali jsme totiž s tím, že loadbalancer umí sticky sessions – tedy že jednoho uživatele pošle vždy na stejný server. To je nutné kvůli socket.io – před navázáním spojení proběhne krátký handshake, který ale musí dorazit na stejný server (v defaultním nastavení – a na jiné nebyl čas). Když jsme tedy druhý frontend připojili do loadbalanceru, začaly všechny spojení padat. Na pár desítek vteřin, než jsme zjistili, co se děje a nový server zase vypli, došlo k přerušení služby.

Tou dobou už panovala v newsroomu slušná panika – řeší se, zda bude možná koalice ČSSD, KSČM a Úsvitu a my jsme přitom čerstvě bez možnosti škálování. To, že jsou všechny instance v jedné Cloud Service pod jednou IP adresou je nám najednou na překážku – nemůžeme ani loadbalancovat aspoň střídáním zdrojového URL iframu s aplikací. Mohli jsme ji nastavit jiný port, ale tím bychom odřízli všechny, kdo jsou za paranoidním firewallem s povoleným portem pouze 80.

Naštěstí v Azure instance startují vcelku svižně a instalace softwaru přes apt-get je na jednotky minut. Během asi deseti minut jsme tedy vytvořili úplně novou instanci, nastavili ji replikaci z master redisu a na homepage IHNED přidali javascriptík, který uživatele náhodně rozdělil mezi dvě instance. Protože mezitím narostl i počet uživatelů, vytvořili jsme pak ještě jednu, takže jsme skončili se třemi funkčními frontendy.

Tou dobou konečně zbyl čas na chválu node.js, redisu a Azure: dá se s nimi udělat takové řešení, že změna architektury jde nouzově i za běhu a zabere 40 minut. Většina z toho navíc bylo bušení příkazů do SSH, které se dá eliminovat vytvořením ready-to-deploy image (nebo aspoň install.sh skriptu).

Po volbách jsme se pak postupně dozvěděli i o dalších chybách, kterými jsme si zkomplikovali život. Socket.io se dá nastavit, aby pro autentizaci používal místo vlastní paměti redis, díky čemuž vcelku efektivně funguje i s round-robin loadbalancerem. A Azure zase nabízí Traffic Manager – ten původně slouží k směrování uživatelů na nejbližší (a tím nejrychlejší) datacentrum pomocí dynamických DNS odpovědí. Tento režim ale lze změnit, z performance na round robin, což prakticky vytvoří loadbalancer s sice delším failoverem (rovným TTL odpovědi), zato se sticky sessions.

Mít ten den vývoje navíc, nejspíše bychom dosáhli 100% úspěšnosti obsloužených požadavků. Takhle jsme byli o pár desetin níž, ale s o to silnějším zážitkem – že node.js, Linux a k tomu Microsoft Azure dovedou podržet.

Marcel Šulek

redakční vývojář, IHNED.cz

Upozorníme vás na články, které by vám neměly uniknout (maximálně 2x týdně).