Knowledge base
Chmurowa architektura aplikacji - buduj elastycznie
Posted by Pomoc Oktawave on 30.06.2016 12:38

Projektując aplikację webową budowaną w oparciu o zasady wysokiej dostępności i o wydajności zoptymalizowanej pod względem zmiennych wymagań biznesowych, można zastosować dwa podejścia, zależnie od środowiska, w którym aplikacja zostanie postawiona. 

 

Wariant klasyczny

Najbardziej popularny jest scenariusz zakładający, że aplikacja będzie funkcjonowała całkowicie w infrastrukturze, którą sami dostarczamy i skonfigurujemy. To klasyczny sposób budowania architektury aplikacji, a jego wybór determinuje wiele elementów na różnych poziomach, o które należy zadbać.

Z perspektywy użytkownika kluczowe jest, by jego zapytania o naszą aplikację dotarły do celu, zatem dostępność infrastruktury staje się priorytetem. Przy założeniu, że wszystko budujemy zgodnie z zasadami wysokiej dostępności (HA), powinniśmy zapewnić co najmniej dwa łącza o odpowiedniej przepustowości od niezależnych dostawców, oferujących wysokie SLA. Same łącza nie zagwarantują jednak pełnej redundancji, zatem należy na naszych urządzeniach brzegowych wdrożyć protokół BGP, co wiąże się między innymi z wykupieniem systemu autonomicznego (Autonomous System, AS).

Kolejnymi elementami, na które w wariancie klasycznym należy zwrócić uwagę, są:

  1. bezpieczeństwo w warstwie sieciowej, zapewniane poprzez wdrożenie firewalla, umożliwiającego zablokowanie ruchu na niepożądanych portach,
  2. kierowanie ruchu na komponenty serwujące naszą aplikację, co znacznie ułatwi wdrożenie mechanizmu takiego jak load balancer.

Niezależnie od tego czy wymienione powyżej elementy występują w postaci rozwiązań sprzętowych czy też wirtualnych, powinny być dublowane tak, aby w wypadku awarii nasza aplikacja była niezmiennie dostępna.

Kolejnym istotnym elementem naszego ekosystemu jest warstwa serwerowa, którą możemy również nazwać warstwą mocy obliczeniowej. Przygotowując wdrożenie, warto pamiętać, aby przeprowadzić możliwie jak najdokładniejszą estymację wymaganych parametrów.

Często dużą trudność stanowi tu próba oszacowana, jaką liczbę procesorów oraz pamięci powinniśmy zapewnić, aby nasza aplikacja z chwilą startu funkcjonowała wydajnie. Jeżeli dołożymy do tego kolejne zmienne w postaci zakładanego wzrostu ruchu, maksymalnego ruchu chwilowego czy też okresowo zwiększonego, może się okazać, że optymalne wyskalowanie infrastruktury fizycznej jest praktycznie niemożliwe. Tymczasem konsekwencje błędnych założeń mogą być dwojakie:

  1. przeszacowanie potrzeb - gdy okaże się, że nadmiernie i niepotrzebnie wzrósł koszt wdrożenia i mimo że część mocy obliczeniowej nie jest wykorzystywana, ponosimy koszt jej utrzymania (np. licencje, prąd, chłodzenie, support producenta),
  2. niedoszacowanie potrzeb - tutaj największym wyzwaniem jest czas i możliwość uzupełnienia brakujących zasobów w razie potrzeby. Na sprzęt można czekać i kilka tygodni, gdy w tym czasie nasi klienci będą zmagać się z problemem niskiej wydajności aplikacji. Ponadto nieprzewidziany w fazie planowania zakup sprzętu może wpłynąć na rentowność całego projektu.

Po zapewnieniu odpowiedniej mocy obliczeniowej, którą będzie stanowiło przykładowo rozwiązanie typu blade, przystępujemy do tworzenia konkretnego środowiska serwerowego. Na potrzeby naszego scenariusza zakładamy wykorzystanie wirtualizacji, stąd będziemy konfigurować maszyny wirtualne.
Proponowana architektura zakłada podstawowy podział na trzy grupy:

  1. serwery aplikacyjne - w tym wypadku będą to zwielokrotnione serwery zapewniające ogólnie pojętą logikę aplikacji np. frontend, backend,
  2. serwery bazodanowe - serwery z silnikami bazodanowymi w konfiguracji wysokiej dostępności,
  3. serwery serwujące elementy statyczne aplikacji. W założeniu utworzenie tej grupy maszyn ma na celu odciążenie serwerów aplikacyjnych z obowiązku przetwarzania zapytań dotyczących grafik, zdjęć czy dokumentów.

Najlepszym podejściem jest ich separacja na poziomie sieci przy wykorzystaniu VLAN, z jednoczesnym umożliwieniem komunikacji pomiędzy serwerami aplikacyjnymi a bazodanowymi.

Ostatnim elementem, który należy zapewnić, jest przestrzeń dyskowa. Podobnie jak z mocą obliczeniową, już na poziomie analizy potrzeb musimy dobrze ocenić, ile jej potrzebujemy oraz jakiej wydajności. Od decyzji, które podejmiemy na tym etapie, będzie uzależnione, jakie rozwiązanie sprzętowe zakupimy oraz w jakiej konfiguracji. Problem błędnych decyzji w tym zakresie będzie miał podobne konsekwencje, jak w przypadku mocy obliczeniowej. Może się zatem okazać, że zastosowanie niewystarczająco wydajnego rozwiązania dyskowego uniemożliwi aplikacji płynne działanie, a nawet dojdzie do jej zatrzymywania. Z drugiej jednak strony znów istnieje spore ryzyko przeszacowania potrzeb i poniesienia nadmiernych wydatków względem realnych potrzeb.

Ryzykowny bywa również etap kreowania konkretnych zasobów dyskowych. Jak wiemy, można wykorzystać do ich tworzenia zarówno same dyski talerzowe, dyski SSD oraz wykorzystać dyski SSD jako cache dla dysków talerzowych. Dzięki temu uzyskujemy możliwość optymalnego wykorzystania posiadanych zasobów sprzętowych. Tu także powraca kwestia analizy potrzeb, zatem należy uniknąć sytuacji, gdy posiadane zasoby nie będą adekwatne do wymogów aplikacji. Co prawda wprowadzenie stosownych zmian po fakcie będzie możliwe, ale zapewne pracochłonne, a na pewno wymuszające przestój aplikacji. Wypada tu także podkreślić, jak dużym obciążeniem finansowym są licencje hypervisora, koszty konserwacji sprzętu oraz podzespoły przechowywane na wypadek awarii sprzętu.


Scenariusz w chmurze

Drugim wariantem budowania architektury aplikacji jest wykorzystanie chmury publicznej oraz jej wbudowanych mechanizmów i narzędzi.

Różnice między tworzeniem rozwiązania w środowisku cloudowym widać już w warstwie dostępowej. Przede wszystkim w scenariuszu zakładającym wykorzystanie chmury odpada problem związany z łączami. Wysoko dostępne łącze o dużej przepustowości wraz z urządzeniami brzegowymi zapewnia nam dostawca chmury. W ramach platformy możemy wykorzystać gotowy mechanizm balansowania ruchu, który wykorzystamy zarówno dla protokołu HTTP, HTTPS, jak i dla wielu innych, np. SIP. Jednocześnie wyposażony jest w różne mechanizmy, np. badające stan aplikacji oraz różne algorytmy balansowania ruchu, takie jak "Least Connection" lub "Least Response Time".

Jednak największa zmiana w architekturze uwidacznia się od warstwy mocy obliczeniowej w dół: elementy fizyczne całkowicie bowiem przestają być przedmiotem zainteresowania administratora. Takie parametry jak procesory czy pamięć są po prostu dostępne i jesteśmy w stanie swobodnie nimi zarządzać w ramach każdej instancji serwera (OCI). Co istotne, możemy nimi zarządzać na kilka sposobów. Podstawowym jest ręczna modyfikacja parametrów przy użyciu portalu administracyjnego.

Bardziej zaawansowane możliwości oferuje jednak Autoskaler. Dzięki mechanizmowi Autoskalera mówimy już nie tylko o zarządzaniu mocą obliczeniową poszczególnych serwerów, ale o zarządzaniu wydajnością aplikacji. Autoskaler umożliwia bowiem skalowanie naszej infrastruktury zarówno horyzontalnie - czyli dodając nowe instancje OCI poprzez klonowanie, jak również wertykalnie, czyli dodając dodatkowe procesory lub pamięć. To wszystko może się odbywać w sposób automatyczny na podstawie ustawionej przez nas polityki, a platforma podejmuje decyzje o zmianie parametrów na podstawie ich aktualnego wykorzystania.

Drugim mechanizmem, który ułatwia pracę administratora jest Scheduler. Umożliwia on zaplanowanie zadań, które mają być wykonane na naszej infrastrukturze. Bardzo dobrym przykładem wykorzystania Schedulera jest sytuacja, kiedy jakiś moduł aplikacji jest potrzebny w konkretnych dniach tygodnia lub widoczne jest zwiększenie ruchu w określonych godzinach. Dzięki Schedulerowi możemy zaplanować najpierw włączenie konkretnych OCI, a następnie ich wyłączenie.

Pomocnym narzędziem, które dostaniemy do dyspozycji jest w chmurze jest RESTful API. Możemy je wykorzystać zarówno w przypadku realizacji powtarzalnych czynności na wielu OCI, jak i do skalowania naszej aplikacji. Istotną zaletą jest możliwość wykorzystania API wspólnie z narzędziami takimi jak Ansible, Chef, Puppet, Salt.

Jeżeli nasze potrzeby dotyczące automatycznego skalowania środowiska będą wykraczały poza standardowe funkcjonalności Autoskalera, dobrym rozwiązaniem będzie wykorzystanie do tego API. Uwzględniając w aplikacji mechanizmy badania np. czasów odpowiedzi, może ona po przekroczeniu zadanych parametrów wywołać stosowne metody API.

Kolejna duża zmiana wiąże się z zastosowaniem obiektowej przestrzeni dyskowej (OCS), która zastąpi całą grupę serwerów odpowiedzialnych za serwowanie elementów statycznych aplikacji. Dzięki zastosowaniu bibliotek programistycznych dostępnych dla praktycznie wszystkich języków programowania (.NET, C/C++, PHP, itp.) aplikacja zyskuje możliwość dostępu do OCS. Taka integracja umożliwia wykorzystanie obiektowego storage’u do przechowania elementów statycznych w formie obiektów wraz z metadanymi. Jednocześnie OCS jest w stanie serwować użytkownikom aplikacji znajdującą się na nim zawartość bez dodatkowych pośredników. Ważną zaletą takiego podejścia jest eliminacja wielu maszyn wirtualnych, wymagających nakładu pracy związanego z ich utrzymywaniem.

Konkretne korzyści pojawiają się także w zakresie przestrzeni dyskowej. Przede wszystkim znikają ograniczenia związane z fizycznym rozmiarem posiadanych zasobów. Jeżeli zaistnieje potrzeba ich zwiększenia, po prostu stworzymy nowy zasób i zaczniemy z niego korzystać.

W scenariuszu z wykorzystaniem chmury wydajność przestrzeni dyskowych również przestaje być problemem. Posiadając udostępnionych pięć poziomów, od Tier-1 o wydajności 1 000 IOPS do Tier-5 z wydajnością 200 000 IOPS, jesteśmy w stanie tworzyć zasoby odpowiadające naszym potrzebom. Co jest niezwykle istotne - zmiana wydajności np. z Tier-2 na Tier-3 jest możliwa, i to w locie oraz w sposób przezroczysty dla systemu operacyjnego. Oczywiście zarządzać parametrami możemy zarówno z panelu, jak i za pomocą API. Podobnie jak w przypadku mocy obliczeniowej jeżeli aplikacja będzie weryfikowała stan dysków może odpowiednio wywołać metody odpowiedzialne za zmianę Tieru lub rozmiaru dysku OVS.


Fotosik, czyli w kierunku elastyczności

Sprawdźmy, jak chmurowe rozwiązania sprawdzają się w praktyce?

Dobrą ilustracją jest przykład klienta Oktawave, a zarazem jednego z największych polskich serwisów hostujących zdjęcia. Serwis Fotosik.pl przeniósł do chmury gigantyczną infrastrukturę, składającą się z ponad 13 terabajtów zdjęć i formatów wideo, ponad 65 milionów plików i ponad 20 maszyn dedykowanych. Przed migracją serwisu do chmury, infrastruktura aplikacji zbudowana była z:

  • load balancera rozdzielającego ruch na cztery serwery dedykowane, obsługujące stronę WWW,
  • czterech serwerów baz danych w tym dwóch działających w trybie master/slave,
  • serwera kopii bezpieczeństwa baz danych,
  • serwera statystyk,
  • dwóch serwerów do generowania różnych formatów zdjęć,
  • 15 serwerów serwujących pliki statyczne (zdjęcia).

Po migracji infrastruktura została zoptymalizowana do następujących rozmiarów:

  • load balancer rozdzielający ruch na dwa serwery obsługujące stronę,
  • dwa serwery baz danych,
  • cztery serwery serwujące pliki statyczne,
  • dwa serwery obsługujące wspólne dyski dla serwerów obsługujących stronę i serwujących pliki statyczne.

Do przechowywania wszystkich zdjęć oryginalnych użyty został Oktawave Cloud Storage (OCS).

Już na pierwszy rzut oka widać, jak bardzo środowisko zostało uproszczone. Wiąże się to również ze znaczną redukcją kosztów utrzymania. Administrowanie stało się także łatwiejsze i mniej czasochłonne, przede wszystkim dlatego, że wymagana moc serwerów dobierana jest automatycznie w zależności od obciążenia. Co istotne, korzyści były także widoczne z punktu widzenia użytkownika końcowego. Serwis zwiększył szybkość działania, pomimo rosnącej liczby użytkowników (więcej szczegółów dotyczących migracji znajduje się pod adresem https://www.oktawave.com/_files/Oktawave_Case_Study_Fotosik_PL.pdf).

Podsumowując: chmura zdejmuje z nas ograniczenia sprzętowe w zakresie warstwy sieciowej, serwerowej i przechowania danych. Nie musimy kupować ani utrzymywać urządzeń, a w dalszej perspektywie myśleć o ich wymianie w związku ze zużyciem.

Unikamy ryzyka niewłaściwego oszacowania potrzebnych zasobów, a co za tym idzie ponoszenia dodatkowych kosztów. Ponadto otrzymujemy bezpieczną, redundantną infrastrukturę zapewniającą parametry dokładnie takie, jakie są nam potrzebne.

Czego jeszcze możemy oczekiwać od chmury? Dostępu do narzędzi, które umożliwiają łatwo oraz elastycznie zarządzać infrastrukturą oraz szybko ją uruchamiać na potrzeby np. krótkookresowych projektów. Także w trakcie ciągłego rozwoju aplikacji, wykorzystując takie narzędzia jak GIT czy Jenkins, jesteśmy w stanie przeprowadzać automatyczne testy funkcjonalne lub wydajnościowe. Zyskujemy w takim wypadku możliwość podnoszenia różnych środowisk testowych o zróżnicowanych parametrach oraz wygaszania ich po zakończonych pracach.

Takie podejście do zarządzania infrastrukturą znacznie przyspiesza i ułatwia dostarczenie webowej aplikacji, uwalnia od niepotrzebnych kosztów sprzętu, jego składowania, serwisowania, a w połączeniu z rozliczaniem godzinowym sprawia, że ponosimy koszt mocy obliczeniową, której realnie potrzebowaliśmy.