Reklamní kampaně v televizi stojí nemalé peníze, zvlášť při událostech jako je mistrovství světa v hokeji. Když už do nich investujete, nechcete, aby se lidi dostali k hlášce „Tento web je momentálně nedostupný“ a naštvaně odešli. Potřebujete funkční web. Právě to byla naše mise, když nás o reklamní kampani při MS v hokeji informoval náš klient Carvago. A tak přišlo na řadu testování, co platforma ustojí, a hledání cesty, jak vylepšit infrastrukturu, aby ustála ještě víc.
Nejdřív potřebujeme znát rozdíl mezi dvěma důležitými pojmy: ATL a BTL.
BTL
Techniky BTL – Below The Line – jsou ty, které asi běžně znáte, protože je to obvykle první krok v marketingu menších projektů. Jsou zaměřené na menší skupiny uživatelů nebo na konkrétního uživatele. Typicky jsou to SEO, PPC, remarketing, e-mailové kampaně apod. Jen si vzpomeňte, jak na vás všude vyskakují bannery, jakmile dáte do vyhledávače nějaký produkt.
ATL
Oproti tomu ATL neboli Above The Line jsou komunikační techniky určené pro masy. Typicky jsou to televizní spoty, reklama v rádiu nebo billboardy.
Když spouštíte BTL kampaně, například PPC, infrastrukturu obvykle neřešíte. BTLka totiž návštěvníky přivádějí na web postupně. Když máte platformu na Kubernetes, díky HPA (Horizontal Pod Autoscaler) web vyšší návštěvnost ustojí. Proces funguje tak, že autoscaleru nastavíte pravidla, za jakých okolností se má škálovat dolů nebo nahoru, a automaticky se přidá další container (Pod).
Při ATL ale váš web musí ustát jednorázový nápor návštěvníků během pár desítek sekund. Situace ze života: váš CEO se objeví v rozhovoru v televizi. Co se stane? Tisíce lidí se najednou chtějí podívat na tu firmu, o které mluví. A pokud na to platforma není ready, váš web spadne.
V našem případě rozhovoru klienta v televizi stoupla instantně vytíženost CPU z 50 na 100 procent. V tu chvíli pody přestaly odpovídat. HPA sice nasadilo nové, ale i cloudu chvíli trvá, než nastartují další servery, než se warmupne cache, než se vytvoří nové spojení do databáze, redisu apod.
Běželi jsme na 12 podech, HPA začalo startovat dalších 6. Ale než se 6 nových podů dostalo do “ready” stavu, ty původní přestaly kompletně odpovídat, protože byly přetížené.
Kvůli přetíženosti přestaly odpovídat na health-checky a loadbalancer je vyřadil z poolu. Následně naskočilo 6 nových podů, ty ale smetla vlna návštěvníků.
Výsledek byla nedostupná aplikace a frustrovaní uživatelé.
Naštěstí máme postavený silný monitoring, takže všem lidem z týmu začaly vyzvánět telefony s kritickými notifikacemi. Nejdřív jsme zjišťovali, jestli se nejedná o útok, co je příčina trafficu, a pak jsme manuálně zasáhli do konfigurace počtu podů.
Nastavili jsme počet podů na 48 (oproti původním 12) a aplikace začala fungovat bez problému i s takhle vysokou návštěvností.
Zkrátka a dobře HPA nebylo schopno zareagovat dostatečně rychle a intenzivně.
Z Carvaga k nám přišla na začátku roku jasná zpráva: bude reklama na mistrovství světa v hokeji a platforma na to musí být ready. Náš DevOpsový tým se proto pustil do příprav.
Otázka pro věštkyni, ale nějaký odhad jsme mít museli, jinak by reklama přišla vniveč. A tak jsme se dali do počítání. Opět jsme si z marketingu vypůjčili termín, a to GRP.
GRP (Gross Rating Point) je ukazatel zásahu kampaně v široké cílové skupině (což jsou obvykle lidi starší 15 let). Pokud tedy bude mít TV kampaň dva spoty, jeden se sledovaností 10 % a druhý 13 %, dosáhne na 23 GRPs.
Česko je hokejový národ, a tak se GRP u jedné reklamy vyšplhá klidně i na hodnotu 50. Potřebovali jsme tedy vymyslet, kolik lidí přijde na carvago.com. Interně jsme si dali cíl 5–10 tisíc aktivních uživatelů.
Pro naše potřeby testování jsme si určili několik možných scénářů, které mohou nastat po odvysílání reklamy:
Původně jsme chtěli simulovat návštěvnost najednou s rozložením napříč těmito scnáři. Vzhledem k tomu, že testování bylo komplikovanější, než jsme si mysleli, nakonec jsme skončili jenom u nejnáročnějšího scénáře – „Chci se podívat na nabídku aut“.
Následoval výběr nástroje, který nám pomůže nasimulovat velký počet návštěv. Prošli jsme jich celou řádku.
Otestovat jen API je rychlé a také relativně svižně škáluje. Jak ale dát dohromady počet endpointů, resp. kolikrát je budeme volat?
Cypress testy sice máme hotové, ale pouští se headless chrome, který žere neskutečně moc zdrojů. Takže na jednom stroji pustíme tak maximálně 5-10 instancí vedle sebe.
Naše testy v Datadog Syntheticu. Jsou hotové, tak je pojďme použít. Problém? Zvládnou maximálně 15 uživatelů za 5 minut.
Vždyť na Carvago platformě procházíme každý den 15 milionů inzerátů a tady jde o podobný princip. Zjistili jsme, že tudy cesta taky nepovede, protože Apify prochází všechno během celého dne. Simulace jednorázového nárazu by byla drahá a náročná.
JMeter nám poradila externí firma. Je to poměrně šikovný nástroj s dobrou konfigurací – kolik lidí chceme, v jakých scénářích apod. Je určený primárně pro API, ale má i WebDriver pro Google Chrome.
To všechno znělo super. Problém ale byl, že to zase potřebuje pustit browser a těch potřebujeme tisíce, což je hodně serverů.
LoadRunner je enterprise řešení. Bez problému zvládne námi požadovaných 10 000 virtuálních uživatelů. Problém byl v tom, že za každého uživatele se pro jeden test platí 0,15 $. Tedy jedno otestování by stálo 3 000 $.
Loadero nám vyhovovalo cenově, testy se píšou v JavaScriptu. To znělo dobře. Testy ale bohužel nebyly příliš spolehlivé.
Hey umí hodně muziky za málo peněz. Je to takový modernější Apache Benchmark. Má ale velké omezení. Umí poslat spoustu requestů, jenže pouze na jeden endpoint.
Využili jsme ale toho, že frontend používá SSR (Server Side Rendering), tj. doptává se na spoustu dat voláním více API requestů. Takže jsme zkrátka provolávali frontend, který zase provolával několik API requestů.
Pojďme ale od teorie pryč a podívejme se, jak proběhly performance testy.
První performance test
Nainstalovali jsme JMeter na dva servery s 8 GB paměti a zkusili pustit maximální počet requestů. Bohužel jsme byli schopni nasimulovat vyšší desítky aktivních uživatelů.
Před ukončením testu v daný večer jsme to celé zkusili ještě sundat přes Hey a povedlo se nám to už na 400 r/s.
Druhý performance test
Tentokrát jsme nainstalovali JMeter na 10 serverů, dohromady asi 50 GB paměti. Pomocí JMeteru jsme byli schopni nasimulovat pár set líných uživatelů, což bylo od našeho cíle 10 000 uživatelů furt na mílové kroky daleko.
Web těch pár set líných uživatelů v pohodě ustál.
Třetí performance test
Opustili jsme od JMeteru, protože by bylo nesmyslně složité vygenerovat zátěž v řádech tisíců uživatelů na našem železe. Sáhli jsme tedy po Loaderu, který to celé má zautomatizované.
Dohromady se nám pomocí Hey a Loadero povedlo nasimulovat tisíc uživatelů. Vzhledem k tomu, že už předtím jsme to sundali na nějakých 400 r/s, rozhodli jsme se pro naprosto nesmyslné naškálování celé infrastruktury.
Na backendové API jsme pustili 64 containerů a frontendovou aplikaci se Server-Side Renderem jsme spustili v 280 containerech. To nám dohromady dalo 732 v CPU a 3 TB paměti.
A jak si hromada železa poradila s 1 000 uživateli? Neporadila. Celé to umřelo a aplikace byla nepoužitelná.
Díky tomuto testu jsme ale získali hromadu informací o tom, co přestává stíhat, a co je potřeba ještě poladit.
Elasticsearch
Jeden z bottlenecků, který jsme našli, byl Elasticsearch, který nezvládal vyřizovat požadavky dostatečně rychle. Z nějakého už neznámého důvodu jsme původně používali Memory Optimized instance, tak jsme je vyměnili za computed instanci a vyřešili jeden problém.
Backendové pody při zátěži umíraly
Stávalo se nám, že jakmile jsme poslali více requestů na jeden pod, tak po chvíli umřel na Sigterm. Nejdříve jsme si mysleli, že to způsobuje Segfault PHP, takže jsme prošli všechny extensions a zjistili, že tím to není.
Problém byl v tom, že jsme měli společný FPM pool pro uživatelské requesty a zároveň i pro health checky z loadbalanceru. Uživatelskými požadavky jsme vybrali komplet všechny FPM workery a tím pádem pod neodpověděl na health-check, byl vyřazenej a dostal Sigterm z venku.
Řešení bylo jednoduché: oddělit FPM pooly.
Čtvrtý (finální) performance test
Všechny optimalizace zajistily to, že jsme byli ready na 1 000 uživatelů. Ale chtěli jsme posunout hranice dál k desítkám tisíců.
V průběhu testu jsme odhalili další řadu problémů.
DNS Servery
V rámci Kube Clusteru běží pody pro překlad DNS adres (CoreDNS). Typicky containery se pojí s hromadou dalších služeb pomocí domén a tudíž potřebují DNS server, aby se dokázaly připojit k databázi, Elasticsearch, Redisu atd. Měli jsme jenom dva miniaturní pody a ty velké množství requestů nezvládly, takže jsme je navýšili. Zjistit to ale až při živé návštěvnosti, měla by tahle pitomá drobnost fatální následky.
Databáze RDS PgSQL
V AWS má databáze omezené množství IOPS na den závislé na velikosti úložiště. Pokud tohle množství vyčerpáte, databáze se znatelně zpomalí.
Řešením je buďto přejít na jiné úložiště, AWS nabízí ještě Provisioned IOPS, ale to vychází cenově znatelně dráž. Nebo u General Purpose SSD navýšit velikost úložiště. Navyšování je bezvýpadkový proces, bohužel snížit bez výpadku se nedá.
Proto jsme zvolili AWS Aurora, čistě Amazonní technologii. Je kompatibilní s klasickým PgSQL, ale úložiště má „neomezené“ IOPSy. Prostě zaplatíte za tolik IOPSů, kolik jich spotřebujete.
Touto migrací jsme vyřešili problém zpomalení databáze při jejím větším zatížení.
RDS Proxy
Na databázi jsme zaznamenali ještě jeden zádrhel, a to problém při vytváření spojení. Na RDS jsme měli nastaven limit na 5 000 spojení, ale jakmile jsme se jich snažili otevřít více než 500, každé nové otevření trvalo delší a delší dobu, až jsme se dostávali na hranici jednotek vteřin.
Detailně je tento efekt popsán na tomto blogu. Pro nás to znamenalo, že jsme mezi databázi a aplikaci vložili ještě RDS proxy.
RDS Proxy držela otevřené spojení vůči databázi a PHP se pojilo k Proxy, což vyřešilo problém s rychlostí otevření spojení.
Po všem testování a přípravách nastala minuta M a proběhla ohlášená reklama. Obstáli jsme?
Ustáli jsme to na pohodu. Na web carvago.com přišlo díky reklamě 7 000 lidí. Původní odhad byl 1 500. Aplikace má navíc docela vtipnou featuru. Díky RDS proxy vznikne efekt, že čím vyšší je počet requestů, tím je aplikace rychlejší. Z 300 ms jsme se dostali na 150 ms.
Naše testy byly naddimenzované na 10 000 uživatelů, ale bylo to tak správně. Díky tomu jsme platformu připravili na nápor návštěvníků během dalších reklam, které jsme ani neměli nahlášené nebo které se nečekaně posunuly. Platforma si s nimi bez problémů poradila, a tak jsme ani nemuseli držet nad výkonem infrastruktury stráž.
Takže až příště budou vaši marketéři přemýšlet o reklamní kampani, nezapomeňte je spojit s vývojáři a hlavně DevOpsáky. Vytěžíte reklamu na maximum a ušetříte si spoustu nelichotivých tweetů.