#62 Architektura single i multi tenant

2023, Feb 17

Multi-tenancy vs single-tenancy! Czyli m. in. implementacja pod B2C a B2B, sposoby implementacji, co w kodzie… a co w infrastrukturze?

Linki i ciekawe znaleziska:

Transkrypcja

Łukasz Kałużny: Cześć. Słuchacie Patoarchitektów. Prowadzą Łukasz Kałużny.

Szymon Warda: I Szymon Warda. Wszystkie linki oczywiście na patoarchitekci.io/62. I przechodzimy do mięsa, bo temat jest taki, gdzie dawno nie mieliśmy takiej kłótni z Łukaszem przed nagrywaniem. Multi-Tenancy czy Single-Tenancy? Problem jest taki: jak hostować i jak stworzyć infrastrukturę pod to, żeby obsługiwać wielu klientów.

Łukasz Kałużny: Tak i teraz powiedzmy sobie o dwóch podejściach, bo to była nasza największa kłótnia. Powiedzmy, są dwie perspektywy. Perspektywa operacyjno-biznesowa; To jest ważny punkt, że potrafimy dostarczyć nasz produkt dla wielu klientów jako usługę. I to jest pierwszy punkt widzenia. Drugi punkt widzenia - jest to, czy architektonicznie jest to multi-tenancy. Tak, i to są dwa podziały. Dobra, przejdźmy sobie od razu do poziomów separacji, czyli w jaki sposób dokonujemy tego i sobie zaczniemy to tłumaczyć, te pojęcia.

Szymon Warda: Dobrze. Najbardziej oczywisty jest taki, że dzielimy to w kodzie. Teraz co rozumiemy przez dzielimy to w kodzie? Czyli że mamy instancję aplikacji, która już w kodzie - dotnecie, czymkolwiek, Javie; na poziomie z reguły jakiegoś headera, z reguły jakiegoś tokenu, który pobierze… Wybiera ten token i wszystkie requesty, o które będzie zapytywała dane, bo te dane są najbardziej wrażliwe, będzie robiła z tym filtrem; z reguły jakimś Client ID albo coś w tym stylu. Czyli de facto to developerzy odpowiadają za to, żeby nie było dostępu z Tenanta A do danych Tenanta B.

Łukasz Kałużny: Czyli Tenant to jest dzierżawca po polsku ładnie, czyli po prostu przestrzeń naszego klienta, którą mu dedykujemy. Więc całość i ta rzecz na poziomie kodu to rynkowo, architektonicznie, technologicznie to jest właśnie to, co nazywamy multi-tenancy z punktu widzenia architektury, to jest takie istotne - z punktu widzenia architektury. Biznesowo to wszystko zależy od naszego modelu operacyjnego.

Szymon Warda: Tak, dla mnie podobnie np. jest też w fakturowniach albo jakiś taki system śledzenia faktur. To jest super, żeby… Nie chcielibyśmy widzieć faktur innej osoby, albo żeby ktoś widział nasze faktury de facto.

Łukasz Kałużny: Wrzuciłbym jeszcze Office’a 365 czy Google Workspace’a do tego worka.

Szymon Warda: Zgadza się jak najbardziej. Dobrze. Kolejny poziom do separacji to są poziomy danych i tu mamy tak; albo mamy wszystko w jednej bazie i de facto tylko na poziomie tabeli… De facto mamy tam Client ID albo jakoś wyciągamy na poziomie per schema, czyli mamy inne… Mamy inne prefiksy do schematów, ale same schematy w jednej bazie danych. I kolejny podział to jest - mamy różne bazy danych dla różnych tenantów. I jedną rzecz dodam: tu ja użyłem określenia bazy danych, ale to też może tyczyć się takich rzeczy jak storage’e. Rzeczy, generalnie, ogólno szeroko pojętych danych. Key vaultów itd. Więc wszystkiego co zapisujemy de facto.

Łukasz Kałużny: Tak, i model, jeżeli popatrzymy sobie, implementacyjny to tu jest bardzo ważny podział - czy to jest B2C czy B2B, czyli to jest czy klient końcowy jest takim Kowalskim. Weźmy aka Gmail - czy klient jest w jakimś bardzo dedykowanym specyficznym sofcie typu bitu B2B. I o co chodzi? W przypadku takiego typowego B2C nie mamy żadnego podziału, bo chcemy jak najbardziej przerzucić to na kod, żeby było jak najtańsze. Jak najbardziej kombinować z infrastrukturą pod spodem. Jeżeli chodzi o rozwiązania typu B2B i te multi-tenancy z poziomu kodu przyjmuje się takie założenie, że jeżeli mamy multi-tenancy na poziomie kodu to zazwyczaj ten podział powinien być per baza danych.

Szymon Warda: Trochę, trochę jeszcze wybiegasz, trochę wybiegasz od tego jak to powinno być, ale jeszcze - bo mieliśmy dalszy temat gdzie to faktycznie nie jest poziomem separacji, ale ja to nazywam poziomem upewnienia się, że ta separacja w porządku generalnie. Albo jeżeli mamy pewne modele, czyli możemy jeszcze mieć rozmowę czy nasz compute, faktyczne maszyny wirtualne, czyli one są współdzielone, czy każdy ma swoje wydzielone maszyny. I tutaj słuszna uwaga Łukasza generalnie, że to już ma sens tylko i wyłącznie przy B2B jeżeli nasz klient wystawia pokaźne faktury. I kolejna opcja czy separujemy sieci tak naprawdę, czy upewniamy się, żeby Tenant A nie był w stanie wykonać żadnego requestu do sieci Tenanta B.

Łukasz Kałużny: Tak, i to już są dedicated tenanty albo single tenanty.

Szymon Warda: Tak, zgadza się.

Łukasz Kałużny: I to jest taka istotna rzecz, czyli te dwie: pierwsza opcja - kod oznacza, że to jest architektoniczny multi-tenant, a to co było powiedziane właśnie z computem, z dedykowanymi deploymentami, wdrożeniami to są single-ten anty. Ale mówimy nasza firma oferuje produkt jako usługa.

Szymon Warda: Tak. Łukasz chce być bardzo poprawny jeżeli chodzi o definicję. Ja trochę mniej na nią patrzę, dobra. Teraz idziemy na sposoby implementacji. W kodzie… Powiedzieliśmy sobie już jak to działa, więc teraz na poziomie platformy i tu mamy kilka opcji. Możemy sobie na poziomie… Będziemy często powoływali się na Kubernetesa, bo tamte pojęcia są znane i mniej więcej najczęściej takie platformy będą robione na Kubernetesie w jakiejś odmianie. Podejrzewam. Możemy na ingressie porozdzielać sobie ładnie, możemy na poziomie jakiegoś Service Mesha wydzielać na poziomie requestów z headerów, kierować ruch odpowiednio do odpowiednich instancji i możemy też robić to na poziomie DNS’ów. Czyli jak klient się u nas rejestruje, dostaje odpowiedniego CName’a, jakąś subdomenę i przez to de facto już na poziomie takim wejścia naszego systemu; jest odpowiednio kierowany do innych de facto instancji naszej aplikacji.

Łukasz Kałużny: I zabawa polega na tym, że Ingress i Mesh de facto obsługują albo tą opcję DNS’ową, albo dedykowanego URL’a, albo rozszycie odpowiednio headera z tokenem i ze wszystkim co potrzebuje.

Szymon Warda: Rozszycie, bo to mesh jest na poziomie rozszycia defacto tak naprawdę. Tak, zgadza się.

Łukasz Kałużny: W locie, a Ingress na wejściu to rozszywa, bo w obydwu możemy uzyskać to samo.

Szymon Warda: DNS też jest o tyle fajny, że potencjalnie on umie przykryć to, czy… Taką wersję najgorszą. To, czy dany tenant ma ma swojego namespace’a czy ma swój klaster de facto. On daje nam to, że dla super wielkich klientów można powiedzieć: Masz osobny klaster i śmigasz tam.

Łukasz Kałużny: Tak, dokładnie - i nie bawisz się też w routing tego jeszcze przed, czyli jakiś… Bramkę wyjściową na całość i rozszywanie.

Szymon Warda: Tak i też geo bardzo wiele rzeczy potencjalnie potrafi ładnie przykryć tylko dla dużych klientów. Nie oszukujmy się. Ostatnią rozmową, która jest to jest rozmowa jak bardzo dzielimy compute i czy wystarczy podzielić compute tak naprawdę, czy każdy duży tenant ma swój dedykowany klaster, czy jednak separacja na poziomie dedykowanych nodegrup wystarcza tak naprawdę? No i tutaj może być każdy… Każde ma swoje plusy.

Łukasz Kałużny: Tak, pójdziemy sobie… Powiedzmy sobie, jak bardzo krytyczną rzecz robimy, bo to chyba trzeba spojrzeć z tej perspektywy. Powiedzmy sobie szczerze - to zależy, jak bardzo możemy zostać też zaatakowani, bo to też trzeba sobie popatrzeć z tej perspektywy, albo jak bardzo kiepsko implementujemy Kubernetesa, bo jeżeli robimy dedykowane nodegrupy przykładowo, czyli wydzielamy dla klienta na klastrze dedykowanym nodegrupy. Oznacza to, że w teorii umiemy coś w Kubernetesa, bo powinniśmy zrobić to np. jeżeli mówimy o hostowaniu tego na Kubernetesie, bo powinniśmy umieć użyć network policy, namespace’ów i innych takich elementów. Co oznacza, że z mojej perspektywy; poza tym, że jeżeli to są rzeczy zasobożerne w CPU i musimy robić tam quality of service taki mocny, to de facto no… Nie ma to sensu. Lepiej już jeżeli w szczególności hostujemy się w cloudzie, lepiej jest kolokować to ze sobą.

Szymon Warda: OK. Bo teraz powoli wychodzi nam inna perspektywa, z której patrzymy de facto. Ja patrzę z perspektywy tego: czy ufam moim deweloperom, żeby się nie machnęli i żeby przetestowali poprawnie, że naprawdę wszystkie requesty nie idą do innego tenanta, albo że przez przypadek np. ktoś z cache’a - to jest też opcja budujemy cache’e wiadomo, dla każdego z reguły jakieś dane cache’owe będą i ktoś nagle mi pobierze wszystkie klucze z cache’a. Bo deweloperzy lokalni - nie oszukujmy się - będą testowali to na jednym tenancie. Nie zmusimy ich, żeby lokalnie testowali to na wielu tenantach. Więc szansa, że ten taki mały błąd brak - jednego ifa albo innego enda albo jednego where’a spowoduje nam wyciek danych, bo to w tej perspektywie mnie przeraża najbardziej… Jest dość duża dla mnie i nie ufam. Patrzmy na tą różnicę: Łukasz zdecydowanie woli mieć to w jednym serwisie na poziomie kodu, bo dawno już deweloperami chyba nie pracował. Natomiast ja dużo częściej i mówię: nie ma tej opcji, ta separacja dla mnie jest dużo bardziej ważniejsza. I też może dlatego, że bardziej sektor finansowy tak naprawdę.

Łukasz Kałużny: Ale dobra, zejdźmy sobie, wróćmy do tego poszczególnego właśnie. Jeżeli masz wydzielanie dedykowanych instancji compute’a w jednym klastrze np. Kubernetesowym, to w mojej perspektywie jeżeli musisz to zabezpieczyć, to poza tym, że potrzebuję dedykowaną moc obliczeniową, to jeżeli ktoś mi ucieknie z workloadu czy cokolwiek się stanie - to i tak może pohasać po całym klastrze.

Szymon Warda: Zgadza się, dla mnie dedykowane nodegrupy de facto są o tyle problematyczne, że w tym momencie totalnie nie oszczędzamy i nie robimy większej utylizacji tego, za co płacimy, czyli naszych maszyn wirtualnych.

Łukasz Kałużny: Mamy dwa problemy w jednym.

Szymon Warda: Tak. I co więcej - i to jest kosztowy de facto jeden element, ale drugi to jest taki, że jak nam coś… mamy np. największy klaster, mamy duży klaster, ale załóżmy, każdy klient ma, nie wiem, trzy maszyny wirtualne; to de facto tam mamy bardzo niski poziom Resiliency. Nawet jak nam tam jedna wirtualka padnie to node’y się nie… Czy workloady się nie przerzucą na inne wirtualki i to nie ma większego sensu de facto.

Łukasz Kałużny: Prościej mieć 30-node’owy klaster, niż klaster pocięty na dziesięć takich przestrzeni.

Szymon Warda: Tak dokładnie, bo to jest po prostu bardziej odporne i to ma większy sens, a przy odpowiedniej skali rzeczy będą się działy. I niestety co z tego, że będziemy mieli wielki klaster; jeżeli padnięcie jednej maszyny wyłącza nam usługę dla innego klienta.

Łukasz Kałużny: Dobra, potem ten dedykowany klaster no to jak klient za to płaci; No to wtedy idziemy w takie rzeczy.

Szymon Warda: Tak, dokładnie, to to już wymagałoby, jak to ty ładnie określasz - wysokiego poziomu paranoi, że klient faktycznie niczemu nie ufa i musi być.

Łukasz Kałużny: Raczej… Tak, jedna rzecz, którą można zapewnić. To jest feature biznesowy. To nazwijmy, taka przewaga, feature technologiczny, który dajemy. Jeżeli mamy takie dedykowane np. klastry, mówimy, że: drogi kliencie, w ogóle dedykowane compute’y, inne rzeczy, np. “drogi kliencie, możemy zaszyfrować dane Twoimi kluczami.”

Szymon Warda: Jest jeszcze jeden case, który ja też widuję zapotrzebowanie na takie przypadki; że tworzymy manage service, czyli tworzymy serwis, który generalnie… mówimy Klientowi: okej, zdeployujemy go u Ciebie z subskrypcji, bo tak czasami bezpieczeństwo chce, ale my nim zarządzamy i w tym momencie mamy osobny klaster, bo on jest poza naszą subskrypcją, my nim zarządzamy. Faktycznie jest osobny. To jest też czasami życzenie biznesowe tak naprawdę, że to się pojawia i ma to swoje zastosowanie z punktu widzenia bezpieczników; powiedzmy sobie szczerze - dla krytycznych systemów.

Łukasz Kałużny: Dobra. Kolejny sposób to jest miksowanie tego wszystkiego.

Szymon Warda: Tak. Czyli możemy miksować serwisy, że niektóre serwisy dzielą w aplikacji, gdzieniegdzie mamy per workload’y, a gdzieniegdzie mamy w ogóle podzielone już instancje pod różnych klientów de facto - i to jest też częsty przypadek. To ja bym teraz doszedł do tego, bo jeżeli mówimy o współdzieleniu, to trochę zamienimy kolejność, o której mieliśmy mówić, to często takimi elementami współdzielonymi… to ja widuję dwa: API Gateway/ Ingressy i STS, czyli serwisy uwierzytelniające. Pytanie teraz do Ciebie: współdzielisz czy nie współdzielisz?

Łukasz Kałużny: Ingressy tak. Raczej, inaczej. Infrastruktura taka techniczna, wejściowa, sieciowa - jak najbardziej tak. Z STS’ami jest problem co jest tym STSem? Bo tak jak powiedziałeś; ja mam jeszcze, w zależności czy to jest STS pudełkowy, to jest jakaś usługa, czy to jest kolejna nakładka na przykład na Identity Server czy inne javowe odpowiedniki.

Szymon Warda: Łukasz, zdradzę Ci sekret. I to i to pisali deweloperzy.

Łukasz Kałużny: Nie ale bardziej chodzi mi o potem… Poziom implementacji. Tak jak Ty mówisz, że nie wierzysz w upilnowanie dobre kodu na multi-tenancy w kodzie, że wymaga lepszej jakości developerów, którzy są świadomi tego, co piszą.

Szymon Warda: Znaczy, dla mnie tam ryzyko głupiego błędu - bo są głupie błędy - jest ogromne; i co więcej: reimplementacji tego samego w każdej aplikacji - jeżeli akurat mówimy o aplikacjach takich… Nazwijmy to mikroserwisowych… Jest zbyt drogie, to jest naprawdę kosztowne.

Łukasz Kałużny: Dobra, jeżeli popatrzysz, to ja za to nie ufam deweloperom - jeżeli chodzi o elementy uwierzytelniające, jeżeli to nie jest pudełko.

Szymon Warda: Ale oczywiście…

Łukasz Kałużny: Więc tak, STS dla mnie jest taki, jeżeli to jest implementacja pudełkowa i my tylko zakładamy tam użytkowników, pobieramy tokeny i inne rzeczy; spoko, może być współdzielona. Jeżeli ktoś znowu kolejny raz popełnił jakąś mocno zcustomizowaną nakładkę, to no fucking way, wolę to mieć osobno.

Szymon Warda: Zdefiniujmy, bo mówisz pudełkowa - czy przez pudełkowa, rozumiesz np. Azure AD?

Łukasz Kałużny: Tak.

Szymon Warda: Okej.

Łukasz Kałużny: Na przykład Azure AD B2C gotowe, oAuthy, Okta, AWS Cognito bodajże. Zawsze mi się ta nazwa myli, więc będzie… Czy jestem…

Szymon Warda: Identity Service? Pudełko, czy nie pudełko wg. Ciebie?

Łukasz Kałużny: Jeżeli jest w wersji teraz tej komercyjnej, pudełkowej, gdzie tylko do niego strzelam po API; wstrzykuję konfigurację to tak. A jeżeli ktoś dorobił tam swoje znowu… Sporo swojej logiki i kodu dorobił do tego to wolę mieć to dedykowane.

Szymon Warda: OK, to ja widzę pewną prawidłowość w tym co mówisz. To, że tożsamość i STSy, które sami hostujemy to temu nie ufasz, a to gdzie strzelamy do usługi de facto ufasz. I ja bym się z tym zgodził. Tak, dla mnie to przy tym podziale… Możemy dyskutować jak najbardziej.

Łukasz Kałużny: Tak, o - z opensource’u myślałem o Keycloak’u, bo też ludzie kombinują z nim, więc jak Keycloak jest z pudełeczka używany przez API - super, jeżeli coś wykombinowaliśmy ponad to - to dedykowane.

Szymon Warda: Dobra, a współdzielenie serwisów platformowych?

Łukasz Kałużny: Słuchaj, platformowych… Jeżeli popatrzymy - teraz jest pytanie, jeżeli mamy rzeczy, które nie sięgają w zależności jak to nazwiesz teraz platformowym, bo to jest…

Szymon Warda: Blogowanie, metryki, wszystko co de facto nie dotykają się developerzy z teamów typowo takich biznesowych.

Łukasz Kałużny: Inaczej; tak, możemy współdzielić. Pytanie jest… Jak mówimy o poziomie paranoi, bo metryki tak, ta cała platformówka, tak. Z logami pytanie jest zawsze jedno - czy dane klientów tam trafiają, czy tylko ID-ki.

Szymon Warda: To się zgadza, żeby nie było… Zgadzam się z tym podejściem, nawet powiem więcej. Dla mnie platformowe są zdecydowanie do łączenia, bo szczególnie to już wychodzi chyba powoli z tej rozmowy, że ja jednak jestem za separacją serwisów tak naprawdę, bo jak powiedziałem - nie ufam i to jest zbyt drogie. Dlatego przy takiej ilości serwisów te metryki, logi itd. Muszą być połączone. Ale zgodzę się z Tobą de facto; co się daje w sytuacji jak klient powie że nie, nie, nie - moje logi są zbyt wrażliwe i tak dalej i że trzeba to podzielić. Wtedy klient płaci więcej po prostu i mamy osobne Grafany, osobne Loki i tak dalej, dalej. Podzielone serwisy platformowe? Dla mnie nie. Zarąbiemy się z operations po prostu, zbyt drogie to będzie.

Łukasz Kałużny: Nie, nie, to trzeba też podzielić: infrastruktura… Wydzielić poziom infra, a poziom tych dedykowanych tenantów, single tenantów na tej infrastrukturze.

Szymon Warda: Dobrze, to chyba warpujemy to. Twoja GOAT metoda, że tak powiem.

Łukasz Kałużny: Ona jest - I teraz kładę mój sposób myślenia: nowy produkt, nowy produkt w zależności od określenia, ale to jest kod + podział… Dobranie modelu podziału danych.

Szymon Warda: Zdefiniuj dobranie modelu podziału danych.

Łukasz Kałużny: Czyli B2C… W ogóle zapominamy. Jeden wspólny worek.

Szymon Warda: To się zgadzamy, jak najbardziej.

Łukasz Kałużny: B2B. To jest wykorzystanie podziału per właśnie wydzielanie kontenerów, baz danych i innych takich elementów.

Szymon Warda: OK.

Łukasz Kałużny: I to jest takie way to go dla nowej aplikacji. I teraz bardzo ważne, co podkreślam, dla nowej aplikacji. Dlatego jak przerabiamy jakiś produkt, żeby firma zaczęła świadczyć as a service - i tak skończymy na single-tenantach. Tak więc zróbmy po prostu tą infrę dobrze.

Szymon Warda: OK, to ja się tu nie zgodzę. Czyli zgodzę się częściowo i częściowo się nie zgodzę. Separacja baz danych - jak najbardziej w każdym wymiarze i powinniśmy mieć osobne bazy danych dla klienta, choćby procedury backupu. Wszystko będzie po prostu łatwiejsze. Zresztą klient jest też super kluczem partycji tak naprawdę, więc taniej będzie nam mieć nawet załóżmy te 500 baz; niż jedną bazę, która będzie miała dane z 500 tenantów. Ale jednak jeżeli mówimy - ja bym tu powiedział inaczej, jeżeli byśmy mieli jakieś takie bardziej monolityczne systemy; duże, takie wielkie, wielkie systemy, to tam na poziomie kodu bym się może zastanowił. Tak naprawdę… Bo to można by ogarnąć jakiś sposób. Będziemy mieli na tyle dużo tej infrastruktury, że de facto będzie nas stać na zbudowanie tej infrastruktury takiej aplikacyjnej w kodzie, która by to filtrowała zawsze. Natomiast jeżeli mamy cokolwiek bardziej serwisowego, mamy tych serwisów więcej, to reimplementacja tego samego kawałka logiki odnośnie tenantowości i na wszystkie requesty… To jest tak piekielnie drogie. I tak ryzykowne. Ciężkie w testach. Zdecydowanie ja jestem tu za dzieleniem: serwisy per tenant. Czysto kosztowo i względem ryzyka.

Łukasz Kałużny: Znaczy tak, można tu sobie popatrzeć, chociaż teraz dotnet core teraz to raczej dotnet. Przepraszam, pełen dotnet teraz, bo muszę się odzwyczaić od tego, że jest dotnet core teraz… Gdzieś tam na swoim poziomie już np. pomaga z tym. Tak samo Spring Boot też zaczyna to… Pomagać nam tworzyć takie rzeczy.

Szymon Warda: Bez dwóch zdań tak. Jak mam tych serwisów dużo, to widzę, że jakieś wspólne biblioteki, one się starzeją super szybko. O tym mówiliśmy de facto już nie raz.

Łukasz Kałużny: Nie, słuchaj, dlatego mówię, że rzeczy wbudowane w frameworki, a nie nasze współdzielone biblioteki.

Szymon Warda: Przecież i tak to zostanie obudowane, i tak to zostanie obudowane w jakiegoś frameworka. No nie oszukujmy się.

Łukasz Kałużny: Dobra, czyli kończąc… Każdy z nas ma inną opinię.

Szymon Warda: Agree to disagree. Dokładnie tak.

Łukasz Kałużny: Dokładnie. Trzymajcie się.

Szymon Warda: Na razie. Hej.

Łukasz Kałużny: Hej!