
Nazywam się Dmitrij Sugrobow, jestem programistą w „Leroy Merlin”. W artykule opowiem, po co potrzebny jest Helm, jak upraszcza pracę z Kubernetes, co się zmieniło w trzeciej wersji i jak za jego pomocą aktualizować aplikacje w produkcji bez przestojów.
To jest streszczenie na podstawie wystąpienia na konferencji by — jeśli nie chcesz czytać, obejrzyj wideo.

Dlaczego używamy Kubernetes w produkcji
„Leroy Merlin” jest liderem na rynku DIY w Rosji i Europie. W naszej firmie pracuje ponad stu programistów, 33 000 pracowników i ogromna liczba osób odwiedzających hipermarkety i stronę internetową. Aby uszczęśliwić wszystkich, postanowiliśmy stosować standardowe podejścia w branży. Tworzyć nowe aplikacje, korzystając z architektury mikrousługowej; aby izolować środowiska i zapewnić właściwą dostawę, stosować kontenery; a do orkiestracji używać Kubernetes. Koszt korzystania z orkiestratorów szybko maleje: na rynku przybywa inżynierów znających tę technologię, pojawiają się dostawcy oferujący Kubernetes jako usługę.
Wszystko, co robi Kubernetes, można oczywiście osiągnąć innymi metodami, na przykład używając Jenkins i docker-compose, ale po co utrudniać sobie życie, jeśli jest gotowe i niezawodne rozwiązanie? Dlatego zdecydowaliśmy się na Kubernetes i już od roku używamy go w produkcji. Obecnie mamy dwadzieścia cztery klastry Kubernetes, najstarszy z nich ma ponad rok, w nim jest około dwustu podów.
Przekleństwo dużych ilości plików YAML w Kubernetes
Aby uruchomić mikrousługę w Kubernetes, musimy stworzyć co najmniej pięć plików YAML: dla Deployment, Service, Ingress, ConfigMap, Secrets — i wysłać je do klastra. Dla następnej aplikacji napiszemy ten sam zestaw plików YAML, dla trzeciej — jeszcze jeden i tak dalej. Pomnóżmy liczbę dokumentów przez liczbę środowisk, już otrzymujemy setki plików, nie uwzględniając jeszcze dynamicznych środowisk.

Adam Reese, główny opiekun Helm, wprowadził pojęcie „”, który wygląda tak:
- Copy YAML — skopiuj plik YAML.
- Paste YAML — wklej go.
- Fix Indents — popraw wcięcia.
- Repeat — powtórz.
Opcja działa, ale trzeba wiele razy kopiować pliki YAML. Aby zmienić ten cykl, wymyślono Helm.
Czym jest Helm
Po pierwsze, Helm — menedżer pakietów, pomagający znaleźć i zainstalować potrzebne programy. Aby zainstalować, na przykład, MongoDB nie trzeba wchodzić na oficjalną stronę i pobierać plików binarnych, wystarczy wykonać polecenie helm install stable/mongodb.
Po drugie, Helm — szablonowanie, pomaga parametryzować pliki. Wróćmy do sytuacji z plikami YAML w Kubernetes. Łatwiej jest napisać ten sam plik YAML, dodać do niego kilka placeholderów, w które Helm podstawia wartości. To znaczy, że zamiast dużego zestawu plików YAML będziemy mieli zestaw szablonów, w które w odpowiednim momencie zostaną podstawione potrzebne wartości.
Po trzecie, Helm — mistrz wdrożeń. Dzięki niemu można instalować, cofać i aktualizować aplikacje. Zobaczmy, jak to zrobić.

Jak używać Helm do wdrażania własnych aplikacji
Zainstalujemy klienta Helm na komputerze, postępując zgodnie z oficjalną . Następnie stworzymy zestaw plików YAML. Zamiast podawania konkretnych wartości zostawimy placeholdery, które w przyszłości Helm wypełni informacjami. Zestaw takich plików nazywa się Helm chart. Do klienta konsolowego Helm można go wysłać na trzy sposoby:
- wskazać folder z szablonami;
- zapakować w archiwum .tar i wskazać na nie;
- dodać szablon do zdalnego repozytorium i dodać link do repozytorium w kliencie Helm.
Niezbędny jest także plik ze wartościami — values.yaml. Dane z niego będą podstawiane w szablon. Stwórzmy go również.

W drugiej wersji Helm istnieje dodatkowa aplikacja serwerowa — Tiller. Działa na zewnątrz Kubernetes i czeka na żądania od klienta Helm, a po ich przyjęciu podstawia odpowiednie wartości w szablon i wysyła do Kubernetes.

Helm 3 jest prostszy: zamiast przetwarzania szablonów na serwerze, informacje są teraz przetwarzane całkowicie po stronie klienta Helm i wysyłane bezpośrednio do API Kubernetes. To uproszczenie zwiększa bezpieczeństwo klastra i ułatwia proces wdrażania.
Jak to wszystko działa
Uruchamiamy polecenie helm install. Wskażemy nazwę wydania aplikacji, podamy ścieżkę do values.yaml. Na końcu wskażemy repozytorium, w którym znajduje się chart oraz jego nazwę. W tym przypadku to „lmru” i „bestchart” odpowiednio.
helm install --name bestapp --values values.yaml lmru/bestchart
Wykonanie polecenia jest możliwe tylko raz, przy powtórnym wykonaniu zamiast install trzeba użyć upgrade. Dla uproszczenia zamiast dwóch poleceń można wykonać polecenie upgrade z dodatkowym kluczem --install. Przy pierwszym uruchomieniu Helm wyśle polecenie do zainstalowania wydania, a następnie będzie je aktualizować.
helm upgrade --install bestapp --values values.yaml lmru/bestchart
Pułapki w deploymencie nowych wersji aplikacji z Helm
W tym momencie opowieści gram z salą w 'Kto chce zostać milionerem', i ustalamy, jak zmusić Helm do aktualizacji wersji aplikacji. .
Kiedy uczyłem się pracy z Helm, zaskoczyło mnie dziwne zachowanie podczas próby aktualizacji wersji uruchomionych aplikacji. Kod aplikacji został zaktualizowany, nowy obraz został załadowany do rejestru Dockera, wysłałem polecenie do wdrożenia – i nic się nie stało. Poniżej kilka nie do końca udanych sposobów aktualizacji aplikacji. Badając każdy z nich bardziej szczegółowo, zaczynasz rozumieć wewnętrzną strukturę narzędzia oraz przyczyny takiego nieoczywistego zachowania.
Sposób 1. Nie zmieniać informacji od ostatniego uruchomienia
Jak głosi Helm, 'Kubernetes chartry mogą być duże i skomplikowane, więc Helm stara się niczego nie zmieniać bez potrzeby'. Dlatego, jeśli zaktualizujesz najnowszą wersję obrazu aplikacji w rejestrze dockera i wykonasz polecenie helm upgrade, to nic się nie stanie. Helm będzie myślał, że nic się nie zmieniło i nie będzie potrzeby wysyłania w Kubernetes polecenia do aktualizacji aplikacji.
Tutaj i dalej tag latest pokazany jest wyłącznie jako przykład. Przy wskazywaniu tego tagu Kubernetes za każdym razem pobiera obraz z rejestru dockera, niezależnie od parametru imagePullPolicy. Używanie latest w produkcji jest niepożądane i powoduje efekty uboczne.
Sposób 2. Aktualizować LABEL w obrazie
Jak napisano w tym samym , 'Helm będzie aktualizować aplikację, tylko jeśli zmieniła się od ostatniego wydania'. Logicznym sposobem na to wydaje się aktualizacja etykiety LABEL w samym obrazie dockera. Jednak Helm nie zagląda do obrazów aplikacji i nie ma pojęcia o jakichkolwiek zmianach w nich. W związku z tym, przy aktualizacji etykiet w obrazie, Helm ich nie zauważy, a polecenie aktualizacji aplikacji w Kubernetes nie zostanie wysłane.
Sposób 3. Używać klucza --force

Zwróćmy się do podręczników i poszukajmy odpowiedniego klucza. Najbardziej sensownym zdaje się klucz --force. Pomimo mówiącej nazwy, zachowanie różni się od oczekiwanego. Zamiast wymuszać aktualizację aplikacji, rzeczywistym przeznaczeniem jest przywrócenie wydania w statusie FAILED. Jeśli ten klucz nie jest używany, konieczne jest sequentialne wykonanie poleceń helm delete && helm install --replace. Zamiast tego proponuje się użycie klucza --force, który automatyzuje sekwencyjne wykonanie tych poleceń. Więcej informacji w tej . Aby poinformować Helm o konieczności zaktualizowania wersji aplikacji, niestety ten klucz się nie nadaje.
Sposób 4. Zmiana oznaczeń bezpośrednio w Kubernetes

Aktualizacja etykiety bezpośrednio w klastrze za pomocą polecenia kubectl edit — to zły pomysł. Ta akcja doprowadzi do niespójności informacji pomiędzy działającą aplikacją a tym, co pierwotnie zostało wysłane na wdrożenie. Zachowanie Helma podczas wdrożenia w tym przypadku różni się od jego wersji: Helm 2 nic nie zrobi, a Helm 3 wyda nową wersję aplikacji. Aby zrozumieć przyczynę, należy zrozumieć, jak działa Helm.
Jak działa Helm
Aby określić, czy aplikacja zmieniła się od ostatniego wydania, Helm może skorzystać z:
- działającej aplikacji w Kubernetes;
- nowego pliku values.yaml i aktualnego wykresu;
- wewnętrznych informacji Helma o wydaniach.
Dla najbardziej dociekliwych: gdzie Helm przechowuje wewnętrzne informacje o wydaniach?Wykonując polecenie helm history, otrzymamy wszystkie informacje o wersjach zainstalowanych za pomocą Helma.

Dodatkowo dostępne są szczegółowe informacje o przesłanych szablonach i wartościach. Możemy je zapytać:

W drugiej wersji Helma te informacje znajdują się w tym samym przestrzeni nazw, gdzie uruchomiony jest Tiller (domyślnie — kube-system), w ConfigMap oznaczonym etykietą „OWNER=TILLER”:

W momencie pojawienia się trzeciej wersji Helma informacje przeniosły się do sekretów, w tym samym przestrzeni nazw, gdzie uruchomiona jest aplikacja. Dzięki temu stało się możliwe jednoczesne uruchamianie kilku aplikacji w różnych przestrzeniach nazw z identycznymi nazwami wydań. W drugiej wersji było to duży ból głowy, gdy przestrzenie nazw były izolowane, ale mogły na siebie wpływać.

Drugi Helm, gdy próbuje określić, czy aktualizacja jest wymagana, korzysta tylko z dwóch źródeł informacji: tego, co zostało mu teraz dostarczone, oraz wewnętrznych informacji o wydaniach, które znajdują się w ConfigMap.

Trzeci Helm używa strategii trójstronnego scalania: oprócz tej informacji bierze pod uwagę także aplikację, która działa obecnie w Kubernetes.

Z tego powodu stara wersja Helma nie będzie nic robić, ponieważ nie uwzględnia informacji o aplikacji w klastrze, a Helm 3 otrzyma zmiany i wdroży nową aplikację.
Metoda 5. Użyj klucza —recreate-pods
Z pomocą klucza --recreate-pods można osiągnąć to, co pierwotnie planowano uzyskać za pomocą klucza --force. Kontenery zostaną ponownie uruchomione, a zgodnie z polityką imagePullPolicy: Always dla tagu latest (o tym w przypisie powyżej), Kubernetes pobierze i uruchomi nową wersję obrazu. Zrobi to w sposób niezbyt optymalny: bez uwzględnienia StrategyType wdrożenia, nagle wyłączy wszystkie stare instancje aplikacji i przystąpi do uruchamiania nowych. W czasie ponownego uruchamiania system nie będzie działał, użytkownicy będą cierpieć.
W samym Kubernetes podobny problem również istniał przez długi czas. I oto, cztery lata po zgłoszeniu , problem został rozwiązany, a począwszy od wersji 1.15 Kubernetes pojawiła się możliwość rolling-restart podów.
Helm po prostu wyłącza wszystkie aplikacje i uruchamia nowe kontenery obok. Na produkcji nie można tak robić, aby nie wywołać przestoju aplikacji. Takie coś można stosować tylko do celów deweloperskich, można to robić tylko w środowiskach stagingowych.
Jak zaktualizować wersję aplikacji za pomocą Helma?
Będziemy zmieniać wartości przekazywane do Helma. Zazwyczaj są to wartości używane w miejsce tagu obrazu. W przypadku latest, często używanego w środowiskach nieprodukcyjnych, zmienną informacją jest adnotacja, która dla samego Kubernetesa jest bezużyteczna, a dla Helma służy jako sygnał do konieczności aktualizacji aplikacji. Opcje wypełnienia wartości adnotacji:
- Losowa wartość z użyciem standardowej funkcji —
{{ randAlphaNum 6 }}.
Jest pewna kwestia: po każdym wdrożeniu z użyciem wykresu z taką zmienną wartość adnotacji będzie unikalna, a Helm będzie uważał, że zaszły zmiany. Oznacza to, że zawsze będziemy ponownie uruchamiać aplikację, nawet jeśli nie zmieniliśmy jej wersji. To nie jest krytyczne, ponieważ nie będzie przestoju, ale wciąż jest to nieprzyjemne. - Wstawiać aktualną datę i czas —
{{ .Release.Date }}.
Opcja ta przypomina losową wartość z ciągle unikalną zmienną. - Bardziej właściwy sposób — użyć sumy kontrolne. To jest SHA obrazu lub SHA ostatniego commita w Gicie —
{{ .Values.sha }}.
Trzeba będzie je obliczyć i wysłać do klienta Helm po stronie wywołującej, na przykład w Jenkinsie. Jeśli aplikacja się zmieni, to również suma kontrolna się zmieni. W związku z tym, Helm zaktualizuje aplikację tylko wtedy, gdy zajdzie taka potrzeba.
Podsumujmy nasze próby
- Helm wprowadza zmiany w sposób najmniej inwazyjny, więc jakakolwiek zmiana na poziomie obrazu aplikacji w Rejestrze Dockera nie spowoduje aktualizacji: po wykonaniu polecenia nic się nie stanie.
- Klucz
--forcejest używany do przywracania problematycznych wydań i nie jest związany z wymuszonymi aktualizacjami. - Klucz
--recreate-podswymusi aktualizację aplikacji, ale zrobi to w brutalny sposób: nagle wyłączy wszystkie kontenery. Na tym ucierpią użytkownicy, w produkcji nie powinno się tak robić. - Nie wprowadzaj zmian w klastrze Kubernetes za pomocą polecenia
kubectl editnie należy: naruszymy spójność, a zachowanie będzie się różnić w zależności od wersji Helma. - Z wydaniem nowej wersji Helma pojawiło się wiele niuansów. Problemy w repozytorium Helma są opisane zrozumiałym językiem, pomogą zrozumieć szczegóły.
- Dodanie zmiennej adnotacji do chartu uczyni go bardziej elastycznym. Umożliwi to prawidłowe wdrażanie aplikacji, bez przestojów.
Mądrość z kategorii „pokój na całym świecie”, działająca we wszystkich dziedzinach życia: czytaj instrukcję przed użyciem, a nie po. Tylko posiadając pełne informacje, można budować niezawodne systemy i uszczęśliwiać użytkowników.
Inne linki na ten temat:
Ta prezentacja pierwszy raz została zaprezentowana na by Mail.ru Cloud Solutions. Zobacz inne prezentacje i subskrybuj zapowiedzi wydarzeń w Telegramie .
Źródło: habr.com
