DevOps

W poprzednich rozdziałach dowiedziałeś się, jak przechowywać dane oraz jakie są cechy dobrej aplikacji internetowej. Kolejną istotną częścią procesu wytwarzania oprogramowania jest DevOps – Dyrygent cyfrowej kuchni. Posiadasz już działającą aplikację, pewne MVP, które ciągle rozwijasz. Dodawanie kolejnych elementów może okazać się problematyczne – szczególnie kiedy w grę wchodzi praca zespołowa. DevOps określa jak poprawnie zautomatyzować elementy implementacji, aby szybciej otrzymać informację zwrotną o produkcie, uprościć procesy i je zautomatyzować. 

Przy tworzeniu małej aplikacji, spora część elementów może wydawać się niepotrzebna, jednak wraz z rozrastaniem się projektu, sam zauważysz, które elementy należy wyrzucić, zmienić lub zautomatyzować, szczególnie przy etapie wdrożenia i przechowywania kodu. 

DevOps, czyli współpraca kucharzy z obsługą sali

Aby dobrze zrozumieć, czym jest DevOps, trzeba wyjaśnić skrót:

  • Dev: implementacja projektu
  • Ops: utrzymanie i wdrożenie aplikacji

Oznacza to w praktyce, że cały zespół jest odpowiedzialny za aplikację, którą wytwarza. Są osoby odpowiedzialne za tworzenie kodu i są osoby, których zadaniem jest wdrożenie aplikacji. Sam możesz zauważyć, że tworzenie kolejnej wersji oprogramowania jest obarczone pewnymi problemami: 

  • Jak podmienić pliki na serwerze? 
  • Czy nowa funkcjonalność działa poprawnie? 
  • Czy po wprowadzonych zmianach projekt jest gotowy do uruchomienia?

DevOps pozwala wprowadzić zasady, dzięki którym odpowiadanie na te pytania zostaną zautomatyzowane i potrzebne dane uzyskamy szybko. Odnosząc to do analogii kuchni, kucharze przygotowują posiłki, ale kelnerzy którzy, dostarczają je, sprawdzają czy spełniają standardy, smakują klientom i dają feedback kucharzom. 

Kolejnym ważnym elementem jest wspomniana analiza działania aplikacji. DevOps zajmuje się monitorowaniem stworzonego rozwiązania, weryfikując czy działa poprawnie, czy aplikacja odpowiada szybko, nie zacina się i nie ma problemów z dostępnością. Najczęstszym problemem działania aplikacji są “wąskie gardła”, które blokują wykonanie aplikacji. Jeżeli kuchnia ma jeden piec, nie będzie mogła upiec jednocześnie 12 pizz, może zrobić tylko 6. Ta sama analogia odnosi się do aplikacji internetowych – jeżeli gdzieś występuje tego typu problem, musimy podjąć pewne działanie – na przykład kupić kolejny piec!

Git i GitFlow: Jedna książka kucharska dla wielu kucharzy

Przy tworzeniu oprogramowania, jednym z najważniejszych elementów jest system kontroli wersji – oprogramowanie, które przechowuje kod źródłowy projektu, zarządza zmianami, wskazuje kto jest odpowiedzialny za część programu. Najpopularniejszy jest program Git, używany na całym świecie w biznesie, programach otwarto-źródłowych i prywatnych. Jako jednoosobowy zespół również możesz z niego korzystać, choćby dla stworzenia kopii zapasowej i historii zmian. Ciężko jest znaleźć analogię do restauracji, może to być dziennik przepisów, w którym zapisane są poprzednie wersje przepisów wraz ze zmianami, jakie wprowadzili poszczególni kucharze.

Git jest przydatny w pracy w dużych zespołach, programiści mogą równolegle wprowadzać zmiany, bez tworzenia niepotrzebnych konfliktów. Zwykle, każdy pracuje na osobnej gałęzi, a kiedy zakończy zadanie scala swoje rozwiązanie do gałęzi głównej. Inny programista może też przed scaleniem sprawdzić kod: sprawdzić jego jakość, działanie itd – nazywamy to pull request. Programista wysyła żądanie scalania zmian, a kucharz może prosić o sprawdzenie dania przed wpisaniem go do karty. 

Trzeba też wspomnieć o modelu organizacji pracy. Szczególnie jest to potrzebne w ogromnych zespołach. GitFlow wprowadza stałą strukturę gałęzi, w których każda ma określoną rolę:

  • Prod: kod produkcyjny, który jest przetestowany i stabilny
  • Staging: odpowiada za testy, poprawki przed wstawieniem na gałąź główną
  • Feature: gałąź do implementacji nowej funkcji, np. feature/robot-gotujący

Oddziela to rozwój od produkcji, wymusza kontrolę nad procesem wydawania i wdrożenia aplikacji, a także pozwala równolegle pracować programistom. Gdy jeden skończy zadanie, nie wpływa to na pracę drugiego. Kucharz zmieniający przepis u siebie w zeszycie, nie zmieni tego z głównej książki, ale też nie może bezpośrednio zmieniać jej, bez wiedzy innych kucharzy. Stosując tę strukturę, możesz bez ryzyka tworzyć i modyfikować agenta, na osobnej gałęzi np.: feature/cooking-agent

Niestety, komplikuje to historię i wymaga przeszkolenia programistów, jeżeli nie pracowali w tym stylu. Możesz odnieść wrażenie, że niepotrzebnie wprowadza dużą ilość połączeń gałęzi. Mówiąc wprost, w małych projektach jest to przerost formy nad treścią. 

Umieszczając pliki w repozytorium, nie powinno się umieszczać wszystkiego. Dobrą praktyką jest wstawiać kod programu oraz listę zależności. Nie należy wstawiać programu wykonalnego i bibliotek, ponieważ każdy może na własną rękę stworzyć program na podstawie kodu i samodzielnie zainstalować biblioteki. Plikiem, który pozwala ustalić dokładnie, co zostanie wstawione do repozytorium jest .gitignore. Zapisujemy tam pliki i katalogi, których git ma nie synchronizować. 

Uwaga! Zmienne środowiskowe, takie jak klucze API do modeli językowych albo dane dostępu do bazy danych powinny być przechowywane w osobnym pliku .env, nie w kodzie. Dodatkowo, ten plik nigdy nie powinien być śledzony przez git, ponieważ wszystkie zmiany w nim będą widoczne. Jest to jeden ze sposobów zabezpieczenia się przed wyciekiem kluczy, to zagadnienie zostanie lepiej opisane w rozdziale dotyczącym bezpieczeństwa. DevOps to automatyzacja i usprawnianie procesów, ale nie wolno zapomnieć o podstawowych elementach bezpieczeństwa. 

CI/CD: Automatyczna linia wydawania dań

CI/CD oznacza pierwszy krok do automatyzacji procesów. Zgodnie ze złotymi zasadami Elona Muska, najpierw należy uprościć procesy przed ich automatyzacją, tak tutaj uprościliśmy proces wprowadzania zmian do głównej aplikacji. Teraz nadeszła pora na automatyzację!
Skrót CI oznacza Continuous Integration, czyli Ciągłą Integrację. Chodzi o to, że po każdej wprowadzonej zmianie, możemy automatycznie sprawdzić czy program spełnia założone przez nas standardy, uruchamia się i jest gotowy do działania. Programista wprowadza zmiany do repozytorium, program automatycznie weryfikuje czy kod jest poprawny. Odnosząc to do restauracji, po przygotowaniu nowego dania, kucharz przekazuje je robotowi, który określa jakość tego dania. Jeżeli jest dobre, zostanie wprowadzone do karty, jeżeli nie, robot powie kucharzowi co jest nie tak. 

CD natomiast to Continuous Delivery, czyli Ciągłe Dostarczanie. Skoro mamy stabilną wersję aplikacji, gotową do uruchomienia, to dlaczego jej nie wdrożyć? Niech automat wstawi te pliki na serwer, nie człowiek. Ręczne wprowadzanie zmian po każdej ważnej zmianie jest podatne na awarie, a ten proces bardzo łatwo się automatyzuje. 

Jakie są tego zalety? Najwięcej zyskasz otrzymując feedback po wprowadzeniu zmian. Jeżeli Twój kod nie działałby poprawnie, dowiedziałbyś się o tym dopiero przy wdrażaniu aplikacji. Wprowadzany kod do repozytorium będzie też kodem, który posiada odpowiednią jakość. Ma to znaczenie, jeżeli wstawiasz zmiany często i zespół pracuje równolegle.

GitFlow wspomaga ten proces, na gałęziach feature możesz uruchamiać testy, a przy dołączaniu zmian na gałąź develop, możesz budować zmiany i wstawiać je na środowisko testowe. Informacje otrzymujesz od razu!

W CI/CD elementy wdrożeniowe muszą być zautomatyzowane, człowiek nie może brać w nich udziału. Programista ma tylko wprowadzić zmiany, nic więcej – nie konfigurować, wpisywać dodatkowych poleceń itd. Wolne wdrożenie oraz testy mogą zniechęcać programistów do częstego wprowadzania zmian, a im częstsze zmiany tym lepiej. 

Docker: Pudełko, które gwarantuje ten sam smak wszędzie

Wyobraź sobie sytuację, idziesz do restauracji we Włoszech i zamawiasz najlepszą pizzę jaką w życiu jadłeś. Wracasz do Polski, idziesz na Stare Miasto, do włoskiej knajpy i prosisz o taką samą pizzę, ale niestety nie smakuje tak samo. Składniki były takie same, została upieczona według takiego samego przepisu, ale nie jest już tak dobra.

Tak jest też z oprogramowaniem, jeden programista wykonał zadanie, wprowadza zmiany i okazuje się, że u klienta program nie działa. Na ratunek przychodzą kontenery – zamknięte pudełko na aplikację, w której są wszystkie elementy potrzebne do działania. Dzięki temu, mamy gwarancję, że program zadziała w każdym środowisku – u klienta i u programisty. 

Najpopularniejszym narzędziem do tworzenia kontenerów jest Docker. Pakuje aplikację do kontenera w etapie budowania, tworzy odpowiednią konfigurację i zapisuje to w gotowym do uruchomienia obrazie Docker. Możemy to uruchomić w dowolnym miejscu na świecie!

Gdyby tylko ta restauracja we Włoszech miała pudełko, z którego nie ucieka ciepło! Można by było zjeść tę pizzę w dowolnym miejscu na świecie.

Dockera wykorzystuje się w procesie CI/CD, kiedy zmiany zostaną wstawione, generowany jest obraz kontenera z aplikacją, co weryfikuje poprawność programu i daje opcje jego szybkiego uruchomienia. 

Skalowanie: Jak obsłużyć tysiąc gości naraz?

Wspomniane zostały wcześniej problemy z działaniem aplikacji. Nasze rozwiązanie może zostać przeciążone i nie być w stanie obsłużyć wszystkich użytkowników. Tak samo jak restauracja ma limit miejsc, tak samo każda aplikacja internetowa ma limit równoległych użytkowników. Musimy nasze rozwiązanie przeskalować, najbardziej znane są 2 typy skalowania: Skalowanie pionowe i Skalowanie poziome. 

Skalowanie pionowe: Kupmy większy piec!

Skoro maszyna ma problemy z wydajnością, ulepszamy ją. Zwykle, usprawnia się procesor i dodaje się więcej pamięci RAM. Im lepsze podzespoły serwera, tym lepiej będzie on działał. Ale ma to swoje limity – ogranicza nas budżet i technologia. Czasami nie wyciśniemy więcej z serwera, w rozsądnej cenie. 

Skalowanie poziome: Kupmy więcej pieców!

Zamiast ulepszać maszynę, stwórzmy drugą identyczną. Zwiększyliśmy dwukrotnie zasoby naszej aplikacji, możemy obsłużyć 2 razy więcej użytkowników, ale ma to też swoje problemy. Trzeba postawić strażnika – load balancer – który będzie zarządzał ruchem i kierował go na odpowiednie urządzenie. Daje to niestety dodatkowy element i komplikuje architekturę naszej aplikacji. Jednak dobrze przemyślana architektura pozwala skalować poziomo aplikację w nieskończoność!

Kubernetes: Kierownik sali w Twoim kulinarnym imperium

Skoro wiesz już czym są kontenery i czym jest skalowanie poziome, możesz wykorzystać potężne narzędzie Kubernetes. Trzeba to porównać do kierownika w restauracji, on zarządza wszystkim, co się dzieje: ile jest stołów na sali, ilu jest kucharzy, ile pieców kucharze mają do dyspozycji. Podobnie działa Kubernetes, uruchamia odpowiednie aplikacje i pilnuje, aby było ich wystarczająco. Powiesz, że chcesz mieć 3 działające kontenery, Kubernetes Ci to zapewni. Jeżeli okaże się, że miejsc na sali jest za mało, automatycznie wyda rozkaz o dodaniu stolików, a jeżeli okaże się być ich za dużo to zabierze je. Kubernetes sam naprawia awarie, jeżeli jakiś stolik nie działa, zostanie automatycznie zmieniony. 

W małej aplikacji Kubernetes może okazać się niepotrzebny, ale wraz z wielkością aplikacji zarządzanie tym wszystkim ręcznie jest nierealne. Trzeba te procesy automatyzować. Bez Kubernetesa ktoś ręcznie musi reagować na awarie, pilnować obciążenia serwerów i uruchamiać je w razie potrzeby. Kiedy zobaczysz, że Twoje MVP zamieniło się w pełną aplikację, a użytkowników przybywa, rozważ Kubernetesa, aby rozwijać swoją aplikację.

Warto wspomnieć o tym, że najlepiej to działa w architekturze niezależnych mikroserwisów. Najlepsza jest sytuacja w której kontenery są niezależne, wykonują określone zadania i są wymienialne. 

Infrastructure as Code: Cyfrowe plany budowy restauracji

Jesteś kierownikiem restauracji, musisz zatrudnić 3 kucharzy, więc wstawiasz ogłoszenie, przeprowadzasz rozmowy kwalifikacyjne i wybierasz najlepsze osoby. Pojawia się potrzeba rozbudowy kuchni, więc przechodzisz przez kolejny, żmudny proces. Podobnie będzie przy tworzeniu aplikacji, chcesz utworzyć bazę danych, tworzysz ją ręcznie, konfigurujesz urządzenia, ale czujesz, że można to zrobić automatycznie.

Na ratunek przychodzi rozwiązanie nazywane Infrastructure as Code, istnieją gotowe rozwiązania do konfiguracji struktury Twojej aplikacji, bez ręcznego przeklikania i konfigurowania. Zapisujesz w pliku konfiguracyjnym odpowiednie informacje i program np. Terraform albo Ansible buduje wszystko automatycznie. Posiadasz dokładny projekt architektoniczny restauracji, którą można postawić na dowolnej działce, ze wszystkimi detalami. Daje to powtarzalność, kontrolę zmian – wiadomo kto co zmienia i dlaczego i łatwo integruje się z procesami CI/CD. 

Istnieją 2 podejścia: deklaratywne (opisujemy co chcemy uzyskać np.: “chcę 3 kucharzy”) albo imperatywne (opisujemy kroki: “przeprowadź rozmowy, wybierz 3 kucharzy”). 

Monitorowanie, czyli Sanepid w Twojej restauracji

Wprowadziłeś tonę automatów do aplikacji, wszystko wykonuje się samo, a ty skupiasz się na programowaniu. Nagle, dostajesz wiadomość od użytkownika, że aplikacja nie działa. Nie sprawdziłeś, czy system zachowuje się poprawnie po wdrożeniu. Aplikację nie tylko trzeba tworzyć, ale też kontrolować ją, jej działanie, zużycie zasobów. Zbierz metryki, stan serwerów, komunikaty, które zwraca aplikacja, liczbę zapytań na sekundę i nie tylko, na tej podstawie podejmij odpowiednie działanie – zmień infrastrukturę, popraw aplikację, przeskaluj. 

Podsumowanie

DevOps to bardzo szeroka dziedzina, która przede wszystkim wspomaga i automatyzuje procesy związane z wytwarzaniem oprogramowania. Przedstawiłem Ci kluczowe koncepcje, ale najważniejsza z nich to: utrzymanie i tworzenie aplikacji jest jedną całością, za którą odpowiada cały zespół. Opisane narzędzia wspomagają ten proces. 

Nie wdrażaj wszystkiego od razu, rób to krokami. Mały projekt na pewno nie potrzebuje Kubernetesa, MVP nie potrzebuje rozbudowanego GitFlow z CI/CD. Zauważysz, że wraz z rozwojem Twojego rozwiązania, kolejne elementy zostaną naturalnymi krokami rozwoju. 

Działająca aplikacja to nie jest tylko kod, ale też zapewnienie reakcji na zmiany i unikanie awarii. Cały zespół odpowiada za restaurację, nie tylko kierownik! 

W kolejnym rozdziale dowiesz się więcej o procesie testowania aplikacji i weryfikacji jakości, który został wspomniany przy okazji CI/CD.

Materiały

Materiały do samodzielnej nauki związane z tym tematem:

  1. Jak używać Git oraz GitHub
  2. Jak używać GitHub Desktop
  3. Git Flow workflow
  4. Metodyka 12 czynników (12-factor app)
  5. Podstawowe pojęcia sieciowe
  6. Cache’owanie w system design
  7. Skalowanie poziome vs pionowe
  8. Monitoring w DevOps – czym jest i jak działa
  9. Najlepsze praktyki: backup w chmurze i Disaster Recovery
  10. Co to jest deployment?
  11. Czym jest Infrastructure as Code
  12. DevOps
  13. Pipeline DevOps
  14. Czym jest CI/CD
  15. Wykorzystanie AI do automatyzacji CI/CD v.1
  16. Wykorzystanie AI do automatyzacji CI/CD v.2
  17. Konteneryzacja w DevOps
  18. Zalety konteneryzacji
  19. Bezpieczeństwo kontenerów
  20. Serwery WWW (WEB serwery)
  21. Podstawy sieci komputerowych
  22. HTTP Caching
  23. Czym jest DevOps
  24. Czym jest CI/CD
  25. Docker Crash Course
  26. Czym jest Infrastructure as Code
  27. Top 5 najczęściej stosowanych strategii wdrażania (deployment)
  28. Główne koncepcje Dockera
  29. Jak działa CDN
  30. Skalowanie pionowe vs skalowanie poziome