Systemy rozproszone: kiedy należy je budować i jak skalować. Przewodnik krok po kroku.

Zdjęcie Jeremy McKnight na Unsplash

Zawsze uderza mnie, ilu młodszych programistów cierpi na zespół oszusta, kiedy zaczęli tworzyć swój produkt.

Rozumiem, istnieje wiele oszałamiających przykładów najlepszych firm z niewiarygodnie złożonymi systemami rozproszonymi, które potrafią obsłużyć miliardy żądań, z wdziękiem aktualizują setki aplikacji bez przestojów, odzyskują po awarii w kilka sekund, uwalniają co 60 minut i mają małą prędkość czasy reakcji z dowolnego miejsca na świecie.

Oczekiwania te mogą być dość przytłaczające, gdy zaczynasz projekt. Ale jak wielu z was już wie, większość tych firm zaczęła od minimalnego opłacalnego systemu i bardzo słabego stosu technologicznego. Jest po temu prosty powód: nie potrzebowali go na początku. Poświęcenie więcej czasu na projektowanie systemu zamiast kodowania może w rzeczywistości spowodować awarię.

Ten artykuł zawiera instrukcje krok po kroku. Pokażę ci, jak w Visage rozpoczęliśmy od najmniejszego systemu, jaki kiedykolwiek zbudowaliśmy, i zbudowaliśmy podstawowy skalowalny system rozproszony o wysokiej dostępności. To prawdziwe studium przypadku, aby usunąć swoje kompleksy, jeśli nigdy nie miałeś okazji zrobić tego sam.

Kiedy po raz pierwszy przybyłem do Visage jako CTO, byłem jedynym inżynierem. Nie wiedziałem nic o stosie technologii, ale dołączyłem, ponieważ bardzo podobał mi się pomysł rekrutacji bez wewnętrznych rekruterów lub działu HR. To była podstawowa idea Visage: crowdsourcing obsługiwany przez wielu niewidzialnych rekruterów pracujących razem nad twoimi rolami przy pomocy sztucznej inteligencji, która w ciągu kilku dni szukałaby najbardziej odpowiedniego talentu. Następnie angażujesz się bezpośrednio z nimi, bez pośrednika.

„Tłum” w crowdsourcingu natychmiast uruchomił mój mózg inżynierów: będzie wielu ludzi pracujących równolegle, oczekujących dobrej wydajności z dowolnego miejsca na świecie. Podobało mi się wyzwanie.

Ale pod względem systemowym rzeczy były złe, naprawdę złe. Oto, co znalazłem, kiedy przybyłem:

  • Zagrożona instancja Wordpress z setkami przestarzałych wadliwych wtyczek, działająca na maszynie wirtualnej na wspólnym serwerze
  • Zaatakowane skrzynki pocztowe
  • Bzdura z Dokumentów i Arkuszy Google.

I to jest całkowicie normalne. Znów w zespole nie było członka technicznego i spodziewałem się czegoś takiego. Mimo to zespół skoncentrował się na możliwościach biznesowych i sprawił, że produkt wydawał się działać magicznie, robiąc wszystko ręcznie! (Udawaj aż ci się uda to osiągnąć). I to było naprawdę niesamowite.

Nasz pierwszy system (tak, to było do bani, ale to zadziałało)!

Nic dziwnego, że moim pierwszym zadaniem było odtworzenie maszyny wirtualnej, ponowna instalacja zaktualizowanej wersji Wordpress, upewnienie się, że wszyscy zmieniają hasła, ustalają zasady dotyczące haseł i usuwają dziesiątki złośliwego oprogramowania na komputerach firmy… ale przejdźmy do kwestii systemowych.

Od Wordpress do aplikacji internetowej

Na pierwszym miejscu, kiedy zaczynasz budować produkt, muszą być dane. Dane decydują o wartości Twojej firmy. Będzie to to, czego używasz na co dzień do podejmowania decyzji, i to, co pokazujesz inwestorom, aby pokazać postęp.

Musisz zrozumieć swoje dane, a odzyskanie danych z różnych źródeł w różnych formatach będzie ogromną stratą czasu. Wordpress może być bardzo dobrym wyborem w wielu przypadkach, oszczędzając sporo czasu na inżynierii, ale na ich potrzeby zespół Visage musiał zainstalować fantazyjne wtyczki, które nie były już utrzymywane. W rezultacie nie mieliśmy kontroli nad generowanym modelem danych, a dane, które nie pasowały do ​​modelu, zostały rozproszone w dziesiątkach dokumentów i arkuszy kalkulacyjnych.

Więc jeśli nie istnieje produkt, który już odpowiada 90% twoich potrzeb, zastanów się nad idealnym modelem danych i zaprojektuj go i wdrożyć minimalnie opłacalny produkt (MVP), który będzie w stanie pomieścić wszystkie twoje dane.

Pomyśl o API. Twoja aplikacja musi mieć interfejs API. Będzie to miało decydujące znaczenie, gdy w końcu ją sprzedasz. Nie skaluj od razu, ale koduj z myślą o skalowalności. Uczyń swój interfejs API bezstanowym i tak RESTful, jak to tylko możliwe, ponieważ wszyscy będą oczekiwać, że będą mogli go zapytać przy użyciu standardowych metod HTTP.

W naszym przypadku wybraliśmy NodeJS, ponieważ większość naszego kodu będzie po prostu przetwarzać dane wejściowe i wyjściowe. NodeJS nie jest blokujący i zawiera bibliotekę, która jest wygodna w projektowaniu interfejsów API: ExpressJS.

Jeśli potrzebujesz witryny skierowanej do klienta, masz kilka opcji. Najpierw możesz utworzyć warstwę na serwerze aplikacji, która będzie generować strony, lub możesz zbudować aplikację JavaScript Single Page, która będzie obsługiwana przez statyczny serwer hostingowy.

W Visage wybraliśmy drugą opcję i postanowiliśmy stworzyć jedną aplikację dla użytkowników i jedną dla administratorów. Stało się tak po prostu dlatego, że mielibyśmy o wiele większe oczekiwania względem użytkowników niż potrzebowaliśmy od administratorów i chcieliśmy zachować prostotę obu baz kodu (również z uwagi na kwestie CORS później). Tak wyglądał nasz system:

Wszystkie dane w jednym miejscu

Delegowanie poufnego przechowywania danych wcześnie

O ile nie jest to krytyczne dla Twojej firmy, nie ma dobrego powodu do przechowywania poufnych danych osobowych w swoich systemach. Bezpieczeństwo to złożona sprawa, a jeśli codziennie modyfikujesz kod, dopóki nie dopasujesz swojego produktu do rynku, zepsuje się. Załóżmy, że ktokolwiek źle myślący może złamać twoje podanie, jeśli naprawdę tego chce.

Kluczem jest tutaj, aby nie przechowywać żadnych danych, które byłyby szybką wygraną dla hakera. Nikt nie rabuje banku, który nie ma pieniędzy. Jeśli projektujesz produkt SaaS, prawdopodobnie potrzebujesz uwierzytelnienia i płatności online. Istnieje wiele stron trzecich, z którymi możesz się zintegrować, które zajmą się tym w znacznie lepszy sposób niż to możliwe.

Na przykład Auth0 jest najbardziej znaną firmą zewnętrzną obsługującą uwierzytelnianie. Stripe to także dobra opcja do płatności internetowych. Poświęcą wszystkie swoje zasoby i najlepsze zespoły inżynierii bezpieczeństwa na świecie, aby zapewnić bezpieczeństwo Twoich danych - w przeciwnym razie nie będą prowadzić firmy.

Rzeczywisty znak na samochodzie w San Francisco

Usługi w chmurze są twoimi najlepszymi przyjaciółmi

W tym momencie mieliśmy sposób na przechowywanie wszystkich naszych danych, uwierzytelniania, płatności online i aplikacji internetowej, z której klienci mogliby korzystać, wraz z interfejsem API, który mogliśmy sprzedawać partnerom w różnych przypadkach. Nasza baza użytkowników rosła i stało się oczywiste, że chcą mieć dostęp do aplikacji w dowolnym momencie. Czas więc pomyśleć o skalowalności i dostępności.

Polegaliśmy na jednym serwerze, ale mógł on obsłużyć tylko tyle żądań, a zmiana serwerów lub wydanie nowej wersji oznaczałoby usunięcie aplikacji podczas wydania. Naszymi kolejnymi priorytetami były: równoważenie obciążenia, automatyczne skalowanie, rejestrowanie, replikacja i automatyczne tworzenie kopii zapasowych. Oczywiście, jeśli jesteś jedynym inżynierem w swojej firmie, próba rozwiązania wszystkich tych problemów na własną rękę byłaby kompletnym szaleństwem.

Na szczęście żyjemy w czasach, gdy tylko jeden dobrze zaokrąglony inżynier może łatwo zbudować taki system w ciągu kilku dni za pomocą usług chmurowych, takich jak Amazon Web Services, Google Cloud Services lub Azure. Zdecydowaliśmy się przenieść nasze systemy do AWS, ponieważ w tym czasie było to najbardziej kompletne rozwiązanie i mieliśmy 2 lata darmowych kredytów.

Właśnie dlatego w tym poście będę głównie mówił o rozwiązaniach AWS, ale na innych platformach są równoważne usługi. Jest to również czas, w którym postanowiliśmy uruchomić nasze moduły w kontenerach Docker z wielu innych powodów, które nie zostaną omówione w tym poście (więcej informacji można znaleźć w tym artykule: https://medium.freecodecamp.org / amazon-fargate-goodbye-infrastruktura-3b66c7e3e413).

To, jak zdecydujesz się uruchomić aplikacje, zależy od konkretnego przypadku użycia, takiego jak elastyczność, której potrzebujesz, w porównaniu do czasu, jaki możesz poświęcić na zarządzanie infrastrukturą.

Nie ma dobrej ani złej odpowiedzi.

Możesz dokonać kontenerizacji wszystkich modułów i użyć systemu zarządzania kontenerami, takiego jak ECS / EKS w AWS lub silnik Kubernetes w GCP. Jeśli nie, a nie chcesz samodzielnie zajmować się automatycznym skalowaniem i równoważeniem obciążenia, możesz użyć Elastic Beanstalk lub App Engine.

Jeśli chcesz przejść na tryb bezserwerowy, możesz także połączyć korzystanie z funkcji Lambda i API Gateway. Zdecydowaliśmy się na ECS. Wdrożyliśmy 3 instancje w 3 strefach dostępności, moduł równoważenia obciążenia, skonfiguruj automatyczne skalowanie w zależności od użycia procesora, zintegrowaliśmy wszystkie dzienniki naszych kontenerów z Cloudwatch i skonfiguruj mierniki, aby obserwować błędy, połączenia zewnętrzne i czas odpowiedzi API.

Wysoka dostępność: czy wiesz, że żyrafy prawie nigdy nie śpią? 99% czasu pracy

W naszej bazie danych wykorzystaliśmy MongoDB, ponieważ nasz model dobrze pasuje do bazy danych NoSQL i ze względu na wysoką spójność. Zdecydowaliśmy się skorzystać z MongoDB Atlas i wdrożyliśmy 3 repliki, aby umożliwić wysoką dostępność. Wśród innych usług Atlas zapewnia automatyczne skalowanie, automatyczne kopie zapasowe i umożliwia płynne cofanie się w czasie w przypadku awarii.

Postanowiliśmy również hostować wszystkie nasze statyczne pliki internetowe w S3 i użyliśmy Cloudfront jako CDN, aby nasze aplikacje JS mogły ładować się bardzo szybko w dowolnym miejscu na świecie i być obsługiwane tyle razy, ile zażądano. Cloudflare jest również dobrą opcją i oferuje ochronę DDOS od razu po wyjęciu z pudełka.

Dla uproszczenia postanowiliśmy użyć Route 53 jako naszego DNS, używając ich serwerów nazw dla wszystkich naszych domen. To jedna z moich ulubionych usług w AWS. To sprawia, że ​​twoje życie jest o wiele łatwiejsze. Za każdym razem, gdy chcesz obsłużyć coś za pośrednictwem nazwy domeny, bez względu na to, czy jest to instancja EC2, elastyczny adres IP, moduł równoważenia obciążenia, dystrybucja w chmurze, czy cokolwiek naprawdę, prywatnie lub publicznie, zajmuje to kilka minut, ponieważ jest tak dobrze zintegrowana ze wszystkimi inne usługi.

Połącz to z Menedżerem certyfikatów, który pozwala uzyskać certyfikaty SSL (w tym symbole wieloznaczne) za darmo w kilka minut i wdrożyć je na wszystkich serwerach, zaznaczając odpowiednie pole, a masz najszybszy i najbardziej niezawodny sposób włączenia HTTPS we wszystkich modułach. Do widzenia Certyfikaty SSL „Let's Encrypt”, które musiałem odnawiać i instalować na moich serwerach co około 3 miesiące .

Zaczyna wyglądać przyzwoicie

Wybierz strategię buforowania

Wszyscy nie znoszą zarządzania pamięcią podręczną, buforowanie może się zdarzyć na wielu różnych warstwach, a problemy związane z pamięcią podręczną są trudne do odtworzenia, a koszmarem do debugowania.

Niestety wydajność systemów rozproszonych zależy w dużej mierze od dobrej strategii buforowania. Jest wiele dobrych artykułów na temat dobrych strategii buforowania, więc nie będę wchodził w szczegóły. Po prostu wiedz, że jeśli twoje statyczne zasoby internetowe są duże, prawdopodobnie będziesz chciał skorzystać z pamięci podręcznej przeglądarki użytkownika, sprytnie używając nagłówka sterującego pamięcią podręczną.

Jeśli strony skierowane do użytkownika są generowane na serwerach aplikacji w kółko, użyj buforującego serwera proxy, takiego jak Squid. Ale co najważniejsze, istnieje duża szansa, że ​​będziesz ciągle wysyłać te same żądania do bazy danych. Aby zmniejszyć obciążenie bazy danych i zaoszczędzić czas przesyłania danych, użyj systemu buforowania obiektów pamięci, takiego jak memcached, dla obiektów, które często były używane i rzadko aktualizowane.

Zaczęliśmy rozważać użycie memcached, ponieważ często prosiliśmy o te same profile kandydatów i oferty pracy w kółko. Wdrożenie go na maszynie zoptymalizowanej pod kątem pamięci zwiększyło naszą wydajność API o ponad 30%, gdy uśredniliśmy czasy odpowiedzi wszystkich żądań w ciągu dnia. Memcached jest również dystrybuowany, więc może działać na różnych serwerach, ale nadal działa tak, jakby to była tylko jedna duża pamięć do przechowywania twoich obiektów.

pamięć podręczna, pamięć podręczna wszędzie

Lokalizacja, lokalizacja, lokalizacja

Teraz mamy system rozproszony, który nie ma ani jednego punktu awarii (jeśli weźmiesz pod uwagę ELB AWS i rozproszone memcached) i może automatycznie skalować w górę i w dół. Używamy również buforowania, aby zminimalizować przesyłanie danych w sieci. Wygląda całkiem nieźle. W tym momencie prawdopodobnie chcesz przeprowadzić audyt swoich stron trzecich, aby sprawdzić, czy one przejmą obciążenie tak samo jak Ty.

Jednak niektórzy z naszych użytkowników narzekali, że aplikacja była dla nich nieco wolniejsza, szczególnie gdy przesyłali pliki. Rzeczywiście, nawet jeśli nasze statyczne pliki internetowe były buforowane na całym świecie (dzięki uprzejmości CDN), wszystkie nasze serwery aplikacji zostały wdrożone tylko na zachodzie Stanów Zjednoczonych. Użytkownicy z Azji Wschodniej doświadczyli znacznie większych opóźnień, szczególnie w przypadku dużych transferów danych.

Rozwiązanie było proste: wdrożyć dokładnie ten sam klaster ECS w nowym regionie w Azji wraz z nowym modułem równoważenia obciążenia i polegać na trasowaniu według trasy zbliżeniowej Route 53, aby kierować użytkowników do „najbliższego” modułu równoważenia obciążenia. MongoDB Atlas umożliwia także wdrażanie replik w różnych regionach, dzięki czemu nie było wymagane dodatkowe prace.

I oto jesteśmy ! Nasz system rozproszony jest gotowy.

Wniosek

Chociaż rozproszony system, który tu widzisz, został uproszczony w tym poście, sprawdziliśmy części, które najprawdopodobniej zobaczysz w wielu nowoczesnych aplikacjach internetowych. Inne tematy związane, ale nie omówione, to architektura mikrousług, przechowywanie i szyfrowanie plików, dzielenie bazy danych, zaplanowane zadania, asynchroniczne obliczenia równoległe… może w następnym poście!

Chodzi mi o to: nie staraj się budować idealnego systemu, kiedy uruchamiasz swój produkt. Większość wyborów dotyczących projektu zależy od tego, co robi Twój produkt i kto go używa. Będziesz wiedział tylko, że kiedy osiągniesz odpowiedni poziom rynku produktowego i zaczniesz mieć dobry przegląd swojej bazy użytkowników, a to może potrwać miesiące, a nawet lata.

Skoncentruj się na tym, aby dowiedzieć się, czego ludzie potrzebują, i spróbuj znaleźć rozwiązanie ich problemu, nawet jeśli ma wiele ręcznych kroków. Następnie pomyśl o sposobach automatyzacji, poświęć swój czas na kodowanie i niszczenie oraz używaj stron trzecich tam, gdzie ma to sens.

Nie skaluj, ale zawsze myśl, koduj i planuj skalowanie. Zbuduj swój system krok po kroku, nie rozwiązuj problemów projektowych w oparciu o funkcje, które nie są jeszcze dojrzałe, i na koniec zawsze staraj się znaleźć najlepszy kompromis między czasem, który spędzisz, a zyskiem wydajności, pieniędzy i obniżonej wydajności ryzyko.

Jeśli podoba Ci się ten artykuł i uważasz, że jest on przydatny, kliknij ten przycisk klaśnięcia i śledź mnie, aby uzyskać więcej artykułów na temat architektury i programowania!