
Znając Dockera i wiedząc już jak wypuścić swoje dzieło na świat, możemy z czasem paść ofiarą własnego sukcesu spędzając bezsenne noce na obserwacji zachowania swojej aplikacji pod naporem użytkowników. Na szczęście nie będziemy w tym sami – z pomocą przychodzi Kubernetes (znany również jako K8s).
Uwaga! W tej serii zajmuję się opisowym przedstawieniem tematu, a celem jest zapoznanie z zagadnieniem w sposób luźny i zrozumiały dla nowicjusza. W związku z tym wiele elementów siłą rzeczy musi zostać pominiętych. Tych z was, którzy chcieliby się dowiedzieć więcej, zapraszam do źródeł na końcu artykułu.
Jak w Javie stworzyć kompletny backend dla aplikacji webowej?
- Wygodnie będzie zacząć od Spring Boota (plus Hibernate). Wchodzimy na start.spring.io i dodajemy potrzebne zależności takie jak Spring Web, Spring Data itp. Nie zapomnijmy o wybraniu odpowiedniego budowniczego projektu: Mavena albo Gradle.
- Zapisujemy ściągnięty zalążek projektu na dysku po czym otwieramy go w ulubionym IDE i ustawiamy wersjonowanie w Gicie.
- Decydujemy się na najlepszą dla nas architekturę, możemy pójść w monolit albo mikroserwisy. A kiedy już jesteśmy gotowi i wiemy co chcemy osiągnąć – piszemy kod.
- W trakcie programowania warto zadbać o niezbędne testy dzięki czemu będziemy wiedzieli, że aplikacja zachowuje się jak powinna. Mamy też okazję poeksperymentować z innymi DBMS-ami w celu optymalizacji działania programu.
- Być może poczujemy potrzebę wprowadzenia szczypty asynchroniczności i jakiegoś message brokera w celu usprawnienia komunikacji pomiędzy poszczególnymi serwisami.
- Wreszcie, gdy projekt będzie już gotowy do odpalenia na tzw. „produkcji”, komponujemy Dockerfile i tworzymy obraz, który następnie odpalamy na docelowym serwerze (również za pomocą Dockera).
- Backend gotowy, można śmiało korzystać API i łączyć się z frontem!
No to mamy już wszystko, czyż nie?

W idealnym świecie mógłbym powiedzieć, że tak – mamy już wszystko.
Aplikacja jest przetestowana, wystawiona na świat i gotowa na przyjęcie użytkowników. W końcu możemy rozwalić się na kanapie i z herbatką w dłoni leniwie obserwować jak rośnie nam liczba odwiedzających, fejm się zgadza, a reklamodawcy walą drzwiami i oknami.
Optymiści powiedzą, że żyjemy w najlepszym ze światów.
Pesymiści obawiają się, że optymiści mają rację.
Niestety, ale rzeczywistość nie jest tak kolorowa. Kontener na którym odpalona została instancja potrafi się wywalić, userzy albo masowo szturmują serwer w godzinach szczytu albo całkiem zapominają o naszej stronie. Świeża, przetestowana wzdłuż i wszerz, wersja wciąż potrafi zaskoczyć bugiem po którym trzeba na szybko wracać do poprzedniej, stabilnej iteracji.
Musimy na bieżąco śledzić te zmiany i reagować tworząc nowe instancje, albo usuwając te „bezrobotne” w celu obniżenia kosztów pracy serwerów.
Co możemy zrobić aby zaadresować te problemy?
Czy istnieje jakiekolwiek sensowne rozwiązanie?

Wespół z najnowszymi i najpotężniejszymi modelami sztucznej inteligencji udało się opracować urządzenie o chwytliwej nazwie „RECVR BETCAIR”, które w sprytny sposób pomaga w zarządzaniu pracą kontenerów.
Sposób funkcjonowania RECVR-a jest banalny w swojej prostocie:
Potrzebujemy sześć młodych i zdrowych owczarków niemieckich, które będą pracować w 4-godzinnym systemie zmianowym. Przed aktywnym w danym momencie psem znajdzie się konsola RECVR-a, wyposażona w trzy przyciski:
- „Repair” natychmiastowo niszczy zepsuty kontener zastępując go nowym, sprawnym
- „Scale” albo usuwa nadmiarowe kontenery w czasie kiedy są zbędne, albo stawia kolejne instancje gdy ruch na stronie zbliża się do limitu przepustowości
- „Rollback” przywraca ostatnią działającą wersję programu jeśli nowa odsłona generuje problemy

Zadaniem zwierzaka jest obserwacja ekranu z odbijającymi się piłkami, na które są mapowane kontenery, a dokładniej – ich aktualny stan. Ruch piłeczki symbolizuje zachowanie instancji, np. jeśli przestanie się odbijać to znaczy, że połączony kontener przestał funkcjonować.
Konsola jest sprzężona z serwerem wykonującym polecenia wprowadzane poprzez wciśnięcie przycisku łapą czworonoga. Podłączony jest również podajnik karmy, tak aby psy mogły szybko się nauczyć, że tylko wciśnięcie właściwego klawisza generuje nagrodę.
W ten sposób, w ciągu zaledwie 4-6 tygodni poświęconych na tresurę, jesteśmy w stanie otrzymać aż 68% skuteczność – czyli 2 na 3 akcje czworonogów będą zgodne z oczekiwaniami. Pozostała, błędna decyzja, musi natomiast zostać skorygowana przez doświadczonego pracownika DevOps nieustannie obserwującego pracę owczarków. Pamiętajmy o zaopatrzenie go w karty do pasjansa oraz słonecznik do łuskania, które uprzyjemnią to zadanie.
Innym rozwiązaniem będzie oczywiście użycie w tym samym celu Kubernetesowych automatyzacji, ale kto chciałby sobie aż tak komplikować życie?

Teraz już rozumiem i w 100% się zgadzam.
Podejrzewam, że powyższy, przydługi i nieco ironiczny wstępniak dał wam już kilka wskazówek czym zajmuje się główny bohater dzisiejszego artykułu.
Tym niemniej wciąż nie mamy odpowiedzi na tytułowe pytanie:
Czym jest Kubernetes?
Czy to jakiś framework? A może zewnętrzna aplikacja? Albo standard architektoniczny?
– Tak.

Jeśli spojrzycie na logo Kubernetesa to zobaczycie ster, zupełnie taki jak mają na statkach. Teraz przypomnijcie sobie, że rozmawiając o Dockerze skupialiśmy się na kontenerach, zaś Docker Compose operował na ich zgrupowaniach = kontenerowcach.
Kubernetes jest tym dla Docker Compose,
czym Docker Compose jest dla Dockera.
Kubernetes jak nawigator, kieruje całą flotą kontenerowców wypełnionych po brzegi kontenerami. Omija mielizny, prowadzi bezpiecznie przez sztormy i siedliska piratów. A wszystko to automatycznie, zgodnie w wcześniej wydanymi poleceniami kapitana DevOpsa.

Kubernetes jako architektura
Aby zrozumieć zachowanie oraz istotę Kubernetesa, musimy uprzednio wyjaśnić sobie kilka pojęć – obiektów na jakich operuje nasz nawigator:
- Cluster
- Node (Worker & Master)
- Pod
- Deployment
- Service
Node (ang. „węzeł”) to inaczej pojedyncza maszyna (komputer-serwer, chmurowa instancja albo wirtualka) na której odpalona jest nasza aplikacja. Node’y są zgrupowane w klastry (cluster) które zawierają jednego Master Node’a kontrolującego pracę dowolnej liczby Worker Node’ów.
Taki pojedynczy Worker Node zawiera w sobie kubeleta czyli programik za pomocą którego Master Node może sterować swoimi „workerami”. Jest też kube-proxy odpowiadający za komunikacją wewnętrzną i zewnętrzną oraz Docker. No i oczywiście Pod, jeden albo wiele (ale najczęściej jeden).
A co to jest ten „Pod”?
Wyobraźcie sobie platformę – tratwę na którą załadujemy dockerowe kontenery oraz wolumeny danych z których one korzystają. „Pod” to właśnie ta tratwa i jednocześnie najmniejsza, niepodzielna jednostka w świecie Kubernetesa. Jak Pod „pójdzie na dno” to zrobi to razem ze wszystkimi kontenerami i wolumenami jakie na nim postawiono. Jak zostanie przywrócony do życia, to również ze wszystkim co było na nim początkowo (ale jakiekolwiek niezapisane dane przepadły!).
Właśnie w ten sposób Kubernetes
prezentuje się pod względem architektury.
Musimy mieć swiadomość, że K8s sam w sobie nie buduje wyżej opisanej struktury – on z niej tylko korzysta w celu zarządzania pracą Podów. Utworzenie klastra to już robota dla pracownika DevOps, który może to zrobić samodzielnie, albo skorzystać z rozwiązań usługodawców chmurowych.

Kubernetes jako framework
Głównym zadaniem typowego frameworka jest uproszczenie i przyśpieszenie pracy. Wyobraźcie sobie kartkę A4 zapisaną kodem który, dajmy na to, łaczy się z bazą danych i pobiera jakieś informacje. A teraz chcemy połączyć się z inną bazą i pobrać inne informacje. Co robimy?
Zamiast przepisywać wszystkie linijki, zmieniamy tylko adres bazy, dane logowania oraz zapytanie. Cała reszta nas nie interesuje. Równie dobrze moglibyśmy sobie wyciąć z drugiej kartki A4 taką ramkę (ang. „frame„), którą jeśli przyłożymy do tej pierwszej kartki – zakrywa cały ten boilerplate code, jedynie w wyciętych miejscach pokazując to co istotne.
Kubernetes również działa w ten sposób. Ale zanim opiszemy w jaki sposób to robi i jak się z nim komunikować, to wpierw musimy odrzucić cały ten body-positive szajs i szczerze powiedzieć, że:
Kubernetes jest gruby.
K8s to kilka milionów linii kodu w języku „Go” i jednocześnie najpotężniejsze obecnie takie narzędzie na rynku…
…chociaż wcale na spasionego nie wygląda.
Komunikacja z Kuberetesem przypomina to co mamy w przypadku Dockera – konsola, jakieś pliki konfiguracyjne, opcjonalny panel sterowania w przeglądarce. Wydawałoby się, że coś takiego byle junior może napisać w tydzień, góra dwa – jeśli akurat szykują mu się sprawdziany w szkole i ma mniej wolnego czasu.

W tym miejscu musimy sobie wyjaśnić różnicę pomiędzy programowaniem imperatywnym, a deklaratywnym. Imperatyw, inaczej rozkaz, to jasne przekazanie polecenia programowi, żeby wyjaśnić czego od niego chcemy i JAK ma to zrobić.
docker run
W ten właśnie sposób komunikujemy się z Dockerem, krok po kroku mówiąc co ma wykonać: zbuduj obraz, uruchom kontener, wyślij obraz, usuń wolumen itp.
Docker Compose przełożył w/w polecenia na modę deklaratywną, gdzie nie interesuje nas jak coś ma być zrobione – my tylko wskazujemy efekt, który ma zostać osiągnięty. Nie inaczej jest z K8s – mówimy programowi jak ma wyglądać efekt końcowy – za pośrednictwem plików .yaml np:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: myapp-image:latest
ports:
- containerPort: 80
Powyższy plik na pierwszy rzut oka (i kilka kolejnych też) może wydawać się niezrozumiały, ale gdybyśmy to wszystko chcieli zaprogramować imperatywnie to dopiero wpadlibyśmy w szambo z którego trudno byłoby się wydostać.
Potęga Kubernetesa tkwi właśnie w tym, że my tylko pokazujemy mu efekt końcowy na którym nam zależy, a on sam – dzięki tym milionom linijek kodu – ustawia wszystko, tak abyśmy byli zadowoleni.
Kubernetes jako aplikacja
Pozostały nam jeszcze dwa terminy do rozszyfrowania:
Deployment oraz Service
O ile K8s działa na fizycznych Node’ach i tam zarządza pracą Podów, to samych Podów raczej nie tworzy się pojedynczo. Gdyby tak było to wystarczyłby Docker Compose.
Podstawową jednostką – nazwijmy ją „operacyjną” – jest „deployment” który na polski tłumaczy się jako „rozlokowanie” co jednak nie brzmi szczególnie zrozumiale. Osobiście wolę to sobie obrazować jako coś w stylu oddziału, np. żołnierzy w jakiejś strategii. Tam pod klawiszem szybkiego dostępu mamy np. 8 wojaków na których składa się 4 piechurów do walki w zwarciu, trzy jednostki strzeleckie i jeden medyk. To jest nasz stan aktualny i jednocześnie docelowy, opisany szczegółowo w pliku .yaml.
Z kolei usługa (Service) opisuje sposób w jaki nasze Pody komunikują się wewnętrznie (między sobą) i zewnętrznie (z Internetem).
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
Żeby te pliki zadziałały to trzeba je najpierw zaaplikować Kubernetesowi, a służy do tego aplikacja
kubeclt
Dzięki kubectl możemy podejrzeć pracę naszych Podów, manualnie je dodawać i usuwać oraz wiele więcej. Ale chyba najważniejszą operacją będzie aplikacja naszych yaml’owych ustawień dzięki czemu wszystko to będzie się dziać automatycznie. A wystarczy tylko jedna komenda:
kubectl apply -f=deployment.yaml

Alternatywy
Podobnie jak Docker, Kubernetes stał się już swojego rodzaju standardem jeśli chodzi o automatyzację i zarządzanie pracą kontenerów aplikacji. Z jednej strony to dobrze, bo jest zupełnie jak z Sauronownym pierścieniem:
Jeden by wszystkimi (kontenerami) rządzić
i w ciemności klaster złączyć.
Znajomość K8s przyda się zarówno gdy samodzielnie chcemy pobawić się w DevOpsów, ale również gdy zdecydujemy się na chmurę od któregoś z wielkiej trójki:
AWS Azure GCloud
Pomimo, że każdy z nich ma konkurencyjne rozwiązania, to jednocześnie każdy respektuje potęgę Kubernetesa i pozwala na korzystanie z jego architektury oraz ustawień.
Z drugiej strony, taka moc oznacza wysoki stopień skomplikowania samego K8-esa przez co zmasterowanie wszystkich jego możliwości to zdanie na długie lata. Pewnym ułatwieniem będzie tutaj nakładka o nazwie „OpenShift” która owszem, ma Kubernetesa „pod maską” ale stara się wiele rzeczy ułatwiać. Pamiętajmy jednakowoż, że K8s to projekt typu open-source z którego każdy bezpłatnie może korzystać, zaś OpenShift to już licencjonowany produkt Red Hata.
Innym wyjściem może być również skorzystanie z Docker Swarm, który oferuje skromniejsze możliwości od tytułowego bohatera, ale jest też o wiele łatwiejszy do rozpracowania i zastosowania w mniejszych projektach.

Ok, myślę, że temat Kubernetesa został nakreślony przyzwoicie* i albo to powinno zaspokoić waszą ciekawość w czasie kiedy wciąż wciągacie podstawy waszego pierwszego języka programowania, albo możecie śmiało uderzać do źródeł poniżej.
(*) Delikatnie mówiąc… Ten odcinek jest bez mała najdłuższym w całej serii i okrutnie mnie wymęczył.
- Czy są jeszcze jakieś pytania?
- Tak, czy mógłbym?
- Oczywiście, słucham.
- Zakładając, że kolega… ekhem… CZYSTO HIPOTETYCZNIE OCZYWIŚCIE… chciałby się pobawić swoim Kubernetesem… własnoręcz… znaczy samodzielnie, bez partne… bez udziału osób trzecich. Czy mam… czy on ma taką możliwość?
- …czysto hipotetycznie?
- Czysto hipotetycznie!
- Zatem czysto hipotetycznie istnieje taka opcja – ale tylko pod warunkiem, że ma małego.

Odpowiedzią na potrzeby singli jest mały Kubernetes pod postacią tzw. „Minikube„. Z pomocą maszyn wirtualnych możemy utworzyć sobie własny klaster z Nodami, podami i co tam jeszcze chcemy. Sprawdzi się idealnie do małych projektów i w celach edukacyjnych.

Źródła:
Kurs Docker & Kubernetes na Udemy (płatne) ANG
Kurs Kubernetes na Udemy (płatne) POL
Kubernetes & Docker Swarm YT/ANG
Kubernetes & Open Shift YT/ANG