De zwakste schakel: hoe geketende afhankelijkheden het systeemrisico beïnvloeden
Bij het beoordelen van risicoscenario’s voor systemen is het zeer gemakkelijk om “geketende” afhankelijkheden over het hoofd te zien. Wij zijn getraind om risico op het niveau van een “knooppunt” te bekijken en ons af te vragen “hoe waarschijnlijk is het dat dit ene ding faalt”. Maar systeemrisico is veel ingewikkelder dan dat.
In de meeste systemen zijn er enkele componenten die afhankelijk zijn van andere componenten. De meest voorkomende plaats waar wij hiernaar kijken, is bij het ontwerp van opslag voor servers, maar het komt in elk systeemontwerp voor. Een ander goed voorbeeld is hoe webapplicaties zowel applicatiehosts als databasehosts nodig hebben om te kunnen functioneren.
Geketende afhankelijkheden zijn het gemakkelijkst uit te leggen aan de hand van een voorbeeld. Wij zullen kijken naar een standaardvirtualisatieontwerp met SAN-opslag om te begrijpen waar de grenzen van failure domains liggen, waar geketende afhankelijkheden bestaan en welke rol redundantie speelt bij het mitigeren van risico op systeemniveau.
In een standaard SAN-ontwerp (storage area network) voor virtualisatie hebt u virtualisatiehosts (die wij voor de eenvoud de “servers” zullen noemen), SAN-switches (switches die zijn toegewijd aan het opslagnetwerk) en de schijfarrays zelf. Elk van deze drie “lagen” is voor het systeem als geheel afhankelijk van de andere om te kunnen functioneren. Indien wij de eenvoudigst mogelijke opstelling zouden hebben met één server, één switch en één schijfarray, dan hebben wij heel duidelijk drie apparaten die drie afzonderlijke storingspunten vertegenwoordigen. Het falen van een van de drie veroorzaakt het falen van het gehele systeem. Geen enkel onderdeel is op zichzelf bruikbaar. Dit is een geketende afhankelijkheid, en de ketting is slechts zo sterk als de zwakste schakel.
In ons simplistische voorbeeld vertegenwoordigt elk apparaat een failure domain. Wij kunnen het risico mitigeren door de betrouwbaarheid van elk domein te verbeteren. Wij kunnen een tweede server toevoegen en een high-availability- of fault-tolerance-strategie op de virtualisatielaag implementeren om het risico op serveruitval te verkleinen. Dit verbetert de betrouwbaarheid van één failure domain, maar laat er twee onaangeroerd en even riskant als zij voorheen waren. Vervolgens kunnen wij de switchinglaag aanpakken door een redundante switch toe te voegen en een multipathing-strategie te configureren om het verlies van een enkel switchingpad af te handelen, waardoor het risico op die laag wordt verkleind. Nu zijn twee failure domains aangepakt. Ten slotte moeten wij het failure domain van de opslag aanpakken, wat op vergelijkbare wijze wordt gedaan door redundantie toe te voegen via een tweede schijfarray die wordt gespiegeld naar de eerste en die in geval van een storing transparant kan overschakelen.
Nu wij ons systeem hebben versterkt, hebben wij nog steeds drie failure domains in een afhankelijkheidsketen. Wat wij hebben gedaan, is elke “schakel” in de ketting, elk failure domain, op zichzelf extra veerkrachtig gemaakt. Maar de ketting bestaat nog steeds. Dit betekent dat het systeem als geheel veel minder betrouwbaar is dan welk afzonderlijk failure domain binnen de ketting op zichzelf is. Wij hebben iets gemaakt dat veel beter is dan waar wij begonnen, maar wij hebben nog steeds veel failure domains. Deze risico’s stapelen zich op.
Wat moeilijk is bij het bepalen van het totale risico, is dat wij het risico van elk item moeten beoordelen, vervolgens het nieuwe risico na mitigatie (door de toevoeging van redundantie) moeten bepalen en vervolgens het cumulatieve risico van elk van de failure domains tezamen in een ketting moeten vinden om het totale risico van het gehele systeem te bepalen. Het is buitengewoon moeilijk om het risico binnen elk failure domain te bepalen, aangezien de wijze van risicomitigatie een aanzienlijke rol speelt. Een cluster van schijfarrays voor opslag dat bijvoorbeeld te traag overschakelt, kan resulteren in een algeheel systeemfalen, zelfs wanneer het opslagcluster zelf naar behoren lijkt te hebben gewerkt. Zelfs het definiëren van een duidelijke storing kan derhalve een uitdaging vormen.
Het is vaak verleidelijk om een risicobeoordeling “van bovenaf” te hanteren, wat zeer gevaarlijk is, maar zeer gangbaar voor mensen die geen reguliere beoefenaars van risicobeoordeling zijn. De neiging is hierbij om naar het risico te kijken door uitsluitend het “bovenste” failure domain te bekijken – over het algemeen de servers in een geval als dit – en eventuele risico’s die onder dat punt liggen te negeren door deze als “onder de motorkap” te beschouwen in plaats van als onderdeel van de risicobeoordeling. Het is gemakkelijk om de technischere, minder zichtbare en slechter begrepen componenten zoals netwerken en opslag te negeren en zich te concentreren op de relatief gemakkelijk te begrijpen en zwaar gemarkete betrouwbaarheidsaspecten van de bovenste laag. Deze “bovenaanzicht” betekent dat de risico’s onder het bovenste niveau worden verhuld en over het algemeen worden genegeerd, wat leidt tot een hoog risico zonder een goed begrip van waarom.
Het begrijpen van het concept van geketende afhankelijkheden verklaart waarom complexe systemen, zelfs met complexe risicomitigatiestrategieën, vaak resulteren in een veel grotere kwetsbaarheid dan eenvoudigere systemen. In ons bovenstaande voorbeeld zouden wij verschillende dingen kunnen doen om de ketting in te “laten klappen”, wat resulteert in een systeem dat als geheel betrouwbaarder is.
De meest voor de hand liggende component die kan worden ingeklapt, is het failure domain van het netwerk. Indien wij de switches volledig zouden verwijderen en de opslag rechtstreeks op de servers zouden aansluiten (uiteraard niet altijd mogelijk), zouden wij in feite een geheel failure domain elimineren en een schakel uit onze ketting verwijderen. Nu hebben wij, in plaats van drie schakels die elk enige kans op falen hebben, er nog slechts twee. Eenvoudiger is beter, als alle overige factoren gelijk blijven.
Wij zouden in theorie ook het failure domain van de opslag kunnen inklappen door over te stappen van externe opslag naar het gebruik van opslag die lokaal aan de servers zelf is, waardoor wij in wezen van twee failure domains naar één enkel failure domain gaan – het ene resterende domein draagt uiteraard meer complexiteit dan het deed vóór het inklappen, maar de algehele systeemcomplexiteit is sterk verminderd. Nogmaals, dit is met alle overige factoren gelijk blijvend.
Een andere benadering die het overwegen waard is, is het op zichzelf betrouwbaarder maken van afzonderlijke knooppunten. Het is vandaag de dag trendy om naar grotere systemen te kijken en risicomitigatie op die manier te benaderen, door redundante knooppunten tegen lage kosten toe te voegen om betrouwbaarheid toe te voegen aan failure domains. Maar van oudsher was dit niet het standaardpad dat naar betrouwbaarheid werd gekozen. Het was in het verleden veel gangbaarder, zoals blijkt uit de vroegere alomtegenwoordigheid van mainframes en systemen van vergelijkbare klasse, om hoge mate van betrouwbaarheid in een enkel knooppunt in te bouwen. Mainframes en high-end opslagsystemen doen dit bijvoorbeeld vandaag de dag nog steeds. Dit kan feitelijk een buitengewoon effectieve benadering zijn, maar schiet tekort in het adresseren van veel scenario’s en is over het algemeen buitengewoon kostbaar, vaak versterkt door de noodzaak om systemen gedeeltelijk of zelfs volledig door de leverancier te laten onderhouden. Dit pakt doorgaans alleen in speciale nicheomstandigheden goed uit en is op een algemener vlak niet praktisch.
In elk systeem van deze aard hebben wij dus drie belangrijke risicomitigatiestrategieën te overwegen: de betrouwbaarheid van een afzonderlijk knooppunt verbeteren, de betrouwbaarheid van een afzonderlijk domein verbeteren, of het aantal failure domains (schakels) in de afhankelijkheidsketen verminderen. Het op verstandige wijze combineren van deze strategieën kan ons helpen het niveau van risicomitigatie te bereiken dat passend is voor ons bedrijfsscenario.
Waar de werkelijke moeilijkheid ligt, en zal blijven liggen, is in de vergelijking van verschillende risicomitigatiestrategieën. Het risico van een afzonderlijk knooppunt kan over het algemeen met enige mate van zekerheid worden ingeschat. Een redundantiestrategie binnen een afzonderlijk domein laat zich veel minder goed inschatten – sommige redundantiestrategieën zijn zeer effectief en creëren buitengewoon betrouwbare failure domains, terwijl andere feitelijk averechts kunnen werken en de betrouwbaarheid van een domein kunnen verminderen! De complexiteit die vaak met redundantiestrategieën gepaard gaat, is nooit zonder voorbehoud, en hoewel deze doorgaans zal lonen, draagt zij zelden de mate van betrouwbaarheidsvoordeel die aanvankelijk wordt verwacht. Het inschatten van het risico van een afhankelijkheidsketen is derhalve des te moeilijker, aangezien het een duidelijk begrip vereist van de risico’s die met elk van de failure domains afzonderlijk gepaard gaan, alsook een begrip van de storingsmogelijkheid die bestaat op de domeingrenzen (zoals de eerder genoemde storing door vertraging bij de failover van de opslag).
Laten wij de vraagstukken rondom het bepalen van risico onderzoeken in twee zeer gangbare benaderingen van hetzelfde scenario, voortbouwend op wat wij hierboven hebben besproken.
Twee extreme voorbeelden van dezelfde situatie die wij hebben besproken, zijn een enkele server met interne opslag die wordt gebruikt om virtuele machines te hosten versus een “ketting” van zes apparaten met twee servers en het gebruik van een high-availability-oplossing op de serverlaag, twee switches met redundantie op de switchinglaag en twee schijfarrays die high availability bieden op de opslaglaag. Indien wij hier een willekeurige grote factor wijzigen, kunnen wij over het algemeen een tamelijk duidelijke inschatting van het relatieve risico geven – indien bijvoorbeeld een van de failure domains betrouwbare redundantie ontbeert – dan kunnen wij vrij duidelijk bepalen dat de enkele server het betrouwbaardere systeem als geheel is, behalve in gevallen waarin een extreme mate van betrouwbaarheid van een afzonderlijk knooppunt aan een enkel knooppunt wordt toegekend, wat financieel gezien over het algemeen een onpraktische strategie is. Maar wanneer elk failure domain redundantie behoudt, worden wij gedwongen de relatieve risico’s van betrouwbaarheid binnen het domein (de redundante ketting) te vergelijken met die van betrouwbaarheid tussen domeinen (de ingeklapte ketting, de enkele server).
Bij de twee volledig verschillende benaderingen is er geen redelijke manier om de vergelijkende risico’s van de twee wijzen van risicomitigatie te beoordelen. Het wordt algemeen aanvaard dat de benadering met zes (of meer) knooppunten met uitgebreide risicomitigatie binnen het domein de betrouwbaardere van de twee benaderingen is, en dit is vrijwel zeker, over het algemeen, waar. Maar het is niet altijd waar, en zelden overtreft deze benadering de strategie met een enkel knooppunt met een werkelijk significante marge, terwijl zij doorgaans vier tot tien keer zoveel kost als de strategie met een enkele server. Dat is mogelijk een zeer hoge prijs voor wat waarschijnlijk een kleine winst in betrouwbaarheid is en een klein potentieel risico op een verlies aan betrouwbaarheid. Elk extra stuk redundantie voegt complexiteit toe die een mens moet implementeren, monitoren en onderhouden, en met complexiteit en menselijke interactie komt steeds meer risico. Het vermijden van menselijke fouten kan vaak belangrijker zijn dan het vermijden van mechanisch falen.
Wij moeten tevens de kosten van herstel in overweging nemen. Indien een storing optreedt, is het over het algemeen triviaal om te herstellen van het falen van een eenvoudig systeem. Een buitengewoon complex systeem dat is gefaald, kan een grote mate van inspanning vergen om weer in werkende staat te brengen. Complexe systemen vereisen tevens veel bredere en diepere mate van ervaring en vertrouwen om te onderhouden.
Er bestaat geen eenvoudig antwoord op het bepalen van de betrouwbaarheid van systemen. Moderne systemen voor informatielevering zijn eenvoudigweg te groot en te complex, met te veel onbepaalbare factoren, om in alle gevallen te kunnen evalueren. Met een goed begrip van geketende afhankelijkheden echter, en een begrip van risicomitigatiestrategieën, kunnen wij praktische stappen ondernemen om ruwweg relatieve risiconiveaus te bepalen, te zien hoe vergelijkbare risicoscenario’s zich qua kosten verhouden, punten van kwetsbaarheid te identificeren, failure domains en afhankelijkheidsketens te herkennen, en te doorgronden hoe wijzigingen in het systeemontwerp ons duidelijk naar of weg van betrouwbaarheid zullen bewegen.
