Varázslatos proxy megoldás

DONE.

Sziget Fesztivál

Az új infrastruktúra és a varázslatos proxy megoldás

Techtalk
2021.07.22. clock 17 perc

A Sziget Fesztivál látogatószám alapján az egyik legnagyobb európai fesztivál. A Sziget mögött álló cégcsoport több kisebb fesztiválért is felelős, mint például a Volt, a Balatonsound, a Strand Fesztivál és a Gourmet Fesztivál. Az összesített látogatószám évről évre növekszik, a Sziget Fesztivál 2019-ben több mint 500 ezer „offline egyedi látogatót” vonzott. A digitális jelenlét hatalmas hangsúlyt kap ezeknél az eseményeknél, mivel a látogatók többsége a weboldalon találkozik először a márkával és annak „érzésével”. A Sziget 2010 óta ugyanazt az alap infrastruktúrát használja, de mivel a látogatói viselkedés és használat az utolsó néhány évben jelentősen megváltozott, lépést kellett tartaniuk a szükségletekkel, és itt ragyogott a mi csapatunk. De térjünk vissza a kezdetekhez.

2017-ben kezdtünk el dolgozni a Szigettel a weboldalon lévő programok megjelenítésének javításán, amit program widgetnek hívunk. Ennek eredményeként egy kis, de hatékony React alkalmazást készítettünk, amely még mindig egyetlen JSON fájlt használt a különböző nézetek megjelenítésére – helyszínek szerint, előadók szerint, stb. 2018-ban további kiterjesztést végeztünk egy újabb, React alapú widgettel, amely a fesztivál információit jeleníti meg a látogatók számára. Abban az évben végeztük el az összes frontend munkát is a fesztiválokon. 2019-ben még tovább léptünk, weboldal dizájnt, UX munkát a webshopon és egy extra infrastruktúrát biztosítottunk, amiről itt részletesen beszélni fogunk. És talán minél kevesebb szó esik a 2020-as fesztiválszezonról, annál jobb.

Ahogy korábban említettük, a látogatói viselkedés 2010 óta jelentősen megváltozott. A trend eltolódott az asztali gépekről a mobil eszközökre, az alacsony sávszélességről a gyors internetkapcsolatra, és csökkent a türelem, amivel a látogatók vártak a teljes oldal betöltésére. Amikor elkezdtünk dolgozni a Szigettel, a rendszerre vonatkozó percepcióink egyszerűek voltak. Volt egy saját szerverünk, amely a program widget működéséhez szükséges háttérmunkát végezte. Tudtuk, hogy van egy CMS szerver, amely kezeli az összes weboldal forgalmat, és egy háttérszerver a mobil alkalmazáshoz. Mivel a program widgetben jegyárakkal foglalkoztunk, hozzáférést kaptunk néhány belső webshop felülethez, így minden egyszerűnek tűnt.

Weboldal backend

Az első közös szezon után sikerült megmutatnunk a Szigetnek, hogy képesek vagyunk skálázható, stabil rendszerek fejlesztésére alacsony erőforrás-használattal, így a második szezonra lehetőséget kaptunk, hogy belesüthessünk a háttérbe, és láthassuk, hogyan néz ki a valódi infrastruktúra. A következő ábra egy egyszerűsített nézetet mutat az összes különböző rendszerről, de lefedi az alapvető fogalmakat is.

Webshop architektúra

Mindenütt összekapcsolt rendszerek voltak, néha elkülönítve és skálázhatóan, néha egyetlen példányra korlátozva, mint például a Site CMS, amely a legnagyobb forgalmat szolgálta ki.

Amikor elkezdtünk mélyebben ásni a CMS-ben, érdekes mérnöki megoldásokat találtunk. Bár a CMS rendelkezik egy publikálási folyamattal a webes tartalom generálására, a generált fájlok PHP és HTML keveréke, ami minden kéréshez egy kis feldolgozást igényelt. A statikus tartalom ugyanarról a szerverről érkezett, megfelelő cache policy-vel.

A használt szerver (és a mai napig is használt) egy Apache 2.4 mod_php és PHP 5.6, ami 2010-ben még megfelelő lehetett mérsékelt használat mellett, de napi több száz látogatóval nem tudott lépést tartani a kérések számával és sávszélességgel. A CMS szerver a webshop kéréseket más példányokra továbbította, de mivel az Apache 2.4 nagyon erőforrásigényes, voltak kiesések, részben konfigurációs problémák miatt – például nyilvános Google DNS szerverek használata a webshop háttér IP-címek feloldására.

Mint minden komplex és ad-hoc rendszernek, ennek is megvan a saját története. A Sziget próbálja elkülöníteni a felelősségeket a szolgáltatásokban, ami több csapat és alvállalkozó munkáját eredményezte minden rendszerben. A következő ábrán minden szín egy külön csapatot képvisel, és igen, a Site CMS annyira színes, amennyire csak lehet. Az összes szerver fizikai, nem rendelkezik hot-replacement lehetőséggel, ami önmagában is kockázatos, ha fizetéssel is foglalkozunk.

Weboldal architektúra

Nagyon nehéz bármit is változtatni egy olyan rendszerben, amelynek annyi függősége van technikai és csapat szinten egyaránt. 2018-ban minden csapatot arra ösztönöztünk, hogy legalább használják a Cloudflare-t a szolgáltatások előtt, de a rendelkezésre álló cache és proxy megoldásokat csak a Sziget fesztivál második napján sikerült implementálni – miután a képeket nem lehetett betölteni a mobil alkalmazásban a sávszélességi korlátok miatt. Ez egy dicsőséges nap volt a skálázhatóság szempontjából, még mindig megvan a képernyőmentés a váltásról.

Sávszélességi korlátok

Az első napon a képeket közvetlenül a szerverről szolgálták ki, a sávszélesség körülbelül 500 Mbit/s-ra volt korlátozva, ami csúcsidőben nem volt elegendő. A második napon a Cloudflare átvette a nehéz munkát, napi 60 GB-ot szolgált ki délig, éjszaka átlagosan közel egy gigabit sebességgel. Gyors emlékeztető, ezek „csak” statikus képek az előadókról a mobil alkalmazásban, egy átlagos fesztivál napon, semmi más. A Cloudflare cache találati arány nem volt túl jó a CMS számára, mivel a publikálási folyamat során néha új tartalommal rendelkező azonos fájlnevek keletkeztek, így nem tudtunk teljes mértékben támaszkodni a cache policy-ra.

Az érvénytelenítési probléma még mindig fennállt, még mindig megoldatlan volt, a Cloudflare cache törlés gombjának megnyomása nem oldotta meg a böngésző oldali érvénytelenítést, ahogy mindannyian tudjuk. 2018-ban végre hozzáférést kaptunk a weboldal forgalmi statisztikáihoz, a Sziget Fesztivál esetében az adatok a következők voltak:

  • 500K egyedi látogató havonta (fesztivál szezonon kívül)
  • 880K egyedi látogató fesztivál hetében
  • 5.3M oldalmegtekintés havonta (fesztivál szezonon kívül)
  • 1.5M oldalmegtekintés egy átlagos fesztivál napon
  • 2.4 TB forgalom havonta (fesztivál szezonon kívül)
  • 13 TB forgalom a fesztivál alatt
  • Átlagosan 240 Mbit/s forgalom a fesztivál alatt

A 2018-as fesztiválszezon után szuper megbeszélést tartottunk a Sziget csapatával, és javasoltunk egy megoldást, amely szerintünk megoldhatja a problémát, viszonylag kisebb erőbefektetéssel a többi csapat számára.

A célok világosan meg voltak határozva:

  • Az infrastruktúra kiterjesztése (csak kisebb változtatások lehetségesek)
  • Anélkül, hogy megszakítanánk a csapatok munkafolyamatát
  • Skálázás, forgalom, tartalom érvénytelenítési, CDN kihívások megoldása
  • Alacsony karbantartási és infrastruktúra költségek
  • Gyorsabb oldalbetöltési idő, elégedett látogatók
  • Ugyanaz a megoldás az összes fesztiválhoz

Egyszerűnek tűnik, igaz? Legalább zöld jelzést kaptunk egy koncepció bemutatására, ha a költségek ésszerűen alacsonyak.

Kezdtünk egy egyszerű benchmarkkal a Volt Fesztivált felhasználva, 100 párhuzamos látogatóval, egyszerű folyamatot követve, Cloudflare nélkül, közvetlenül a CMS szerverhez.

Válaszidő grafikon

Válaszidő adatok

Még 100 párhuzamos munkamenet esetén is a szerver hibákat kezdett dobálni, és az átlagos terhelés 25-re emelkedett (32 magos gépen). Az oldalbetöltési idő 15 másodpercre nőtt, és sikerült DDoS-olni a szervert 200 egyidejű kapcsolattal. Ez egy jó kezdésnek bizonyult.

Már tudtuk, hogy skálázhatóbb és stabilabb megoldásra van szükség. Egyszerű választás lehetett volna mindent a felhőbe költöztetni, és elkezdeni a skálázást, de ez rengeteg munkát igényelt volna az összes csapattól, mivel az integrálás ebbe a területbe nem könnyű, ha a rendszered nincs felkészülve a vertikális skálázásra.

Egy másik lehetséges megoldás volt egy extra proxy réteg hozzáadása minden elé. A proxy skálázható, felhő alapú lehet, és talán megoldhatja a problémákat. Azonban az egyik cél korlátozta a lehetőségeinket: alacsony infrastruktúra költségek.

Miután értékeltük a „nagy szereplők ajánlatait” statikus tartalom szolgáltatása, némi dinamikus feldolgozás és szilárd proxy megoldás tekintetében a webshop háttérszervereihez, arra a következtetésre jutottunk, hogy az AWS (CloudFront, S3, néhány Lambda funkció) lehet a legolcsóbb megoldás, de a számok még mindig 5000+ USD/hónap körüli tartományban maradtak, bármennyire próbáltuk is csökkenteni a költségeket, a sávszélesség ezen a skálán elég drága.

A Cloudflare abban az évben vezette be a workers-t, és azon gondolkodtunk, hogy használjuk KV-vel, de néhány korlátozás megakadályozta, hogy olyan szép proxy réteget hozzunk létre, ami megfelelt volna az igényeinknek. Nevezetesen nem volt módunk kontrollálni a proxy kapcsolatok számát az eredeti szerverekhez (a webshophoz és a CMS-hez), így magunkat DDOS-olhattuk volna. A KV rétegnek vannak más érdekes jellemzői is, mint például az egyedi kulcsok maximum 2 MB-ig tárolhatók (ami nem elég néhány cache-elt tartalom tárolásához), van egy írási limit: 1 írás per másodperc per kulcs, ami megölte volna az álmainkat – 1 másodperccel a publikálási folyamat után. A költségek körülbelül 100-200 USD/hónap körül lettek volna, de a korlátozások miatt ezt az ötletet félretettük.

Az Nginx LUA skriptekkel varázslatot tud véghezvinni a megfelelő kezekben, de miután egy sikertelen próbálkozás után nem találtunk Nginx LUA szakértőt Magyarországon, feladtuk ezt az ötletet is. Az utolsó próbálkozásunk, hogy megtaláljuk a megoldást az szinte lehetetlen célok kielégítésére, az volt, hogy keresünk egy VPS szolgáltatót, amely legalább biztosítja a nyers erőt és sávszélességet, hogy létrehozzunk egy proxy réteget a Cloudflare és az eredeti szerverek között. Így azt tettük, amit minden tapasztalt fejlesztő tenne. Google: „korlátlan sávszélességű VPS Európa”.

Contabo

Így üdvözölhettük a Contabo-t. Korlátlan forgalom szép gépekkel, hihetetlenül olcsó áron. Kiválasztottuk a VPS L és VPS S csomagokat, a havi 20 EUR ésszerűen alacsony árnak tűnt, ami 3,33 EUR fesztiválonként havonta. De a neheze még hátra volt, egy proxy megoldás létrehozása, amely képes kihasználni az összes sávszélességet, és javítani a Cloudflare cache találati arányát a csúcsforgalom kezelésére.

Így hát elkezdődött a kalandunk, egy varázslatos proxy megoldás írásával, amely megold egy nagyon specifikus problémakört.

Weboldal varázslat

 

Az ötlet az volt, hogy kiterjesztjük az infrastruktúrát ezzel a varázslatos proxy-val, amely képes cache-elni és kezelni a statikus és dinamikus tartalmat, és segít az érvénytelenítésben minden publikáláskor. Emellett proxyzni kell a webshop kéréseket a webshop szerverekhez, korlátozott számú kapcsolaton keresztül.

A javasolt infrastruktúra így nézett ki:

Cloudflare implementáció

Node.js-t használtunk, mivel nagyszerű, aszinkron, gyors, könnyen telepíthető és kezelhető. A specifikus igényeket különböző modulokkal kezeltük a megoldásunkban, kezdve a statikus tartalmakkal, érvénytelenítéssel és a publikálási folyamattal.

Kód részlet

A CMS publikálási folyamata HTML kódot generál és a statikus tartalmakat meghatározott könyvtárakba másolja, nevezetesen /<lang>/themes/… és /<lang>/cache/… . A cache könyvtárban néhány fájl tartalmaz hash-t, de a képek ugyanazzal a névvel rendelkeznek, amelyet a CMS felhasználó használt a feltöltés során. Expire vagy ETag ellenőrzés nélkül a böngészők nem tudják, hogy ezek a tartalmak változtak-e vagy sem. A statikus tartalom ugyanarról a domainről érkezett, mint a HTML, ami korlátozta a párhuzamos kapcsolatok számát a böngészők és a szerver között.

Cloudflare kód részlet

Az ötletünk az volt, hogy minden fesztiválhoz egy aldomain-t vezessünk be, hogy megfelelő CDN megoldást hozzunk létre, és a forráneveket egy hash értékkel egészítsük ki, amely minden publikálás után változhat. Így meg tudjuk mondani a böngészőknek, hogy minden URL örökké ugyanazt a tartalmat tartalmazza, így azok végtelenül cache-elhetők. Egyszerű, ehhez az HTML-t újra kellett írni a varázslatos proxy-nkban néhány regex segítségével.

Az eredeti URL-ek is elérhetők lesznek, ha valami még mindig arra hivatkozik, például a JS-ből történő kliens oldali források betöltése.

A rewrite előtti URL-ek így néztek ki:

https://szigetfestival.com/en/themes/frontend/sziget/assets/sziget/css/prod.min.css

A rewrite után:

https://cdn2.szigetfestival.com/tc2kje/f851/css/prod.min.css

Ahogy látható, eltávolítottuk a redundáns részt (/…/themes/frontend/sziget/assets/…) és a hash második részében tároltuk (f851), mivel ezekből a könyvtárakból csak korlátozott számú volt a szerveren. Az első rész „t….” tartalmazza a publikálási esemény dátumát és idejét.

Mivel Cloudflare-t használunk a magic proxy előtt, megadhatjuk Cloudflare-nek, hogy végtelen ideig cache-elje az adott aldomain összes kérését, és a backend „figyelmen kívül hagyhatja” a közzététel dátumát, letöltheti az eredeti erőforrást, és hagyhatja, hogy a Cloudflare kezelje a többit. Kis mellékhatás, hogy még ha létrehozol egy „hamis” közzétételi hash-t, akkor is kiszolgáljuk azt a tartalmat, így például a https://cdn2.szigetfestival.com/t_any_random_string/f851/css/prod.min.css is működik. De a cél az volt, hogy minden egyes közzététel után érvénytelenítsük, és végtelen ideig cache-eljük minden lehetséges rétegen (Cloudflare és böngésző).
Mivel HTML tartalom cache-elési szolgáltatásként is szeretnénk működni, minden választ a magic proxyban kell tárolnunk, újraírás után. Sajnos Cloudflare itt nem tud annyit segíteni, mivel egyes kérések dinamikus funkciókat váltanak ki, mások teljesen statikusak, megint másokat a webshop backendekhez továbbítanak. De minden statikus erőforrást egyszer kérhetünk le az eredeti szerverről, és a választ (ha nem 4xx, 5xx) memóriában tároljuk. Minden azonos erőforrásra vonatkozó kérés memóriából szolgálható ki, és minden közzététel után érvénytelenítjük a memóriacache-t.
Egy ügyes trükköt is bevetettünk, hogy a böngészők cache-eljék a generált statikus HTML tartalmat, amíg új közzétételi folyamat nem fut. Minden HTML válaszhoz ugyanazt az ETag-et (a közzétételi időbélyeget) rendeltük, így a böngészők „If-None-Match” kérést tehetnek. A backend két kódsora ellenőrizheti, hogy a cache-elt tartalom még érvényes-e:
if (req.headers['If-None-Match'] === publishTimestamp){
return res.writeHeader(304).end();
}
Eddig minden remekül működik, mivel szinte mindent cache-elhetünk, és a közzétételi folyamat során érvénytelenítjük a cache-ünket, így a rendszerben lévő kérés számok nagyon korlátozottak:
  • Cloudflare edge szerverenként csak 1 kérés éri el a magic proxy-t CDN URL-en.
  • Közzétételenként csak 1 kérés megy az eredeti szerverhez CDN URL-en.
  • Közzétételenként csak 1 „valódi” HTML válasz URL-en látogatónként, minden más csak 304.
Az eredeti szerverek (beleértve a webshop backend szervereket) párhuzamos kérés számának korlátozása egyszerű Node.js-ben, csak meg kell nézni az útmutatót.
const httpAgent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 80
});
**maxSockets <number>** Az egyes gazdagépekhez engedélyezett maximum socket szám. Minden kérés új socketet használ, amíg el nem éri a maximumot. Alapértelmezés: Végtelen.
Az egyetlen hátralévő dolog a webshop kérések továbbítása a webshop backendekhez. A Node.js lehetővé teszi a bejövő és kimenő http kérések összekapcsolását, minimalizálva az overhead-et. Hogy normál proxyként működjön, hozzáadtuk saját IP címünket az x-forwarded-for fejlécbe.
Az első tesztek ígéretesek voltak, 8K párhuzamos kérés kiszolgálása könnyedén ment, de a szintetikus tesztek félrevezetőek lehetnek. És tudjuk, hogy 880K egyedi látogató kiszolgálása egy hétnapos fesztivál alatt 2 VPS instanciából (20 euró / hó) őrültségnek tűnhet, de mi végül megtettük.
A Sziget fesztivál 2019 első napján (0. nap) a weboldal sávszélesség és kérés száma így nézett ki:

Sávszélesség

Cloudflare kérelmek

A CDN URL-ek miatt a Cloudflare végezte a munka nagy részét, a sávszélesség csak 3,3%-a érte el a magic proxy-t, és csak a kérések 1%-át kellett kiszolgálni. Egy 100Mbit/s VPS is túlzásnak bizonyult ekkor.
Cloudflare csúcsforgalom: 150 GB / óra
2,5 GB / perc -> 42 MB / mp -> 336 Mbit / mp + headerek
Magic proxy csúcsforgalom: 6,2 GB / óra
105 MB / perc -> 1,7 MB / mp -> 14 Mbit / mp + headerek
A CMS szerver gyakorlatilag üresjáratban volt a fesztivál alatt, de a közzétételi események után elérte a 400 Mbit / mp sebességet. Nem volt terhelés a VPS-eken, de 3 GB RAM-ot használtunk.
2019 óta finomítottunk néhány értéket, észrevettünk néhány versenyhelyzetet, kiterjesztettük a modulokat, de az alapvető koncepció változatlan maradt. Hagyjuk, hogy a Cloudflare végezze a nehéz munkát, az egyetlen (szinte statikus) weboldal feladata, hogy biztosítsa a szükséges koncepciókat – érvénytelenítés és egyedi URL-ek statikus tartalmakhoz. Minden mást cache-elt tartalom és kis proxy trükkök segítségével lehet megoldani, amelyek be vannak építve a szerverbe.
Tudjuk, hogy ez nem egy „igazán skálázható” megoldás, de a korlátok és célok figyelembevételével nagyon elégedettek vagyunk vele, és a Sziget is.

További blogbejegyzések

Sikeres weboldal készítése
Techtalk

A sikeres weboldal 5 jellemzője

2023.07.22.
4 perc

A foci, a vírusok és az energiapolitika mellett a weboldalakat sikeressé tevő jellemzők és alapelvek azok, amikhez mindenki ért, de valójában alig rendelkezik valaki megbízható tudással ezekben a témákban. Nem[...]

Weboldal tervezés
Techtalk

Az eredményes weboldal 5 alapvető szempontja 2022-ben

2023.02.16.
4 perc

Van valami, amit mindenki jobban tud másoknál, mégis senki sincs igazán biztos benne. Azokról a jellemzőkről és elvekről beszélünk, amelyek segítenek egy hétköznapi weboldalt abszolút sikerre vinni 2022-ben. Ez a[...]

Kubernetes
Techtalk

A legjobb házigazda – Melyik tárhely megoldás a legmegfelelőbb számodra?

2022.05.13.
9 perc

When it comes to hosting, such a thing as ‘best practice’ simply does not exist. However, as diverse this area of expertise has become […]