Dzisiejszy artykuł to dowód na to, że inspiracja do tekstu może przyjść z dowolnego miejsca, w dowolnym czasie. W tym przypadku przyszła z mema, który wyskoczył mi na osi czasu w portalu społecznościowym. Konkretnie chodzi o ten obrazek:
Samo zdjęcie jest stosunkowo zabawne samo z siebie - mamy dużą kanapę, która symbolizuje coś wielkiego, pojemnego, potężnego oraz małego kotka, który wygląda, jakby zgubił się na tym meblu. Gdy przyjrzymy się podpisom, to ładunek humorystyczny docenią głównie w osoby zajmujące się programowaniem i wdrażaniem oprogramowania. Mamy bowiem opis środowiska, które wymaga odpowiedniego przygotowania i bardzo dobrej, drogiej maszyny, a z drugiej strony - to wszystko z myślą aplikacji, która ma tak mało użytkowników, że równie dobrze można ją uruchomić na zwykłym "domowym" serwerze i nadal będzie dobrze działać. No, przezabawne.
Po chwili refleksji dotarło do mnie, że w tym memie jest coś więcej - to nie jest tylko internetowa krotochwila, która ma momentalnie uprzyjemnić życie smutnego programisty. To obrazek, który (być może niechcący) uchwycił podejście niektórych, szczególnie młodych, osób do warunków, jakie napotykają podczas procesu wdrażania systemu. Wiadomo, że każdy chce mieć świetny sprzęt dla swojego oprogramowania, jednak prawda jest taka, że nie zawsze "potwór" jest potrzebny i warto się na niego wykosztowywać. Niestety, nie wszyscy to rozumieją i w najgorszym przypadku niekiedy prowadzi to do niesnasek oraz bezsensownych sporów. To specyficzny przypadek bycia "pistoletem" w świecie zdominowanym przez podejście chmurowe i devopsowe.
Dzisiaj rozwiniemy sobie ten temat, co przybierze pewnie bardziej formę eseju. Jednak, aby tekst jednocześnie był edukacyjny, rozwiniemy też sobie też te dziwne skróty, o których traktuje mem będący źródłem tekstu. Być może bowiem niektórzy są zainteresowany tym, czym są te mityczne kontenery, Docker i Kubernetes, o którym kiedyś coś słyszeli.
Czym są kontenery?
Aby w ogóle zacząć wyjaśniać to, o czym wspomniałem pod koniec wstępu i dlaczego ten mem jest śmieszny, musimy opowiedzieć sobie o tym, czym są kontenery. Z góry uprzedzam, że to słowo będzie się pojawiać w tekście bardzo często.
Być może niektórzy znają pojęcie maszyn wirtualnych - są one niejako "komputerami w komputerze". Mówiąc ściślej, w naszym systemie operacyjnym (gospodarzu) możemy mieć oprogramowanie, które umożliwia wydzielenie zasobów na potrzeby instalacji dodatkowych, osobnych systemów operacyjnych, które są izolowane i stanowią same w sobie działające środowiska. W ten sposób można coś przetestować, uruchomić np. Linuksa w Windowsie (albo odwrotnie), ale także wydzielić miejsce na hostowanie konkretnej aplikacji. Oznacza to, że jeden system może tworzyć środowiska dla wielu klientów korzystając z jednego komputera - tak właśnie powstają wirtualne serwery prywatne (VPS) oferowane przez wiele firm. Te dzielą wówczas potężną fizyczną maszynę na wiele mniejszych, wirtualnych.
Zaletą maszyn wirtualnych jest to, że są izolowane i mogą mieć dynamicznie modyfikowane parametry (przynajmniej niektóre). Zwiększenie np. pamięci RAM nie oznacza bowiem faktycznego dołożenia dysku do komputera, a więc jego rozkręcenie, wmontowanie układu scalonego i ponownego uruchomienia, tylko przesunięcie suwakiem odpowiedniej wartości w narzędziu do wirtualizacji i ewentualnie restart. Jednak "wirtualki" mają jedną wadę, która od razu się narzuca osobom, które miały z nimi do czynienia nawet w domowych warunkach - są "ciężkie". To systemy operacyjne działające w ramach innego systemu operacyjnego, gdzie fakt istnienia maszyny fizycznej jest symulowany - to musi kosztować pierwotny system odpowiednie zasoby, a do tego wpływać na wydajność samego środowiska, który jest "gościem", gdyż to wszystko nie działa natywnie. Co prawda, w większości zastosowań (do czego jeszcze wrócimy i jest sednem dzisiejszego tekstu) i tak to wystarcza, ale niepokorni ludzie z IT znaleźli inne rozwiązanie.
Są nimi kontenery, które są ściśle powiązane z systemami uniksowymi. Stanowią sposób na wirtualizację, ale już na poziomie systemu operacyjnego - ten pozwala utworzyć "pudełko" z wydzielonymi zasobami i w którym instalowane są konkretne moduły. Gdzie jest zatem haczyk? Podczas gdy maszyny wirtualne symulują CAŁY komputer (łącznie ze wszystkimi podzespołami itd.), to kontenery działają w ramach systemu operacyjnego gospodarza, bez pośredników w postaci narzędzi do pełnej wirtualizacji. To sprawia, że są dużo "lżejsze", a co za tym idzie, wydajne i zarządzanie nimi jest prostsze koncepcyjnie. Fakt, że są bliżej hosta sprawia, że izolacja nie jest aż tak mocna, ale z odrobiną uwagi nie powinno to stanowić żadnego problemu dla cyberbezpieczeństwa.
Dlaczego kontenery są takie ważne i zrewolucjonizowały zarządzanie aplikacjami w wielu przypadkach? Choćby dlatego, że:
- pozwalają przygotować środowisko wcześniej i wgrywać je w praktycznie niezmienionej formie na różne maszyny,
- izolują się od komponentów aplikacyjnych hosta, czyli np. mogą korzystać ze swojego serwera HTTP niezależnie od tego, czym dysponuje gospodarz,
- można dokładnie określić wersje modułów, a więc nie powinno być sytuacji, że na jednej maszynie aplikacja działa, gdyż jakiś komponent jest w wersji 3.3, a na drugiej maszynie już jest problem, gdyż tam akurat ktoś zainstalował 3.4,
- nie wymagają instalacji wszystkich komponentów aplikacji bezpośrednio u programisty i żonglowania wersjami oprogramowania (np. programista może swobodnie pracować nad rozwojem dwóch aplikacji, z których jedna wymaga PHP 7.4, a druga PHP 8.3 - zamiast tego stawia sobie pudełko "all-in-one"),
- wiąże się z nimi mały narzut czasowy i zasobowy, więc nie obciażają systemu w taki sposób, aby stanowiło to problem,
- przez swoją "lekkość" można łączyć kontenery w taki sposób, aby korzystały z siebie nawzajem i nadal jest to wydajne, a jednocześnie odizolowane od reszty.
Od razu wyjaśnię jedną rzecz - tak, maszyny wirtualne także teoretycznie można współdzielić. Problem w tym, że są one tak niewydajne i duże, że nie ma to żadnego sensu praktycznego. Dodatkowym problemem staje się aktualizacja kodu przekazywanego do takiej maszyny wirtualnej - jest to wykonalne, ale po prostu niewygodne.
Kontenery stanowią podstawę metodyki DevOps i wielu procesów wdrożeniowych, ułatwiają aktualizację oprogramowania i generalnie stały się odpowiedzią na wiele problemów w wytwarzaniu software'u. Nie są bez wad - wymagają trochę wiedzy przy konstrukcji reguł, które je tworzą i zarządzają, a także odpowiedniego środowiska, które pozwala obsługiwać takie pakiety. Tutaj na scenę wkracza m.in. Docker, który jest właśnie usługą do zarządzania takimi paczkami - to za pomocą Dockera możemy uruchamiać kontenery, pobierać ich obrazy, zatrzymywać, wchodzić do ich wnętrza itd. Jednocześnie należy pamiętać, że Docker nie jest tym samym co kontener (podobnie jak Word nie jest dokumentem tekstowym), a samo oprogramowanie z charakterystycznym logiem przedstawiającym niebieskiego walenia nie jest jedynym, które potrafi obsłużyć kontenery - istnieją też choćby LXC czy Podman (często polecany zamiast Dockera ze względu na brak potrzeby uzyskania tak dużych uprawnień do systemu, choć ma też wady).
Co jest dla nas ważne w kontekście mema? Kontener to "pudełko", które zawiera jeden (zazwyczaj; może być ich wiele) komponent aplikacji, np. frontend, backend, bazę danych, system cache'ujący lub coś innego. Nie jest rzadkim przypadkiem sytuacja, że programista musi skorzystać w jednym projekcie z PostgreSQL, ale nie chce go instalować na swoim komputerze, więc uruchamia sobie kontener.
Czym jest Kubernetes?
Gdy ludzie zaczęli masowo wykorzystywać kontenery, szybko okazało się, że faktycznie powstaje ich dużo. Na tyle dużo, że same programy typu Docker przestały wystarczać, gdyż pojawiła się potrzeba zarządzania wieloma kontenerami, ich kolejnością, skalowaniem itd. I nie mówimy tutaj o komputerze programisty, który ma uruchomionych kilka takich paczek, tylko środowiskach produkcyjnych, gdzie może być ich znacznie więcej, ich obsługa jest krytyczna, są na wielu serwerach, ale przede wszystkim są skalowane. Jest to coś, co nazywa się fachowo orkiestracją, a więc można wyobrazić sobie, że potrzebny jest dyrygent, który ma pieczę nad wieloma muzykami grającymi na różnych istrumentach i decyduje, którzy włączają się w określonych momentach, jak głośno to robią i ilu wykonawców z danego obszaru jest potrzebnych, aby orkiestra pięknie grała.
Takich "dyrygentów" jest kilku na rynku i jednym z najprostszych jest Docker Compose, który ułatwia w Dockerze uruchamianie i zarządzanie kilkoma kontenerami naraz. Jednak do potężnych zastosowań serwerowych jest zazwyczaj niewystarczające, zwłaszcza, jeśli mamy do czynienia nie z jednym, tylko kilkoma instancjami połączonymi w tzw. węzły (ang. nodes). W związku z tym istnieje drugie narzędzie od twórców Dockera, które nazywa się Docker Swarm - posiada więcej możliwości oraz rzeczywiście pozwala na prowadzenie "orkiestry" stworzonej z wielu serwerów. Jednak nawet Swarm, który jest stosunkowo prosty w obsłudze (w porównaniu do kolejnego bohatera tekstu), może nie wystarczyć w bardziej zaawansowanych przypadkach - wówczas na scenę wkracza rzeczony Kubernetes, zwany też skrótowo K8s.
Jest to złożony, modułowy system, pozwalający zautomatyzować wdrażanie (ang. deployment) skonteneryzowanych aplikacji, uprzyjemnia ich skalowanie (także pozwalając zdać się na algorytmy, które automatycznie wyliczą odpowiednie ustawienie) oraz po prostu codzienne zarządzanie. Dzieli infrastrukturę na klastry, wspomniane już węzły, a także pody, które można wyróżnić w ich ramach. Co ciekawe, to projekt open source, którego początków należy szukać w Google'u, ale teraz zarządza nim fundacja Cloud Native Computing Foundation (CNCF). Co ważne, to system, który może być rozszerzany, wokół którego urosła bardzo duża społeczność oraz powstały liczne narzędzia pomagające w obsłudze. A te przydają się tym bardziej, że Kubernetes jest dość skomplikowany i jego konfiguracja nie należy do rzeczy, które robi się z marszu, między pierwszą kawą a śniadaniem.
To, co musimy wiedzieć w kontekście mema będącego bohaterem tego odcinka, to fakt, że Kubernetes jest systemem ułatwiającym zarządzanie kontenerami na potencjalnie wielu serwerach. Co ważne, kontenery możemy też uruchamiać na wirtualnych serwerach - dla orkiestratorów nie ma znaczenia, czy coś działa na fizycznej maszynie, czy zwirtualizowanej, bo serwer to serwer.
Jak określa się liczbę użytkowników aplikacji?
Z drugiej strony mamy informację o tym, że z aplikacji korzysta 12 osób. Wbrew pozorom, nie jest to taki rzadki przypadek - software house'y często tworzą oprogramowanie dla danej firmy, dedykowane ich potrzebom i konkretnym pracownikom, a wiadomo, że firmy mają różny rozmiar. Nawet system dla 12 osób ma sens, jeśli rozwiązuje problem, potrzebę biznesową lub zwyczajnie przyspiesza pracę, pozwalając np. obsłużyć więcej zgłoszeń od klientów.
Określanie liczby użytkowników aplikacji to zadanie niekiedy dość trudne. Prosta sytuacja to właśnie oprogramowanie dla konkretnej grupy osób, gdyż wiadomo, jak liczna ona jest. Jednak nawet w takich przypadkach trzeba pamiętać o tym, że systemy IT rozwijają się, podobnie jak potrzeby klientów. Aplikacja, która na początku była przeznaczona dla 10 osób, z czasem może być przekształcona w coś większego lub zwyczajnie firma może urosnąć. Jeszcze trudniej jest z oprogramowaniem przeznaczonym na wolny rynek, mające przyciągnąć jak najwięcej osób - określanie tak dużej grupy docelowej to zadanie już bardziej marketerów i sprzedawców, jednak zespół IT musi przewidzieć infrastrukturę, która nie załamie się przy założeniu X jednoczesnych osób.
No właśnie - o czym w ogóle mówimy w kontekście mierzenia rozmiaru użytkowników? Przede wszystkim, o ile sami użytkownicy są właściwym terminem z punktu widzenia marketingu i "zwykłych" ludzi, o tyle mierząc systemy mówimy raczej o tym, ile żądań HTTP jest w stanie obsłużyć. Zależy to od wydajności aplikacji, ustawień serwera HTTP, mocy maszyny i wielu innych czynników - określenie tego nawet w przybliżeniu jest bardzo trudne. Można za to testować wydolność oprogramowania poprzez różne programy, jak np. JMeter czy k6.
To, o czym jeszcze trzeba pamiętać, to fakt, że liczba przyjmowanych żądań może być jednoczesna lub maksymalna. Ta pierwsza wartość to średnie, realne obciążenie aplikacji przez większość czasu. Szczególnie często mówimy o tym w przypadku serwisów społecznościowych, serwerów gier, ale też aplikacji dedykowanych firmom, gdzie zakładamy, że oprogramowanie będzie wykorzystywane np. 8 godzin dziennie przez 40 osób. Natomiast w pewnych przypadkach trzeba wziąć pod uwagę możliwe "piki", a więc momenty, w których żądania nagle rosną do wyjątkowo dużej liczby i serwer musi to obsłużyć. Przykładem może być news na stronie klubu sportowego, nowy artykuł na popularnym blogu czy ogłoszenie konkursu. Także przy organizacji Rankingu "OpowiemCi" organizatorka musi się z tym zmagać, gdyż w momencie ogłoszenia otwarcia możliwości głosowania, ustawienia serwera muszą być zmienione w taki sposób, aby przez kolejne kilka dni przyjąć zwiększony ruch.
Także jedna rzecz, to temat prawdopodobnego wolumenu użytkowników korzystających z aplikacji jednocześnie i w "gorących" momentach. Drugie to określenie, jaka infrastruktura jest do tego wymagana - to już znacznie trudniejsze i często bardziej wynikające z doświadczenia lub eksperymentów.
Czy Kubernetes zawsze jest potrzebny?
I tutaj dochodzimy do pewnego - nie bójmy się tego powiedzieć - wariactwa. Nie każda aplikacja musi być przygotowana na potężną infrastrukturę, tak samo jak nie do każdego systemu IT trzeba kupować chmurę i organizować solidny pion administracyjny. Mówiąc wprost, duża część oprogramowania powstającego na co dzień może działać na normalnych, prostych serwerach, gdzie często programiści mają mniejsze możliwości niż ma root, ale też większy spokój z racji zapewnienia całej konfiguracji przez wykwalifikowane zespoły.
Natomiast czasami można spotkać się z innym podejściem osób w IT wychowujących się w czasach, kiedy mówi się tyle o chmurach, konteneryzacji itd., którzy uważają, że absolutnie każde oprogramowanie działające na serwerze musi korzystać z tych udogodnień. Aplikacja składająca się z jednego formularza widocznego dla kilkudziesięciu osób? Trzeba kupić instancję AWS! System sprzedażowy dla kilku osób? Będziemy skalować horyzontalnie na czterech maszynach! Prosta strona typu landing page w technologiach stosowanych i tak w innych aplikacjach firmy? Najpierw trzeba poświęcić cały dzień na szczegółową definicję kontenera w pliku Dockerfile
! No bo przecież tak robią teraz we wszystkich startupach, w Ju-Es-Ej, Dolinie Krzemowej, więc my też musimy! Widziałem w tutorialach na YouTube!
Oczywiście, trochę sobie teraz żartuję i to, co piszę, absolutnie nie oznacza, że nie należy myśleć o przyszłości i przygotować aplikację np. na skalowanie lub do konteneryzacji (która akurat bardzo może się przydać, szczególnie przy samym rozwoju kodu). Zresztą, przykład z aplikacją z jednym formularzem może być o tyle nietrafiony, że ten formularz może być ogólnodostępny w trybie 24/7 oraz wykorzystywany przez dziesiątki tysięcy osób, a więc umieszczenie jego kopii na kilku serwerach może mieć sens. Ale właśnie - może i trzeba robić to z rozmysłem. Wszystko zależy od sytuacji i oceny architektonicznej - nie zawsze potrzebna jest solidna infrastruktura zarządzana przez doświadczonego DevOpsa, aby wdrożyć jakąś aplikację, która ma szybko przynosić efekty. Są takie, w których ma to przyszłość i wyższe koszty są uzasadnione, ale w wielu przypadkach nie ma to żadnego sensu, gdyż software house lub klient się wykosztują, a efekt nie będzie odczuwalny ani w krótkiej, ani w dłuższej perspektywie. Nie wszyscy to rozumieją i bywają z tego powodu niesnaski - to podobny przypadek, jak niechęć głównego programisty do zastosowania pewnej, modnej technologii, jeśli nie stoi za tym żaden solidny argument przeważający nad kontrargumentami. Bo uwierzcie - moda i "bo wszyscy to robią" to zdecydowanie za mało, aby np. do zespołu i projektu, którego 99% stosu technologicznego to Java, wprowadzać nagle Rusta.
To podobny przypadek, jak z zaawansowaną architekturą oprogramowania - oczywiście, zawsze dobrze jest, gdy jest ono podatne na skalowanie, rozwój, rozszerzenie i nie zamyka się na początkowe warunki. Natomiast trzeba też wziąć pod uwagę rozsądek oraz biznes - jeśli klient oczekuje aplikacji dla kilkunastu osób z kilkoma prostymi widokami i to się nie zmieni w ciągu najbliższych lat, to tworzenie architektury mikroserwisowej tylko po to, aby była, mija się z celem, gdyż więcej czasu zabierze przygotowanie odpowiednich struktur aniżeli realne napisanie funkcjonalności. Dużo lepiej w takim przypadku jest napisać to prościej, szybciej, a mając już solidnie przygotowany kod działający dla "mniejszej" infrastruktury, dostosować wszystko do większego ruchu, gdy rzeczywiście realne jest, że on nadejdzie. Natomiast - z góry uprzedzając rozjuszony tłum, który nie zgadza się z tym zdaniem - wszystko zależy od sytuacji, warunków i założeń. Są sytuacje, w których faktycznie trzeba myśleć o przyszłości. A są takie, gdzie będzie to zwykłe przepalanie budżetu, czasu i mocy przerobowych tylko po to, aby poczuć się "profesjonalnie".
Podsumowanie
Zbierając wszystko do prostego wniosku - aplikacja działająca dla 12 osób, o ile nie jest wyjątkowo skomplikowana, bez problemu powinna dobrze działać na prostym serwerze. Niepotrzebna jest infrastruktura składająca się z 20 węzłów, po 4 wirtualne rdzenie procesora każdy, samodzielnie się skalująca w zależności od potrzeb i zarządzana Kubernetesem, aby uruchomić takie oprogramowanie. To klasyczny przykład wyciągania armaty na mrówkę.
To nie oznacza, że w każdym przypadku przygotowywanie systemu IT na bardziej złożone warunki nie ma sensu - jak najbardziej warto o tym myśleć, ale też realizować wtedy, kiedy rzeczywiście opłaci się to w krótszej lub nieco dalszej przyszłości.
Pozdrawiam i dziękuję - Jakub Rojek.