Stworzenie stabilnej, łatwo skalowalnej architektury, która jest prosta w utrzymaniu i optymalna, jeśli chodzi i koszt utrzymania, to nielada wyzwanie. Ponieważ czasem bywa wręcz dramatycznie, my na przekór postanowiliśmy nasze zmagania z usługami AWS, Kubernetesem i innymi rozwiązaniami opisać w sposób humorystyczny.
*Disclaimer: Poniższy artykuł ma zabarwienie humorystyczne i pewne sytuacje przedstawia w krzywym zwierciadle. Nie mniej, zmagania zespołu sysops są w pełni prawdziwe.
Prolog
Spośród projektów, które realizowaliśmy, jeden szczególnie pozostał nam w pamięci, ponieważ był to nasz debiut, jeśli chodzi zbudowanie koncepcji architektury oraz utrzymanie infrastruktury dla globalnego serwisu, o trudnej do oszacowania docelowej skali. Opowiem Wam o naszych zmaganiach, wzlotach i upadkach. Momentach zwątpienia i chwały. Jak się przekonaliśmy była to niezła przygoda.
Tak to się zaczęło… A przynajmniej, tak to zapamiętaliśmy.
Scena 1: Przychodzi Klient
Klient: “Mam pomysł, będę bogaty… potrzebuję globalnego serwisu wysokiej dostępności, pozwalającego na łatwą integrację z zewnętrznymi API, bezpiecznego zarówno w warstwie programowej, jak i architektury, docelowo z wersją mobilną na IOS i Android. Tylko tyle i aż tyle. Robimy?”
inProjects Team: “Robimy! Najpierw mały research, później machniemy PoC i zobaczymy, co z tego wyjdzie.”
Na tym etapie nie ma jeszcze, żadnej niebanalnej historii w tle. Tylko pot i łzy. Wzięliśmy się do roboty. W skrócie nasze kroki wyglądały następująco:
1. Wybór języka i frameworka: bez zbędnych dyskusji padło na duet znany i lubiany: Python i Django.
2. Poszukiwanie rozwiązania do wdrożenia kluczowej funkcjonalności platformy:
-
-
- Podejście 1: HomeMadeJanuszFreeSoft – spaliliśmy trochę godzin, na poszukiwanie rozwiązania relatywnie taniego, żeby na końcu stwierdzić: “Nie działa.”
- Podejście 2: Licencjonowane rozwiązanie komercyjne – szybko trafiamy na rozwiązanie przystępne cenowo, globalne, stabilne i rozwojowe.
-
3. Przygotowanie środowiska: na nowej wirtualce wg. starego oklepanego mechanizmu stawiamy kolejno:
-
-
- venv;
- uWSGI;
- Nginx;
- lokalne środowiska wg. potrzeby na własnych komputerach;
-
4. Development PoC’a.
Scena 2: Demo PoC’a
Klient: “Biorę to! PoC jest taki, jak trzeba. A teraz działamy na poważnie. Serwis będzie globalny więc zaprojektujcie to dobrze, bo jak raz postawimy to musi stać i działać. Ma być szybko, tanio i dobrze ;-).”
inProjects Team: “Zrobimy. Będzie Pan zadowolony.”
Chociaż najpierw po cichutku, w głowach zadaliśmy sobie pytanie: “Ciekawe, jak ma na drugie imię? Może jakoś na J?
![AWS](https://inprojects.pl/assets/tanio-dobrze-szybko-1.png)
Jak do tego doszło? Do końca nie wiemy, ale okazało się, że mamy nie tylko wyprodukować oprogramowanie. Mamy je również osadzić na samodzielnie zaprojektowanej architekturze, w sposób odpowiedni dla serwisu, który ma zagwarantować wysoką dostępność usługi oraz być gotowy na przyjęcie dużego obciążenia. Oczywiście nie daliśmy po sobie tego poznać, ale był moment w którym poczuliśmy, że robi nam się nieco cieplej i bynajmniej nie była to wina źle działające klimatyzacji. Pojawiło się wyzwanie, z którym musieliśmy się zmierzyć.
Scena 3: Chwytamy byka za rogi i badamy, co to za byk
Szczęśliwie nasz zespół ma ludzi zorientowanych zarówno dewelopersko jak i devopsowo, dlatego do tematu mogliśmy podejść kompleksowo. Zaczęliśmy od zdefiniowania wymagań dotyczących infrastruktury.
Założenia:
- globalny zasięg;
- skalowalność w pionie i poziomie (teoretycznie nieograniczona);
- customizacja – możliwość podnoszenia niezależnych instancji pod konkretne wymagania klienta lub osadzanie w serwisach klientów i personalizacja aplikacji mobilnych;
- bezpieczeństwo – polityki (security groups, IAM, szyfrowanie);
- modułowość – możliwość dokładania kolejnych elementów do systemu, bez znaczącego wpływu na całość;
- prosty development i wdrażanie kolejnych releasów na produkcję;
- przewidywalne koszty;
- 99.99999999% SLA;
- asynchroniczność (także w we współpracy z zewnętrznymi API);
- backupy/monitoring;
Scena 4: Wiemy z czym się mierzymy, wiemy co musimy osiągnąć. Tylko jak?
Do określonych założeń stworzyliśmy listę możliwych rozwiązań. Po przeczytaniu całego internetu i uwzględnieniu dobrych rad zaprzyjaźnionych fachowców, pozostały nam następujące opcje:
Zagadnienie | Opcje |
---|---|
Zasięg | AWS, Azure(tfu), Cloudflare, Oracle |
Bezpieczeństwo | CDN (np: CloudFlare) |
Koszty | możliwie najniższe, ale nie kosztem jakości |
Skalowalność | proxy, LoadBalancing, mikroserwisy (nie wiedzieliśmy tego od razu) |
Dostępność | HA, CDN, chmura |
Kopie zapasowe | chmura? |
Wdrażanie/utrzymanie | CI/CD, kontener/wirtualizacja, modułowość/mikroserwisy |
Integracja | async, API, Celery, Redis/SQS |
DB | Mongo, Postgres |
Jak widać, rozwiązania oparte o chmurę nas mocno kusiły. Ponieważ Microsoft ze względów ideologicznych nas zniechęcał, zaś opinie kolegów z branży na temat jakości usług w Oracle nie były pozytywne, to AWS pod względem dojrzałości produktu nie miał konkurencji. Nadal jednak mieliśmy pewne obszary na mapie do zbadania. Chodziło o integrację usług AWS, CI/CD i konteneryzację.
![AWS](https://inprojects.pl/assets/gkee3xdcfdz11.png)
Scena 5: Zaczynamy prawdziwą zabawę
Początkowo nasze prace przebiegały dwutorowo. Programiści rozpoczęli pracę nad projektem i podstawową wersją serwisu, a devopsi, pod dowództwem Wasyla, nad budową stacka technologicznego. Chmura dawała nam skalowalność, dostępność i koszty za faktycznie wykorzystane usługi, a kontenery niezależność środowisk, łatwiejszy i szybszy proces wdrażania.
Względnie bezproblemowo dotarliśmy do punktu, w którym nasz produkt osadzony był w AWS na Kubernetes. No dobra… Bezproblemowo, to trochę przesadzona wersja zdarzeń 😉 Prawda jest taka, że początki były trudne, a później było tylko gorzej 😉
Na starcie bardzo chcieliśmy skorzystać ze wszystkich wodotrysków AWS, bo połknęliśmy za dużo marketingowej paplaniny. Spotkała nas za to kara…
- EKS okazał się totalnym niewypałem ze względu na sposób implementacji i ograniczenia w liczbie uruchomionych kontenerów (PODów) per serwer, jakie z tego wynikały. Postanowiliśmy, że zrobimy to po swojemu i na naszych warunkach. Nie będzie nam AWS dyktował, jak mamy klaster stawiać!
- Bjoern vs uWSGI; Bjoern skusił lekkością i szybkością działania (przynajmniej w teorii), ale kopnął nieprawidłową obsługą mediów. Decyzja: wracamy do tego, co znamy i lubimy.
Część rozwiązań okazała się jednak dla nas odpowiednia:
- RDS As A Service vs Postgres hostowany u nas. Tutaj nie było argumentów przeciwko usłudze Amazona. Mechanizmy replikacji, wysoka dostępność, święty spokój,
- EFS vs Portworx; póki co dane przechowujemy na EFS ze względu na łatwość implementacji oraz utrzymania. W niedalekiej przyszłości mamy zamiar przesiąść się na S3.
Scena 6: Spektakularny sukces
![AWS](https://inprojects.pl/assets/mem.jpg)
Po wielu godzinach potyczek z AWS, K8S i GitaLab, nasz stack technologiczny wyglądał mniej więcej tak:
Przenaszalność stała się genialnie prosta, deweloperzy mogli za pomocą ‘docker-compose’ podnosić lokalnie w pełni funkcjonalny produkt, demo dla klienta dało się zrobić na lapku programisty albo na tymczasowej wirtualce. Deployment odbywał się całkowicie automatycznie na różne środowiska w zależności od potrzeb. W kilka minut po zmergowaniu do gałęzi RC, Testerzy (przez duże T) z zapałem i radością mogli wytykać błędy programistom. Update serwisu (pod spodem to wystawienie nowego ‘deploymentu’ w Kubernetes) generował jedynie kilkusekundową niedostępność.
Na powyższych technologicznych podstawach mogliśmy wdrożyć serwis o strukturze, która pozwalała na skalowanie i wykorzystanie wypracowanej architektury.
Scena 7: Mamy wszystko na oku – przed wdrożeniem produkcyjnym
![AWS](https://inprojects.pl/assets/your-code-cant-fail-unit-tests-openim-if-you-dont-14499557-e1569407706241.png)
Zanim nasz produkt doczekał się wdrożenia produkcyjnego musieliśmy zadbać o monitoring. Programiści na bieżąco dbali o pokrycie kodu testami jednostkowymi (true story!), jednak testy integracyjne należało dopracować. Ponieważ ‘przeklikanie’ całego flow w serwisie wraz z weryfikacją spodziewanych wyników jest tożsame ze sprawdzeniem funkcjonalności wszystkich modułów (API, DB, Front) zdecydowaliśmy się wykorzystać Selenium, a wyniki jego działania zwrócić do Zabbixa. Dodatkowo do monitoringu używamy: Sentry, ELK+beats, Prometheus, Grafana.
Epilog
To był nasz pierwszy raz w takiej skali. Pierwszy raz, kiedy tak kompleksowo zmierzyliśmy się ze stworzeniem architektury o takich wymaganiach. Nie było łatwo i nie do końca przyjemnie, ale ostatecznie zdobyliśmy nowe doświadczenia i rozbudowaliśmy nasz stack technologiczny.
Na koniec kilka rad:
- nie ufajcie ślepo usługom w modelu SaaS/PaaS (np: EKS) – marketingowo wszystko wygląda pięknie, ale nie zawsze da się je dopasować do Waszych wymagań;
- uważajcie na torowisko tramwajowe w lewoskręcie jadąc na rowerze przy Mickiewicza w Szczecinie bo Wasz główny DevOps może poważnie się uszkodzić (mieliśmy przez to małe opóźnienie w dostarczeniu prac);
zmuście developerów do pisania testów wydajnościowych – będziecie mieli szansę wykryć wąskie gardła w infrastrukturze zanim zrobią to klienci;
![](https://inprojects.pl/assets/1.png)
- nie bójcie się wdrażać w dotychczas nieznane rozwiązania. Ktoś je po coś stworzył i zanim kategorycznie powiecie “nie” lepiej temat zbadać osobiście;
- monitorujcie wszystko, co tylko można, ale nie zasypujcie się alarmami, bo w końcu zaczniecie je ignorować;
- nie bójcie się prosić o rady – ludzie mają różne doświadczenia i chętnie się nimi dzielą;
My również chętnie podzielimy się naszym doświadczeniem i wiedzą zdobytą przy tym projekcie, więc jeśli nurtują Cię, jakieś pytania, odezwij się do nas.
W rzeczywistości jesteśmy dużo sympatyczniejsi i cywilizowani niż może to wynikać z tego wpisu 😉
Zdjęcie: link
Diagram: link
AWS: link
AWS 2: link
Mem Unit Tests: link
Zdjęcie rower: link