Scale-Up vs. Scale-Out

V dobách dřevních IT systémů, bylo běžným modelem zvýšení výkonu jakéhokoliv systému navýšení jeho zdrojů. Ať již šlo o paměť, síťovou kartu či CPU, typickým krokem byl upgrade či pořízení nového, v té chvíli rychlejšího stroje, což problém vyřešilo. Toto šlo až do určité chvíle a tato mechanika se běžně nazývá Scale-Up (škálování do výšky).

V určité chvíli se zvýšila potřeba výkonu natolik, že se další upgrade ekonomicky buď nevyplatil, či již ani nebyl možný. Běžné řešení, se kterým se často setkáme i dnes, je dělení požadavků – systém se rozdělil, část požadavků vyřizuje jedna část, druhou druhá část, problém odstraněn. Takto lze systém typicky několikrát dělit, vždy však na úkor celistvosti informace a složitosti infrastruktury. Ve světě, kde chcete požadavky umět ztisícinásobit však nakonec opět narazíte, ani dělit se nedá donekonečna.

Řešením takového problému je využití tzv. distribuovaných systémů – ty umí dělit problém na úroveň jednotlivých požadavků či jednotlivých entit, současně se však chovají jako celek a udržují celkový náhled na strukturu a systém. Typickým modelem navýšení výkonu, kapacity či propustnosti je k současným serverům přidat další takový server. Této metodě škálování se říká Scale-Out (škálování do šířky). Potenciálně, má takové škálování výrazně vyšší limity, než Scale-Up. Distribuované systémy však neumí všechno a některé systémy nahradit jen tak neumí.

CAP teorém

Před cca 20 lety vznikl tzv. CAP teorém, který popisuje právě schopnost distribuovaných systémů. Tento teorém říká, že z vlastností Konzistence (Consistency), Dostupnosti (Availability) a Tolerance vůči dělení (Partitioning tolerance) nelze dosáhnout všech 3 současně a vždy je možné mít současně pouze 2.

Jednotlivé požadavky jsou v detailu popsatelné takto:

  • - Konzistence – v jeden okamžik má celý systém zcela shodná data. Z pohledu klienta je to důležitá vlastnost, protože znamená, že všichni uživatelé dostanou v jeden okamžik vždy stejná data.
  • - Dostupnost – systém je schopen pokračovat v běhu i po pádu některé jeho části. Celek je tedy závislý na jednotlivých komponentách.
  • - Tolerance vůči dělení – systém je schopen pokračovat v práci, i pokud dojde k jeho rozdělení (typicky síťovou chybou).

Zatímco jednotlivé dvojice vlastností jsou dnes poměrně dobře zvládnutou kapitolou, všechny tři možné nejsou. V daný okamžik je nutné provést vhodnou volbu z hlediska architektury řešení. Pro každá data či různé procesy může být vhodné volit jiné databáze a akceptovat rizika plynoucí z chybějící vlastnosti.

Podíváme-li se na problém s ohledem na schopnost škálovat, nejméně zajímavá je kombinace CA, která představuje standardní relační systémy – neschopnost řešení dělit nám nedává možnost škálovat jinak, než v Scale-Up, dělením úlohy apod., ale nikoliv čistě Scale-Out. Naopak kombinace AP a CP jsou pro distribuovanost podstatně zajímavější.

Systémy s vlastností AP – chybí nám vlastnost konzistence, může se tedy stát, že 2 různí klienti dostanou ve stejném čase od systému 2 různé odpovědi. To může být problém, který můžeme výrazně zmírnit, pokud se stejní klienti budou dotazovat stejných částí systémů, a odpovědi pro ně samotné tedy konzistentní budou. Pokud bychom stavěli e-shop, ukládání dat o obsahu košíku by bez těchto ošetření mohl znamenat, že po obnovení stránky bychom získali jinou informaci o obsahu svého košíku, což není příliš praktické. Opačně, pokud bychom takto udržovali data s malou četností změn a dlouhými prodlevami mezi změnami, neznamenalo by toto riziko žádný podstatný problém. Mezi systémy s vlastnostmi AP patří např. systémy CouchDB či Cassandra.

U systémů CP je chybějící vlastností Dostupnost. Systém tedy není schopen pokračovat v produkční činnosti při ztrátě některého z nodů. Pokud se vrátíme k modelu e-shopu, klient uvidí neustále správně obsah košíku, až do momentu pádu – v takový moment se mu košík např. vyprázdní a on bude nucen s košíkem pracovat zcela od počátku. To předpokládá, že jsme schopni nahradit nefungující systém funkčním, což v cloudovém světě velký problém není. Pro data, která mají velmi rychlou frekvenci změn, a buď to pro ně není důležité či jsou dopočitatelná (tedy nejsou součástí uzavřené nevratné transakce), je takový systém velmi vhodný. Např. právě uváděný košík – klient nadšený určitě nebude, nicméně až do kliknutí na odeslání nevznikla závazná transakce, jejíž ztráta by byla velmi problematická a data se tedy dají oželet či dopočítat (klient je zadá znovu). Typickým představitelem takových systémů je např. Redis.

Pro škálování je nezbytné uvažovat o těchto architektonických modelech již v době vývoje aplikace.

Řízení zdrojů

Pro různé úlohy se hodí jiné řešení na úrovni řízení zdrojů a to proto, že různé metody řízení dovolují jinou mechaniku a dynamiku jejich škálování a efektivity využití. V dnešním světě je nejčastější formou přidělení zdrojů virtualizace. Z původního scénáře fyzického železa, dnes dál nabízeného jako Bare Metal as a Service se víceméně ustupuje a BMaaS se ponechává zejména pro speciální případy užití zdrojů. Současně s tímto postupem se objevila kontejnerizace, která se naopak snaží izolovat pouze aplikaci a nikoliv celý Operační Systém (jako ve virtualizaci), či dokonce celý server (jako v BMaaS).

Bare Metal se dnes stále hodí zejména na úlohy, u nichž očekáváte bezprostředně stabilní výkon, a to na řadě úrovní – CPU, RAM, HDD ale i síť, její odezva apod. Stejně tak je BMaaS velmi vhodným řešením při použití speciálních technologií jako jsou GPU karty, různé akcelerační karty s ASIC, FPGA či třeba karty pro generování náhodných čísel.

Vedle toho virtualizace nabízí získání stability zejména základních parametrů výkonu – typicky CPU, RAM, HDD. Přesto může poskytovatel tyto zdroje natolik agregovat, že prostředí nebude stabilní. Sdílení potenciálně nemusí vadit, pokud daný zdroj nevyužíváte extensivně, moderní hypervizory si s takovou situací jsou schopny poradit. Vyžadujete-li však určitou garanci daného zdroje, nemusí být virtualizace vůbec vhodná – typicky např. encoding life video kanálu vyžaduje velmi přesnou odezvu na CPU/GPU a je-li tento sdílen, může zhoršení odezvy snadno znamenat i zhoršení kvality obrazu (výsledku encodingu).

Nejnovější způsob řízení zdrojů směrem k aplikacím je kontejnerizace. Ta ve velkém přišla společně s Dockerem, ačkoliv princip sám je starší a již dříve se obdobné mechanismy používaly ve virtualizaci. Kontejner dovoluje izolovat hlavní komponenty aplikace jako samostatný a přenositelný celek, neobsahuje jádro operačního systému a jemu přidružené knihovny. Díky tomu je výsledek relativně malý, snadno zreplikovatelný a za pomoci nástrojů jako je docker-compose se dá ve většině případů popsat na pár řádcích kódu. Této mechanice se standardně říká Infrastructure as a Code (IaaC). Nad kontejnarizací vznikla další orchestrace, jejíž největší představitelé jsou dnes OpenShift a Kubernetes. 

Pro výsledek je důležitá volba přidělení zdrojů. Dnes platí, že optimální bude typicky použití více než jedné metody. Pro řadu extrémně zatížených systémů může být zajímavé řešení BMaaS, naopak pro snadno se škálující role systémů mohou být zajímavé kontejnery.

(Bez)stavové role

Další důležitou věcí při tvorbě škálovatelných aplikací je rozdělení rolí na stavové a bez-stavové. Bezstavové role jsou takové, které ke své činnosti nepotřebují žádný předchozí stav, tedy za všech okolností, pouze se znalostí daného požadavku mohou vykonat, co je od nich požadováno. Můžeme si představit 2 funkce – funkce A provede součet vstupu V1 a V2 a vrátí výsledek – taková funkce je bezstavová, neboť ke správnému výsledku postačují vstupy requestu. Funkce B může být čítač přístupů – její výstup bude hodnota „kolikátí se ptáte“. 1. dotaz vrátí 1, 2. dotaz 2 atd. – taková funkce je stavová, protože si při každém běhu musí zapamatovat vrácenou hodnotu, aby ji napříště o 1 zvýšila a opět vrátila.

Obecně platí, že podstatně snazší jsou bezstavové role. Celý problém popsaný v rámci CAP teorému je právě nutnost uložit nějaký stav, tento stav distribuovat v rámci systému a zajistit jeho dostupnost při výpadku a současně dostatečný výkon celého systému při jeho zapisování a čtení.

Architektura projektu

Námi připravený projekt, jenž demonstruje jeden z modelů webové škálovatelné aplikace využíval tyto role:

  • - Router
  • - L4 Router
  • - SSL Terminator
  • - LoadBalancer
  • - HTML Cache
  • - Webový nod
  • - Content Data
  • - Image-resize
  • - Content Search
  • - Seat Cache
  • - User Data
  • - Log Collector
  • - Log Analyze
  • - Version and Deployment Control

Router

Jde o roli vstupu do sítě. Může se zdát nadbytečně uváděná a také patří mezi role, jež snadno podceníte. V našem případě jde o L3 routing v kapacitách x100Gbit. Důležitou vlastností je schopnost vytvořit níže uvedený L4 Routing. Pokud bychom potřebovali zvyšovat kapacitu přenosu, první krok by byl standardní Scale-Up. Scale-Out je u této role také možný a představuje jej změna propagace systému do Internetu, např. RoundRobin či AnyCast sítí s opakováním infrastruktury. To v našem případě nebylo nutné. Typické řešení velkých operátorů je zmíněný RoundRobin v rámci DNS.

L4 Router

L4 routing je role, jež na úrovni L4 zajišťuje balancing (v našem případě routing za pomoci BGP) vůči SSL terminaci. V tomto případě jde o velmi klíčovou roli, protože SSL Terminace je snadno přetížitelnou rolí a tento L4 Routing dovoluje její velmi snadné škálování. V našem případě je směrem k SSL Terminaci využit nginx a tzv. proxyprotocol. Naopak nahoru směrem k routeru je využito BGP směrování. 

SSL Terminator

Jde o roli, jež zajišťuje terminaci SSL spojení. Komunikace dále probíhá nešifrovaná. V našem případě jde opět o nginx a celá role je spojena rovnou i s rolí LoadBalanceru a HTML Cache. Všechny tyto role se současně škálují (což není obecně šťastné řešení!). Vstupem je výstup z L4, kde je provoz zabalen do proxyprotocolu, což zajišťuje, že získáme původní informace o zdroji (jako source IP, které bychom jinak na L4 přepsali). Pro SSL jsou nezbytné certifikáty. Ty jsou v tomto případě šířeny na všechny LB, kde se v pravidelných (a dlouhých) intervalech obměňují.

LoadBalancer

Tato role je spojena s SSL Terminatorem, zajišťuje jí nginx. Směrování probíhá na základě server_name, případně dalších specifických parametrů, není již využívána IP adresa. Důvodem je zejména obecnost – takto je definice dalšího webového projektu otázkou pouze směrování server_name na novou skupinu web nodů, o IP se stará L4 a tu naopak server_name nezajímá.

Parametry nastavení jsou centrálně řízeny ze systému consul, který zde provádí distribuci configurace jednotlivých webů. Weby jsou současně napsané tak, aby nebylo nutné držet permanentní session. Požadavky jsou tedy náhodně směrovány na funkční web nody. To je důležitý bod, protože v případě potřeby permanentních session by jednak došlo k poklesu výkonu a celý systém by bylo nutné řešit v celé vrstvě balancerů jinak.

HTML Cache

Pro odlehčení webových nodů je implementována HTML Cache. Je součastí LoadBalancerů, což však není nejlepší řešení. Zde je důvodem fakt, že v rámci Cache postačuje relativně malý objem dat, pro vysoký cache hit, data jsou nicméně shodně umístěna na všech těchto nodech shodně. Existuje zde potenciál využít oddělenou Cache tvořenou např. Varnishem, Redisem či jiným systémem a ušetřit tak prostředky na opakování dat napříč LoadBalancery.

Webový nod

Webové nody jsou zde stavěny jako bezstavovové, což je velmi důležité pro rychlost jejich deploymentu. Všechny klíčové části webového systému jsou implementovány lokálně, jsou nedílnou součástí deploymentu webového serveru. S ohledem na velikost každého z nich byla využita virtualizace, obecně by však tato role typicky skončila spíše v rámci kontejneru.

Content Data

Obsahová data jsou umístěna na sdílené službě NAS, kterou disponují všechny webové servery. Obecně je sledováno její vytížení, neboť jde o důležitou součást systému. Ačkoliv se zprvu užití NAS služby jevilo jako výkonnostní problém, předřazena HTML Cache snižuje její vytížení na minimum. Důležitým faktem je také to, že v rámci Content Data nedochází k pravidelnému zápisu. Data jsou v podstatě statická, což podstatně zvyšuje výkon NAS. V rámci implementace byly uvažovány i systémy jako GlusterFS či OCFS (v obou případech jde o distribuované filesystémy), převládlo však užití NAS zejména kvůli množství zdrojů (NAS je z tohoto pohledu nejekonomičtější).

Z pohledu škálování jde však v podstatě o faul. NAS systém je jen špatně škálovatelný ve výkonu a jedná se o typicky Scale-Up prvek celé architektury.

Image-resize

Image resize je funkce, jež zajišťuje zmenšení obrázku na požadovanou velikost a to před distribucí obrázku návštěvníkovy. Tento systém je typickým bezstavovým členem architektury, parametry pro resizing i obrázek samotný získává vzdáleně. U nás je tvořen nginx, škálování je nutné pouze kvůli výkonnostním požadavkům. Výsledky jsou cachovány, nejsou však ukládány trvale. Tato role je vyčleněna z ostatních tvořených nginxem z důvodu úspory výkonu. SSL terminace a LB jsou velmi závislé na odezvě CPU, jde poměrně výpočetně drahé operace a umístění náhodně volaného image-resize by vedlo k občasným zhoršením odezev.

Content Search

Pro vyhledávání v obsahu slouží katalogy nahrané v ElasticSearch. V této věci je systém velmi malý a služba vyhledávání je považována za nekritickou. Důvodem pro škálování by potenciálně byl výkon, v tomto případě je však ze strany klienta požadavek na statické řešení.

Seat Cache

Cache je pro jednotlivé uživatele asi nejvytíženější systém v rámci celé sestavy. Jde o Redis, v němž jsou uloženy data všech session webových nodů. Díky tomuto systému web nody nevyžadují persistentní session. Pád, resp. ztráta dat, by vyžadovala zejména znovu přihlášení v rámci webových stránek. Data jsou v Redis ukládána s velmi dlouhou retencí a z důvodů maximálního urychlení přenosu byl uvažován pro tuto roli Bare Metal. Finále jsme vybudovali na virtualizaci, potenciálně však můžeme kdykoliv přejít.

Redis je snadno škálovatelný zejména za účelem zvýšení kapacity, přičemž díky stromové struktuře dochází jen k malému zpoždění.

User Data

User Data jsou umístěna v relační databázi, kde jsou standardním způsobem strukturována. Systém počítá s oddělením různých požadavků na data – Read, Write a existuje zde standardně podporovaná možnost škálování tzv. Slave serverů, zajišťujících Read operace.

Write operace jsou málo četné. Systém byl uvažován v modelu Multi-Master, po testech byla však tato možnost zrušena jako neekonomická. Současný model škáluje pouze Read Operace, a ačkoliv tyto převládají, s rostoucím počtem Write Operací by nutně podstatně klesal výkon.

Log Collector

Ukládání logů z jednotlivých systémů je velmi důležité a to nejen pro zajištění zpětné analýzy dění. Důležitou potřebou je co nejrychleji dostat logy z jednotlivých nodů a to ideálně bez provedení zápisu na disk. Provést jakýkoliv zápis je jednak výpočetně velmi neekonomické, druhý důvod je pak samotná potřeba zachování bezstavovosti nodů.

Pro Log Collection používáme Log Stash, který tvoří most směrem k Log Analyze. Škálování provádíme pomocí DNS směrování, kde se pracuje s různými cíli v rámci systému. Transformace logů bohužel není příliš jednoduchá činnost a dlouhodobě řešíme spíše nahrazení Log Stash poněkud modernějším Fluentd. Ten vykazuje menší spotřebu zdrojů a jeví se snazší i pro konfiguraci transformací.

Log Analyze

Pro vyhledávání a analýzu chování celé sestavy používáme opět Elastic Search vybavený systémem Kibana. Jde o velmi tradiční setup a také velice silný právě pro tyto činnosti. I zde je nutné umět škálovat, což je v případě ElasticSearch dobře podporováno. Důvodem ke škálování je zejména množství zpracovávaných indexů – Log Analyze zde slouží i ke sledování dlouhodobějších trendů a dat je tedy velké množství. S rostoucí návštěvností se i data chovají relativně nepredikovatelně.

Version and Deployment Control

Nejde o poslední součást sestavy, nicméně pravděpodobně o poslední, jež stojí za zmínku – deployment a řízení verzování. Deployment je u řady rolí prováděn v paralelní přípravě a rolling updatem je následně nová verze nasazena. Tyto činnosti výrazně usnadňuje consul, který se prolíná celým systémem jako podpůrná platforma. Řízení verzí je tvořeno GitLabem s různými vzdálenými přístupy, což se velmi osvědčilo. V minulosti uvažované téma např. AVX/Ansible Tower jsme pro dnes odsunuli do pozadí.