Cześć wszystkim! Nazywam się Paweł Agalewski. Pracuję jako team leader w zespole, który opracowuje system dostaw Lamoda. W 2018 roku występowałem na konferencji HighLoad++, a dziś chcę przedstawić transkrypcję swojego wystąpienia.
Mój temat jest poświęcony doświadczeniu naszej firmy w zakresie wdrażania systemów i usług w różne środowiska. Zaczynając od naszych prehistorycznych czasów, kiedy wdrażaliśmy wszystkie systemy na standardowe serwery wirtualne, aż po stopniowe przejście od Nomad do wdrożeń w Kubernetes. Opowiem o tym, dlaczego to zrobiliśmy i jakie mieliśmy w tym procesie problemy.

Wdrażanie aplikacji na VM
Zacznijmy od tego, że 3 lata temu wszystkie systemy i usługi w firmie były wdrażane na standardowych serwerach wirtualnych. Technicznie było to zorganizowane tak, że cały kod naszych systemów był przechowywany i kompilowany za pomocą automatycznego systemu budowania, przy pomocy jenkins. Przy pomocy Ansible była on wdrażana z naszej systemu kontroli wersji na serwery wirtualne. Przy tym każdy system, który był w naszej firmie, był wdrażany na przynajmniej 2 serwery: jeden z nich - na head, drugi - na tail. Te dwa systemy były absolutnie identyczne pod względem wszystkich swoich ustawień, mocy, konfiguracji i innych aspektów. Różnica między nimi polegała jedynie na tym, że head otrzymywał ruch użytkowników, a tail nigdy nie otrzymywał ruchu użytkowników.
Dlaczego to zostało zrobione?
Gdy wdrażaliśmy nowe wersje naszej aplikacji, chcieliśmy zapewnić możliwość płynnego wdrożenia, to znaczy bez zauważalnych konsekwencji dla użytkowników. Osiągano to poprzez to, że kolejna skompilowana wersja za pomocą Ansible była wdrażana na tail. Tam osoby zajmujące się wdrożeniem mogły sprawdzić i upewnić się, że wszystko działa: wszelkie metryki, sekcje i aplikacje działają; uruchamiane są potrzebne skrypty. Dopiero po upewnieniu się, że wszystko jest w porządku, ruch był przełączany. Zaczynał kierować się na ten serwer, który wcześniej był tail. A ten, który wcześniej był head-em, pozostawał bez ruchu użytkowników, aczkolwiek z istniejącą na nim poprzednią wersją naszej aplikacji.
W ten sposób dla użytkowników było to bezproblemowe. Ponieważ przełączenie odbywa się natychmiast, jako że to po prostu przełączenie równoważnika obciążenia. Bardzo łatwo można wrócić do poprzedniej wersji, po prostu przełączając równoważnik z powrotem. Również mogliśmy upewnić się o zdolności aplikacji w produkcji jeszcze zanim użytkownikowy ruch zacznie na nią napływać, co było dość wygodne.
Jakie widzieliśmy w tym wszystkim korzyści?
- Przede wszystkim, to dość prosto działa. Wszyscy rozumieją, jak funkcjonuje podobny schemat wdrożenia, ponieważ większość ludzi kiedykolwiek wdrażała na zwykłe serwery wirtualne.
- To dość niezawodne, ponieważ technologia wdrożenia jest prosta, przetestowana przez tysiące firm. Miliony serwerów wdraża się w ten sposób. Trudno coś zepsuć.
- I w końcu mogliśmy uzyskać atomowe wdrożenia. Wdrożenia, które dla użytkowników odbywają się natychmiast, bez widocznego etapu przełączania między starą a nową wersją.
Ale w tym wszystkim widzieliśmy także kilka wad:
- Oprócz środowiska produkcyjnego, jest także środowisko deweloperskie i inne. Na przykład, qa i preproduction. W tamtym czasie mieliśmy dużo serwerów i około 60 usług. Z tego powodu musieliśmy utrzymywać aktualną wersję maszyny wirtualnej dla każdej usługi. Co więcej, jeśli chcesz zaktualizować biblioteki lub zainstalować nowe zależności, musisz to zrobić we wszystkich środowiskach. Również trzeba było synchronizować czas, kiedy zamierzasz wdrożyć nową wersję swojej aplikacji, z czasem, kiedy devops wykona niezbędne konfiguracje środowiska. W takim przypadku łatwo wpaść w sytuację, w której środowisko różni się nieco w każdym środowisku. Na przykład, w środowisku QA będą inne wersje bibliotek, a w produkcji — inne, co doprowadzi do problemów.
- Trudności w aktualizacji zależności twojej aplikacji. To nie zależy od Ciebie, ale od innego zespołu. A mianowicie, od zespołu devops, który utrzymuje serwery. Musisz postawić przed nimi odpowiednie zadanie i podać opis tego, co chcesz zrobić.
- W tamtym czasie również chcieliśmy podzielić istniejące duże monolity na osobne małe serwisy, ponieważ zdawaliśmy sobie sprawę, że będzie ich coraz więcej. Na ten moment mieliśmy już ich ponad 100. Było konieczne tworzenie dla każdego nowego serwisu oddzielnej nowej maszyny wirtualnej, którą również trzeba było obsługiwać i wdrażać. Poza tym potrzebna była nie jedna, a przynajmniej dwie maszyny. Do tego dochodzi jeszcze środowisko QA. To powoduje problemy i sprawia, że tworzenie i uruchamianie nowych systemów staje się bardziej skomplikowane, kosztowne i czasochłonne.
Dlatego podjęliśmy decyzję, że łatwiej będzie przejść z wdrożenia zwykłych maszyn wirtualnych na wdrożenie naszych aplikacji w kontenerze docker. Przy obecności dockera potrzebny jest system, który będzie w stanie uruchomić aplikację w klastrze, ponieważ nie można po prostu tak podnieść kontenera. Zazwyczaj chce się śledzić, ile kontenerów jest uruchomionych, aby uruchamiały się automatycznie. Z tego powodu musieliśmy wybrać system zarządzania.
Długo zastanawialiśmy się, który z nich mógłby być odpowiedni. Chodzi o to, że w tamtym czasie nasza stos wdrożeniowy na zwykłe serwery wirtualne był nieco przestarzały, ponieważ były tam nie najnowsze wersje systemów operacyjnych. W pewnym momencie był tam nawet FreeBSD, którego nie było łatwo utrzymać. Zdawaliśmy sobie sprawę, że musimy jak najszybciej migrować do dockera. Nasi devops przyjrzeli się swojemu dotychczasowemu doświadczeniu z różnymi rozwiązaniami i wybrali system Nomad.
Przejście na Nomad
Nomad to produkt firmy "HashiCorp". Są oni również znani z innych swoich rozwiązań:

"Consul" — to narzędzie do odkrywania serwisów.
"Terraform" — to system do zarządzania serwerami, który pozwala na ich konfigurację za pomocą tzw. infrastructure-as-a-code.
"Vagrant" pozwala na uruchamianie maszyn wirtualnych lokalnie lub w chmurze za pomocą określonych plików konfiguracyjnych.
Na ten moment Nomad wydawał się wystarczająco prostym rozwiązaniem, na które można szybko przejść bez zmiany całej infrastruktury. Co więcej, jest dość łatwy do opanowania. Dlatego właśnie jego wybraliśmy jako system filtracji naszego kontenera.
Co jest potrzebne, aby w ogóle wdrożyć Twój system w Nomad?
- Przede wszystkim potrzebny jest obraz docker Twojej aplikacji. Należy ją zebrać i umieścić w magazynie obrazów docker. W naszym przypadku jest to artifactory – taki system, który pozwala na wgrywanie do niego różnych artefaktów różnego typu. Potrafi przechowywać archiwa, obrazy docker, pakiety composer PHP, pakiety NPM i tak dalej.
- Również potrzebny plik konfiguracyjny, który powie Nomadowi, co, gdzie i w jakiej ilości chcesz wdrożyć.
Kiedy mówimy o Nomadzie, używa on języka HCL jako formatu pliku informacyjnego, co rozszyfrowuje się jako HashiCorp Configuration Language. To jest nadzbiór języka Yaml, który pozwala na opisanie twojej usługi w terminach Nomada.

Pozwala on określić, ile kontenerów chcesz wdrożyć, przekazać im różne parametry podczas wdrożenia. W ten sposób podajesz ten plik Nomadowi, a on uruchamia kontenery w produkcji zgodnie z jego zawartością.
W naszym przypadku zrozumieliśmy, że pisanie absolutnie identycznych plików HCL dla każdej usługi nie będzie zbyt wygodne, ponieważ usług jest wiele i czasami chcemy je zaktualizować. Bywa, że jedna usługa jest wdrożona w wielu egzemplarzach. Na przykład jeden z systemów, który mamy w produkcji, ma ponad 100 instancji w produkcji. Uruchamiane są z tych samych obrazów, ale różnią się ustawieniami konfiguracyjnymi i plikami konfiguracyjnymi.
Dlatego postanowiliśmy, że wygodniej będzie przechowywać wszystkie nasze pliki konfiguracyjne do wdrożenia w jednym wspólnym repozytorium. W ten sposób stają się one przeglądane: łatwo je utrzymać i można zobaczyć, jakie systemy mamy. W razie potrzeby również łatwo coś zaktualizować lub zmienić. Dodanie nowego systemu również nie sprawi problemu – wystarczy po prostu stworzyć plik konfiguracyjny w nowym katalogu. Wewnątrz znajdują się pliki: service.hcl, który zawiera opis naszej usługi, oraz kilka plików env, które pozwalają skonfigurować tę usługę, będąc wdrożoną w produkcji.

Jednak niektóre nasze systemy są wdrożone w produkcji nie w jednym egzemplarzu, a w kilku naraz. Dlatego postanowiliśmy, że wygodniej będzie przechowywać ich nie w czystej postaci, lecz w formie z szablonami. Jako język szablonów wybraliśmy jinja 2. W takim formacie przechowujemy zarówno konfiguracje samej usługi, jak i pliki env, które są dla niej potrzebne.
Ponadto umieściliśmy w repozytorium wspólny skrypt wdrożeniowy dla wszystkich projektów, który pozwala uruchomić i wdrożyć usługę na produkcji, w odpowiednim środowisku i w odpowiednim celu. W przypadku, gdy przekształciliśmy naszą konfigurację HCL w szablon, ten plik HCL, który wcześniej był zwykłą konfiguracją Nomad, w tym przypadku wygląda trochę inaczej.

To znaczy, że zastąpiliśmy niektóre zmienne miejsca konfiguracji wstawieniami zmiennych, które pochodzą z plików env lub innych źródeł. Oprócz tego uzyskaliśmy możliwość dynamicznego generowania plików HCL, co oznacza, że możemy stosować nie tylko zwykłe wstawienia zmiennych. Ponieważ jinja obsługuje pętle i warunki, można również tworzyć pliki konfiguracyjne, które zmieniają się w zależności od tego, gdzie dokładnie wdrażasz swoje aplikacje.
Na przykład chcesz wdrożyć swoją usługę w środowisku przedprodukcyjnym i produkcyjnym. Załóżmy, że w środowisku przedprodukcyjnym nie chcesz uruchamiać skryptów cron, a po prostu chcesz zobaczyć usługę na osobnej domenie, aby upewnić się, że działa. Dla każdego, kto wdraża usługę, proces wygląda bardzo prosto i przejrzyście. Wystarczy wykonać plik deploy.sh, wskazać, którą usługę chcesz wdrożyć i w jakim celu. Na przykład, chcesz wdrożyć jakiś system w Rosji, Białorusi lub Kazachstanie. Wystarczy po prostu zmienić jeden z parametrów, a odpowiedni plik konfiguracyjny zostanie wygenerowany.
Gdy usługa Nomad zostanie wdrożona w klastrze, wygląda to następująco.

Na początek potrzebujesz zunifikowanego balansu na zewnątrz, który przyjmuje cały ruch użytkowników. Będzie współpracował z Consul i dowie się od niego, gdzie, na której nodzie, pod jakim adresem IP znajduje się konkretna usługa, która odpowiada odpowiedniemu nazwie domeny. Usługi w Consul pojawiają się z samego Nomad. Ponieważ są to produkty tej samej firmy, są dobrze ze sobą powiązane. Można powiedzieć, że Nomad z pudełka potrafi rejestrować wszystkie uruchamiane w nim usługi wewnątrz Consul.
Po tym, jak zewnętrzny load balancer dowie się, do którego serwisu należy wysłać ruch, przekierowuje go do odpowiedniego kontenera lub do kilku kontenerów, które odpowiadają twojej aplikacji. Oczywiście należy również pomyśleć o bezpieczeństwie. Nawet mimo że wszystkie serwisy działają na tych samych maszynach wirtualnych w kontenerach, zazwyczaj wymaga to zakazu swobodnego dostępu z jakiegoś serwisu do innego. Osiągaliśmy to poprzez segmentację. Każdy serwis działał w swojej własnej sieci wirtualnej, w której ustalono zasady routingu i zasady zezwalania/zakazu dostępu do innych systemów i serwisów. Mogły one znajdować się zarówno wewnątrz tego klastra, jak i na zewnątrz. Na przykład, jeśli chcesz zabronić serwisowi łączenia się z określoną bazą danych, można to zrobić poprzez segmentację na poziomie sieci. Oznacza to, że nie możesz przypadkowo połączyć się z bazą danych produkcyjną z środowiska testowego.
Ile nas kosztował proces przejścia pod względem zasobów ludzkich?
Przejście całej firmy na Nomad zajęło około 5-6 miesięcy. Przechodziliśmy serwis po serwisie, ale w dość szybkim tempie. Każdy zespół musiał stworzyć własne kontenery dla swoich serwisów.
Mamy przyjęte podejście, że każdy zespół odpowiada samodzielnie za obrazy docker swoich systemów. DevOps zapewniają wspólną infrastrukturę niezbędną do wdrożeń, czyli wsparcie samego klastra, wsparcie systemu CI i tak dalej. W tym czasie ponad 60 systemów przeszło na Nomad, co zaowocowało około 2000 kontenerów.
DevOps odpowiadają za ogólną infrastrukturę wszystkiego, co związane z wdrożeniem i serwerami. Natomiast każdy zespół deweloperski odpowiada za implementację kontenerów dla swojego konkretnego systemu, ponieważ to właśnie zespół wie, co tak naprawdę potrzebuje w danym kontenerze.
Powody rezygnacji z Nomad
Jakie korzyści uzyskaliśmy, przechodząc na wdrożenie z pomocą Nomad i Dockera?
- My zapewniliśmy równe warunki dla wszystkich środowisk. W środowisku deweloperskim, QA, prerelease i produkcyjnym używane są te same obrazy kontenerów, z tymi samymi zależnościami. Z tego powodu praktycznie nie ma szans, że na produkcję trafi coś, czego wcześniej nie przetestowano lokalnie lub w środowisku testowym.
- Zauważyliśmy również, że wystarczy łatwo dodać nową usługę. Jakiekolwiek nowe systemy z perspektywy wdrożenia uruchamia się bardzo prosto. Wystarczy przejść do repozytorium, w którym przechowywane są konfiguracje, dodać tam kolejną konfigurację dla swojego systemu, a wszystko jest gotowe. Możesz wdrożyć swój system na produkcję bez dodatkowego wysiłku ze strony DevOps.
- Wszystkie pliki konfiguracyjne w jednym ogólnym repozytorium okazały się przeglądane. W momencie, gdy wdrażaliśmy nasze systemy przy pomocy serwerów wirtualnych, używaliśmy Ansible, w którym konfiguracje znajdowały się w jednym i tym samym repozytorium. Jednak dla większości programistów praca z tym była nieco trudniejsza. Tutaj objętość konfiguracji i kodu, który musisz dodać, aby wdrożyć usługę, stała się znacznie mniejsza. Dodatkowo, dla DevOps bardzo łatwo jest to poprawić lub zmienić. W przypadku przejść, na przykład na nowej wersji Nomad, mogą oni zaktualizować masowo wszystkie pliki operacyjne znajdujące się w tym samym miejscu.
Ale napotkaliśmy również kilka wad:
Okazało się, że nie udało nam się osiągnąć bezszwowości wdrożeń w przypadku Nomad. Podczas wdrażania kontenerów z różnych warunków może się zdarzyć, że zostanie uruchomiony, a Nomad traktuje go jako gotowy do przyjęcia ruchu kontener. To zdarzało się jeszcze przed tym, jak aplikacja w środku miała czas, aby się uruchomić. Z tego powodu system przez krótki czas zaczynał zwracać błędy 500, ponieważ ruch zaczynał trafiać do kontenera, który jeszcze nie był gotowy do jego przyjęcia.
Napotkaliśmy na kilka błędów. Najistotniejszy błąd polega na tym, że Nomad nie radzi sobie dobrze z dużym klastrem, jeśli masz wiele systemów i kontenerów. Kiedy chcesz wyłączyć jeden z serwerów, który jest częścią klastra Nomad, istnieje dość duża szansa, że klaster nie będzie się czuł dobrze i rozpadnie się na kawałki. Część kontenerów może na przykład upaść i nie wstać — to później kosztuje cię bardzo drogo, jeśli wszystkie twoje systemy produkcyjne znajdują się w klastrze zarządzanym przez Nomad.
Dlatego postanowiliśmy zastanowić się, w jakim kierunku dalej iść. W tamtym momencie znacznie lepiej zaczęliśmy rozumieć, co chcemy osiągnąć. A mianowicie: chcemy niezawodności, trochę więcej funkcji niż daje Nomad oraz bardziej dojrzałego, stabilniejszego systemu.
W tym kontekście nasz wybór padł na Kubernetes jako na najpopularniejszą platformę do uruchamiania klastrów. Szczególnie biorąc pod uwagę, że rozmiar i liczba naszych kontenerów były dość duże. Do takich celów Kubernetes wydawał się najbardziej odpowiednim systemem z tych, które mogliśmy rozważyć.
Przejście na Kubernetes
Trochę opowiem o tym, jakie są podstawowe pojęcia Kubernetes i czym różnią się one od Nomad.

Przede wszystkim najważniejszym pojęciem w Kubernetes jest pojęcie pod. Pod — to grupa jednego lub kilku kontenerów, które zawsze uruchamiane są razem. Działają one jakby zawsze były ściśle na jednej maszynie wirtualnej. Są dla siebie dostępne pod adresem IP 127.0.0.1 na różnych portach.
Załóżmy, że masz aplikację PHP, która składa się z nginx i php-fpm – klasyczny schemat. Prawdopodobnie chcesz, aby kontenery nginx i php-fpm zawsze były razem. Kubernetes pozwala na to, opisując je jako jeden wspólny pod. Dokładnie tego nie mogliśmy osiągnąć przy użyciu Nomad.
Drugim pojęciem jest deployment. Chodzi o to, że pod sam w sobie to rzecz efemeryczna, uruchamia się i znika. Chcesz najpierw usunąć wszystkie swoje wcześniejsze kontenery, a potem uruchomić od razu nowe wersje, czy wolisz wprowadzać je stopniowo — to właśnie za ten proces odpowiada pojęcie deployment. Opisuje to, jak wdrażasz swoje pod'y, w jakiej liczbie i jak je aktualizować.
Trzecim pojęciem jest service. Twój service to w zasadzie twój system, który przyjmuje pewien ruch, a następnie kieruje go do jednego lub kilku podów odpowiadających twojemu serwisowi. Innymi słowy, umożliwia to przekazywanie całego przychodzącego ruchu do takiego serwisu o takim nazwie na konkretne te pody. Przy tym zapewnia ci balansowanie ruchu. Możesz uruchomić dwa pody twojej aplikacji, a cały przychodzący ruch będzie równomiernie rozdzielany pomiędzy odpowiednie pody związane z tym serwisem.
Czwarta podstawowa koncepcja — Ingress. To serwis uruchamiany w klastrze Kubernetes. Działa jako zewnętrzny balancer obciążenia, który przyjmuje wszystkie zapytania. Dzięki API Kubernetes Ingress może określić, dokąd należy wysłać te zapytania. Co ważne, robi to w bardzo elastyczny sposób. Możesz powiedzieć, że wszystkie zapytania na ten host i dany URL wysyłamy do tego serwisu. Natomiast te zapytania, które przychodzą do tego hosta na inny URL, wysyłamy do innego serwisu.
Najlepsze dla dewelopera aplikacji to to, że możesz tym wszystkim zarządzać samodzielnie. Ustalając konfigurację Ingress, możesz kierować cały ruch przychodzący na określone API do oddzielnych kontenerów, zapisanych na przykład w Go. A ten ruch przychodzący na ten sam domen, ale na inny URL, przesyłasz do kontenerów napisanych w PHP, gdzie jest dużo logiki, ale nie są one bardzo szybkie.
Porównując wszystkie te pojęcia z Nomad, można powiedzieć, że pierwsze trzy pojmowania to w sumie Service. A ostatnia koncepcja w Nomadzie nie istnieje. Wykorzystaliśmy zewnętrzny balancer: może to być haproxy, nginx, nginx+ i tak dalej. W przypadku Kubernetesa nie musisz wprowadzać tej dodatkowej koncepcji osobno. Jednak, patrząc na Ingress w środku, to jest albo nginx, albo haproxy, albo traefik, ale wbudowany w Kubernetes.
Wszystkie opisane przeze mnie pojęcia to w zasadzie zasoby, które istnieją w klastrze Kubernetes. Do ich opisu w kubie używany jest format yaml, bardziej czytelny i znajomy niż pliki HCL w przypadku Nomada. Strukturalnie opisują one dla przykładu pod to samo. Mówią — chcę wdrożyć takie cuda w tamto miejsce, z takimi obrazkami, w takiej liczbie.

Ponadto zrozumieliśmy, że nie chcemy ręcznie tworzyć każdego pojedynczego zasobu: wdrożenia, usług, Ingress i innych. Zamiast tego chcieliśmy w trakcie wdrożenia opisać każdy nasz system w terminach Kubernetes, aby nie musieć ręcznie odtworzyć w odpowiedniej kolejności wszystkich niezbędnych zależności zasobów. Jako system, który pozwolił nam to zrobić, wybrano Helm.
Podstawowe pojęcia w Helm
Helm to menedżer pakietów dla Kubernetes. Jest bardzo podobny do tego, jak działają menedżery pakietów w językach programowania. Pozwala on na przechowywanie usługi, składającej się na przykład z wdrożenia nginx, wdrożenia php-fpm, konfiguracji dla Ingress, configmaps (to jest byt, który pozwala na określenie env i innych parametrów dla twojego systemu) w postaci tzw. chartów. Przy tym Helm działa na Kubernetes. To znaczy, że nie jest to jakiś system stojący z boku, a po prostu jeszcze jedna usługa uruchamiana wewnątrz klastra. Interakcję z nim odbywasz przez jego API za pomocą polecenia w konsoli. Jego wygoda i zaleta polega na tym, że nawet jeśli Helm przestanie działać lub usuniesz go z klastra, twoje usługi nie znikną, ponieważ Helm służy zasadniczo tylko do uruchamiania systemu. Za sprawność i stan usług dalej odpowiada sam Kubernetes.
Zrozumieliśmy również, że szablonowanie, które wcześniej musieliśmy robić samodzielnie poprzez wdrożenie jinja w nasze konfiguracje, jest jedną z podstawowych możliwości Helma. Wszystkie konfiguracje, które tworzysz dla swoich systemów, są przechowywane w Helmie w postaci szablonów, które są trochę podobne do jinja, ale w rzeczywistości korzystają z szablonowania języka Go, w którym napisano Helma, tak samo jak Kubernetes.
Helm dodaje nam jeszcze kilka dodatkowych pojęć.
Chart to opis twojej usługi. W innych menedżerach pakietów nazwano by to pakietem, bundlem lub czymś podobnym. Tutaj nazywa się to chart.
Values to zmienne, które chcesz użyć do tworzenia swoich konfiguracji z szablonów.
ReleaseZa każdym razem serwis, który jest wdrażany za pomocą helm, otrzymuje inkrementalną wersję wydania. Helm zapamiętuje, jaka była konfiguracja serwisu przy poprzednim, przedpoprzednim wydaniu i tak dalej. Dlatego, jeśli trzeba się cofnąć, wystarczy wykonać polecenie helm callback, wskazując mu poprzednią wersję wydania. Nawet jeśli w momencie cofania odpowiednia konfiguracja w Twoim repozytorium nie będzie dostępna, helm wciąż pamięta, jak ona wyglądała, i przywróci Twój system do stanu, w jakim był przy poprzednim wydaniu.
W przypadku, gdy używamy helma, zwykłe konfiguracje dla Kubernetes również przekształcają się w szablony, w których można używać zmiennych, funkcji, stosować operatory warunkowe. W ten sposób możesz tworzyć konfigurację swojego serwisu w zależności od środowiska.

W praktyce zdecydowaliśmy się postąpić nieco inaczej niż w przypadku Nomad. Jeśli w Nomad w jednym repozytorium przechowywane były zarówno konfiguracje do wdrożenia, jak i zmienne n, które są potrzebne do wdrożenia naszego serwisu, to tutaj zdecydowaliśmy się je podzielić na dwa osobne repozytoria. W repozytorium "deploy" przechowywane są tylko zmienne n potrzebne do wdrożenia, a w repozytorium "helm" znajdują się konfiguracje lub wykresy.

Co nam to dało?
Mimo że w samych plikach konfiguracyjnych nie przechowujemy żadnych naprawdę wrażliwych danych, na przykład haseł do baz danych. Są one przechowywane jako sekrety w Kubernetes, jednak wciąż istnieją tam oddzielne rzeczy, do których nie chcemy dawać dostępu wszystkim. Dlatego dostęp do repozytorium "deploy" jest bardziej ograniczony, a repozytorium "helm" zawiera tylko opis serwisu. Z tego powodu można tam bezpiecznie dać dostęp szerszemu gronu osób.
Ponieważ mamy nie tylko produkcję, ale i inne środowiska, dzięki takiemu podziałowi możemy ponownie wykorzystać nasze wykresy helm, aby wdrażać serwisy nie tylko w produkcji, ale także na przykład w środowisku QA. Nawet do tego, aby uruchamiać je lokalnie, używając Minikube — to taka rzecz do lokalnego uruchamiania Kubernetes.
Wewnątrz każdego repozytorium pozostawiliśmy podział na oddzielne katalogi dla każdego serwisu. To znaczy, że w każdym katalogu znajdują się szablony związane z odpowiednim wykresem i opisujące zasoby, które należy wdrożyć, aby uruchomić nasz system. W repozytorium „deploy” pozostawiliśmy tylko zmienne środowiskowe. W tym przypadku nie zdecydowaliśmy się na użycie szablonizacji za pomocą jinja, ponieważ helm sam oferuje szablonizację z pudełka – to jedna z jego podstawowych funkcji.
Zostawiliśmy skrypt do wdrożenia – deploy.sh, który upraszcza i standaryzuje uruchamianie wdrożenia za pomocą helma. Tak więc dla każdego, kto chce wdrożyć, interfejs wdrożenia wygląda dokładnie tak samo, jak w przypadku wdrożenia przez Nomad. Taki sam deploy.sh, nazwa Twojego serwisu i miejsce, w które chcesz go wdrożyć. To prowadzi do uruchomienia helma, który z kolei zbiera konfiguracje z szablonów, podstawia w nich niezbędne pliki values, a następnie wdraża je, wysyłając do Kubernetes.
Wnioski
Serwis Kubernetes wydaje się bardziej skomplikowany niż Nomad.

Tutaj wychodzący ruch trafia do Ingress. To właśnie frontowy kontroler, który przyjmuje wszystkie żądania i następnie kieruje je do odpowiednich serwisów zgodnych z danymi żądania. Określa je na podstawie konfiguracji, które stanowią część opisu Twojej aplikacji w helmie i które programiści określają sami. Serwis wysyła żądania do swoich podów, czyli konkretnych kontenerów, równoważąc przychodzący ruch między wszystkimi kontenerami, które odnoszą się do danego serwisu. No i oczywiście nie można zapominać o tym, że w zakresie bezpieczeństwa na poziomie sieci, nie powinniśmy nigdzie uciekać. Dlatego w klastrze Kubernetes działa segmentacja, która opiera się na tagowaniu. Wszystkie serwisy mają określone tagi, do których są przypisane prawa dostępu serwisów do różnych zewnętrznych/wewnętrznych zasobów w lub poza klastrem.
Przechodząc do Kubernetes, zauważyliśmy, że ma on wszystkie możliwości, które oferował wcześniej używany przez nas Nomad, a także wiele nowych funkcji. Można go rozszerzać za pomocą wtyczek, a właściwie przez własne typy zasobów. Oznacza to, że masz możliwość nie tylko korzystać z tego, co jest dostępne w Kubernetes od ręki, ale także stworzyć własny zasób i usługę, która będzie odczytywać twój zasób. To daje dodatkowe możliwości rozszerzenia twojego systemu bez konieczności reinstalacji Kubernetes i bez potrzeby jakichkolwiek zmian.
Przykładem takiego użycia jest Prometheus, który jest uruchamiany w naszym klastrze Kubernetes. Aby mógł zacząć zbierać metryki z danego serwisu, musimy dodać do opisu serwisu dodatkowy typ zasobu, tak zwany monitor serwisowy. Prometheus, dzięki temu, że umie odczytywać własne typy zasobów, automatycznie zaczyna zbierać metryki z nowego systemu. To jest bardzo wygodne.
Pierwszy deploy, który przeprowadziliśmy w Kubernetes, miał miejsce w marcu 2018 roku. Od tego czasu nigdy nie mieliśmy z nim żadnych problemów. Działa stosunkowo stabilnie, bez poważnych błędów. Ponadto, możemy go dalej rozwijać. Na dzień dzisiejszy wystarczają nam możliwości, które oferuje, a tempo rozwoju Kubernetes bardzo nam się podoba. Obecnie w Kubernetes znajduje się ponad 3000 kontenerów. Klaster zajmuje kilka Node. Jest przy tym zarządzany, stabilny i bardzo kontrolowany.
Źródło: habr.com
