Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

W tym roku główna europejska konferencja Kubernetes – KubeCon + CloudNativeCon Europe 2020 – miała charakter wirtualny. Taka zmiana formatu nie przeszkodziła nam jednak w realizacji długo planowanego raportu „Go? Grzmotnąć! Poznaj operatora Shell” poświęconego naszemu projektowi Open Source operator powłoki.

W tym artykule, zainspirowanym wykładem, przedstawiono podejście do uproszczenia procesu tworzenia operatorów dla Kubernetesa i pokazano, jak można stworzyć własne przy minimalnym wysiłku, korzystając z operatora powłoki.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Przedstawiamy wideo z raportu (~23 minuty w języku angielskim, zauważalnie więcej informacji niż artykuł) i główny wyciąg z niego w formie tekstowej. Iść!

We Flant stale optymalizujemy i automatyzujemy wszystko. Dziś porozmawiamy o kolejnej ekscytującej koncepcji. Poznać: skrypty powłoki natywne w chmurze!

Zacznijmy jednak od kontekstu, w którym to wszystko się dzieje: Kubernetes.

Kubernetes API i kontrolery

API w Kubernetesie można przedstawić jako rodzaj serwera plików z katalogami dla każdego typu obiektu. Obiekty (zasoby) na tym serwerze są reprezentowane przez pliki YAML. Dodatkowo serwer posiada podstawowe API, które pozwala na wykonanie trzech rzeczy:

  • odbierać zasób według rodzaju i nazwy;
  • reszta zasób (w tym przypadku serwer przechowuje tylko „poprawne” obiekty - wszystkie błędnie utworzone lub przeznaczone do innych katalogów są odrzucane);
  • tor dla zasobu (w tym przypadku użytkownik od razu otrzymuje jego aktualną/zaktualizowaną wersję).

Kubernetes pełni zatem rolę swego rodzaju serwera plików (dla manifestów YAML) z trzema podstawowymi metodami (tak, właściwie są jeszcze inne, ale na razie je pominiemy).

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Problem polega na tym, że serwer może przechowywać jedynie informacje. Aby to działało, potrzebujesz kontroler - druga najważniejsza i fundamentalna koncepcja w świecie Kubernetesa.

Istnieją dwa główne typy kontrolerów. Pierwszy pobiera informacje z Kubernetesa, przetwarza je według zagnieżdżonej logiki i zwraca do K8s. Drugi pobiera informacje z Kubernetesa, ale w odróżnieniu od pierwszego typu zmienia stan niektórych zasobów zewnętrznych.

Przyjrzyjmy się bliżej procesowi tworzenia Deploymentu w Kubernetesie:

  • Kontroler wdrażania (zawarty w pakiecie kube-controller-manager) otrzymuje informacje o wdrożeniu i tworzy ReplicaSet.
  • ReplicaSet tworzy dwie repliki (dwa zasobniki) na podstawie tych informacji, ale te zasobniki nie są jeszcze zaplanowane.
  • Harmonogram planuje pody i dodaje informacje o węzłach do ich YAML.
  • Kubelets wprowadzają zmiany w zasobie zewnętrznym (powiedzmy Docker).

Następnie całą sekwencję powtarza się w odwrotnej kolejności: kubelet sprawdza kontenery, oblicza stan kapsuły i odsyła ją. Kontroler ReplicaSet odbiera status i aktualizuje stan zestawu replik. To samo dzieje się z kontrolerem wdrażania, a użytkownik w końcu otrzymuje zaktualizowany (bieżący) status.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Operator powłoki

Okazuje się, że Kubernetes opiera się na wspólnej pracy różnych kontrolerów (operatorzy Kubernetes są także kontrolerami). Powstaje pytanie, jak przy minimalnym wysiłku stworzyć własnego operatora? I tu z pomocą przychodzi ten, który opracowaliśmy operator powłoki. Umożliwia administratorom systemu tworzenie własnych zestawień przy użyciu znanych metod.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Prosty przykład: kopiowanie sekretów

Spójrzmy na prosty przykład.

Załóżmy, że mamy klaster Kubernetes. Ma przestrzeń nazw default z jakimś Sekretem mysecret. Ponadto w klastrze znajdują się inne przestrzenie nazw. Do niektórych z nich dołączona jest specjalna etykieta. Naszym celem jest skopiowanie Secret do przestrzeni nazw z etykietą.

Zadanie komplikuje fakt, że w klastrze mogą pojawić się nowe przestrzenie nazw, a część z nich może mieć tę etykietę. Z drugiej strony, gdy etykieta zostanie usunięta, należy również usunąć Sekret. Oprócz tego sam Sekret również może się zmienić: w tym przypadku nowy Sekret należy skopiować do wszystkich przestrzeni nazw z etykietami. Jeśli Sekret zostanie przypadkowo usunięty w dowolnej przestrzeni nazw, nasz operator powinien go natychmiast przywrócić.

Teraz, gdy zadanie zostało sformułowane, czas przystąpić do jego realizacji za pomocą operatora powłoki. Ale najpierw warto powiedzieć kilka słów o samym operatorze powłoki.

Jak działa operator powłoki

Podobnie jak inne obciążenia w Kubernetes, operator powłoki działa we własnym zasobniku. W tym zasobniku w katalogu /hooks przechowywane są pliki wykonywalne. Mogą to być skrypty w języku Bash, Python, Ruby itp. Takie pliki wykonywalne nazywamy hakami (haczyki).

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Operator powłoki subskrybuje zdarzenia Kubernetes i uruchamia te hooki w odpowiedzi na potrzebne nam zdarzenia.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Skąd operator powłoki wie, który hak uruchomić i kiedy? Chodzi o to, że każdy hak ma dwa etapy. Podczas uruchamiania operator powłoki uruchamia wszystkie zaczepy z argumentem --config To jest etap konfiguracji. A potem haki są uruchamiane w normalny sposób - w odpowiedzi na zdarzenia, do których są dołączone. W tym drugim przypadku hak otrzymuje kontekst wiązania (wiążący kontekst) - dane w formacie JSON, o których porozmawiamy bardziej szczegółowo poniżej.

Tworzenie operatora w Bashu

Teraz jesteśmy gotowi do realizacji. W tym celu musimy napisać dwie funkcje (swoją drogą polecamy Biblioteka biblioteka_shell, co znacznie ułatwia pisanie hooków w Bashu):

  • pierwszy jest potrzebny na etapie konfiguracji – wyświetla kontekst wiązania;
  • druga zawiera główną logikę haka.

#!/bin/bash

source /shell_lib.sh

function __config__() {
  cat << EOF
    configVersion: v1
    # BINDING CONFIGURATION
EOF
}

function __main__() {
  # THE LOGIC
}

hook::run "$@"

Następnym krokiem jest podjęcie decyzji, jakich obiektów potrzebujemy. W naszym przypadku musimy śledzić:

  • tajemnica źródłowa zmian;
  • wszystkie przestrzenie nazw w klastrze, abyś wiedział, które z nich mają dołączoną etykietę;
  • docelowe klucze tajne, aby upewnić się, że wszystkie są zsynchronizowane z kluczem źródłowym.

Subskrybuj tajne źródło

Konfiguracja powiązania dla niego jest dość prosta. Informujemy, że interesuje nas Secret nazwą mysecret w przestrzeni nazw default:

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

function __config__() {
  cat << EOF
    configVersion: v1
    kubernetes:
    - name: src_secret
      apiVersion: v1
      kind: Secret
      nameSelector:
        matchNames:
        - mysecret
      namespace:
        nameSelector:
          matchNames: ["default"]
      group: main
EOF

W rezultacie hak zostanie uruchomiony, gdy zmieni się sekret źródłowy (src_secret) i otrzymać następujący wiążący kontekst:

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Jak widać zawiera nazwę i cały obiekt.

Śledzenie przestrzeni nazw

Teraz musisz subskrybować przestrzenie nazw. W tym celu określamy następującą konfigurację powiązania:

- name: namespaces
  group: main
  apiVersion: v1
  kind: Namespace
  jqFilter: |
    {
      namespace: .metadata.name,
      hasLabel: (
       .metadata.labels // {} |  
         contains({"secret": "yes"})
      )
    }
  group: main
  keepFullObjectsInMemory: false

Jak widać w konfiguracji pojawiło się nowe pole z nazwą Filtr jq. Jak sama nazwa wskazuje, jqFilter odfiltrowuje wszystkie niepotrzebne informacje i tworzy nowy obiekt JSON z interesującymi nas polami. Hak o podobnej konfiguracji otrzyma następujący kontekst wiązania:

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Zawiera tablicę filterResults dla każdej przestrzeni nazw w klastrze. Zmienna logiczna hasLabel wskazuje, czy etykieta jest dołączona do danej przestrzeni nazw. Selektor keepFullObjectsInMemory: false wskazuje, że nie ma potrzeby przechowywania w pamięci całych obiektów.

Śledzenie sekretów celu

Subskrybujemy wszystkie Sekrety, które mają określoną adnotację managed-secret: "yes" (to jest nasz cel dst_secrets):

- name: dst_secrets
  apiVersion: v1
  kind: Secret
  labelSelector:
    matchLabels:
      managed-secret: "yes"
  jqFilter: |
    {
      "namespace":
        .metadata.namespace,
      "resourceVersion":
        .metadata.annotations.resourceVersion
    }
  group: main
  keepFullObjectsInMemory: false

W tym przypadku jqFilter filtruje wszystkie informacje z wyjątkiem przestrzeni nazw i parametrów resourceVersion. Ostatni parametr został przekazany do adnotacji podczas tworzenia sekretu: pozwala on porównywać wersje sekretów i na bieżąco je aktualizować.

Hak skonfigurowany w ten sposób po uruchomieniu otrzyma trzy konteksty powiązania opisane powyżej. Można je traktować jako swego rodzaju migawkę (migawka) klaster.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Na podstawie tych wszystkich informacji można opracować podstawowy algorytm. Iteruje po wszystkich przestrzeniach nazw i:

  • jeśli hasLabel sprawy true dla bieżącej przestrzeni nazw:
    • porównuje sekret globalny z sekretem lokalnym:
      • jeśli są takie same, to nic nie robi;
      • jeśli się różnią - wykonuje kubectl replace lub create;
  • jeśli hasLabel sprawy false dla bieżącej przestrzeni nazw:
    • upewnia się, że Secret nie znajduje się w podanej przestrzeni nazw:
      • jeśli obecny jest lokalny sekret, usuń go za pomocą kubectl delete;
      • jeśli lokalny sekret nie zostanie wykryty, nie robi nic.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Implementacja algorytmu w Bashu możesz pobrać w naszym repozytoria z przykładami.

W ten sposób udało nam się stworzyć prosty kontroler Kubernetes przy użyciu 35 linii konfiguracji YAML i mniej więcej tej samej ilości kodu Bash! Zadaniem operatora powłoki jest połączenie ich ze sobą.

Jednak kopiowanie sekretów nie jest jedynym obszarem zastosowania narzędzia. Oto kilka kolejnych przykładów pokazujących, do czego jest zdolny.

Przykład 1: Wprowadzanie zmian w ConfigMap

Przyjrzyjmy się rozmieszczeniu składającemu się z trzech zasobników. Pody używają ConfigMap do przechowywania niektórych konfiguracji. Kiedy pody zostały uruchomione, ConfigMap był w pewnym stanie (nazwijmy to v.1). W związku z tym wszystkie pody korzystają z tej konkretnej wersji ConfigMap.

Załóżmy teraz, że ConfigMap uległ zmianie (v.2). Jednakże pody będą korzystać z poprzedniej wersji ConfigMap (v.1):

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Jak mogę ich nakłonić do przejścia na nową mapę ConfigMap (wersja 2)? Odpowiedź jest prosta: użyj szablonu. Dodajmy adnotację o sumie kontrolnej do tej sekcji template Konfiguracje wdrożeniowe:

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

W rezultacie ta suma kontrolna zostanie zarejestrowana we wszystkich podach i będzie taka sama jak w przypadku wdrożenia. Teraz wystarczy zaktualizować adnotację, gdy zmieni się ConfigMap. W tym przypadku przydaje się operator powłoki. Wszystko, co musisz zrobić, to zaprogramować hak, który zasubskrybuje ConfigMap i zaktualizuje sumę kontrolną.

Jeśli użytkownik dokona zmian w ConfigMap, operator powłoki zauważy je i ponownie obliczy sumę kontrolną. Po czym zadziała magia Kubernetesa: orkiestrator zabije kapsułę, utworzy nowy, poczeka, aż stanie się Readyi przechodzi do następnego. W rezultacie wdrożenie zostanie zsynchronizowane i przełączone na nową wersję ConfigMap.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Przykład 2: Praca z niestandardowymi definicjami zasobów

Jak wiesz, Kubernetes umożliwia tworzenie niestandardowych typów obiektów. Możesz na przykład stworzyć rodzaj MysqlDatabase. Załóżmy, że ten typ ma dwa parametry metadanych: name и namespace.

apiVersion: example.com/v1alpha1
kind: MysqlDatabase
metadata:
  name: foo
  namespace: bar

Posiadamy klaster Kubernetes z różnymi przestrzeniami nazw, w których możemy tworzyć bazy danych MySQL. W tym przypadku do śledzenia zasobów można użyć operatora powłoki MysqlDatabase, łącząc je z serwerem MySQL i synchronizując pożądane i obserwowane stany klastra.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Przykład 3: Monitorowanie sieci klastrów

Jak wiadomo, polecenie ping jest najprostszym sposobem monitorowania sieci. W tym przykładzie pokażemy, jak zaimplementować takie monitorowanie za pomocą operatora powłoki.

Przede wszystkim musisz subskrybować węzły. Operator powłoki potrzebuje nazwy i adresu IP każdego węzła. Z ich pomocą będzie pingował te węzły.

configVersion: v1
kubernetes:
- name: nodes
  apiVersion: v1
  kind: Node
  jqFilter: |
    {
      name: .metadata.name,
      ip: (
       .status.addresses[] |  
        select(.type == "InternalIP") |
        .address
      )
    }
  group: main
  keepFullObjectsInMemory: false
  executeHookOnEvent: []
schedule:
- name: every_minute
  group: main
  crontab: "* * * * *"

Parametr executeHookOnEvent: [] zapobiega uruchomieniu haka w odpowiedzi na jakiekolwiek zdarzenie (to znaczy w odpowiedzi na zmianę, dodanie, usunięcie węzłów). Jednak on będzie działać (i zaktualizuj listę węzłów) Zaplanowany - co minutę, zgodnie z zaleceniami terenowymi schedule.

Teraz pojawia się pytanie, skąd dokładnie wiemy o problemach takich jak utrata pakietów? Rzućmy okiem na kod:

function __main__() {
  for i in $(seq 0 "$(context::jq -r '(.snapshots.nodes | length) - 1')"); do
    node_name="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.name')"
    node_ip="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.ip')"
    packets_lost=0
    if ! ping -c 1 "$node_ip" -t 1 ; then
      packets_lost=1
    fi
    cat >> "$METRICS_PATH" <<END
      {
        "name": "node_packets_lost",
        "add": $packets_lost,
        "labels": {
          "node": "$node_name"
        }
      }
END
  done
}

Iterujemy listę węzłów, uzyskujemy ich nazwy i adresy IP, pingujemy je i wysyłamy wyniki do Prometheusa. Operator powłoki może eksportować metryki do Prometheusa, zapisując je do pliku zlokalizowanego zgodnie ze ścieżką określoną w zmiennej środowiskowej $METRICS_PATH.

Tutaj tak możesz stworzyć operatora do prostego monitorowania sieci w klastrze.

Mechanizm kolejkowy

Artykuł ten byłby niekompletny bez opisu innego ważnego mechanizmu wbudowanego w operatora powłoki. Wyobraź sobie, że wykonuje pewien rodzaj haka w odpowiedzi na zdarzenie w klastrze.

  • Co się stanie, jeśli w tym samym czasie coś wydarzy się w klastrze? jeszcze jeden wydarzenie?
  • Czy operator powłoki uruchomi kolejną instancję haka?
  • A co jeśli, powiedzmy, w klastrze wydarzy się pięć zdarzeń jednocześnie?
  • Czy operator powłoki będzie przetwarzał je równolegle?
  • A co ze zużytymi zasobami, takimi jak pamięć i procesor?

Na szczęście operator powłoki ma wbudowany mechanizm kolejkowania. Wszystkie zdarzenia są umieszczane w kolejce i przetwarzane sekwencyjnie.

Zilustrujmy to przykładami. Powiedzmy, że mamy dwa haczyki. Pierwsze zdarzenie trafia do pierwszego haka. Po zakończeniu przetwarzania kolejka przesuwa się do przodu. Kolejne trzy zdarzenia są przekierowywane na drugi hak - są usuwane z kolejki i wprowadzane do niej w „pakiecie”. To jest hook odbiera tablicę zdarzeń — lub, ściślej, szereg wiążących kontekstów.

Również te wydarzenia można połączyć w jedno duże. Odpowiada za to parametr group w konfiguracji wiążącej.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Możesz utworzyć dowolną liczbę kolejek/hoków i ich różne kombinacje. Na przykład jedna kolejka może współpracować z dwoma hakami i odwrotnie.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Wystarczy odpowiednio skonfigurować pole queue w konfiguracji wiążącej. Jeśli nazwa kolejki nie zostanie określona, ​​hak zostanie uruchomiony w kolejce domyślnej (default). Ten mechanizm kolejkowania pozwala całkowicie rozwiązać wszystkie problemy związane z zarządzaniem zasobami podczas pracy z hakami.

wniosek

Wyjaśniliśmy, czym jest operator powłoki, pokazaliśmy, jak można go wykorzystać do szybkiego i łatwego tworzenia operatorów Kubernetesa oraz podaliśmy kilka przykładów jego użycia.

Szczegółowe informacje na temat operatora powłoki, a także krótki poradnik, jak z niego korzystać, są dostępne w odpowiednim dziale repozytoria na GitHubie. W razie pytań nie wahaj się z nami skontaktować: możesz omówić je w specjalnym wydaniu Grupa telegramów (w języku rosyjskim) lub w to forum (w języku angielskim).

A jeśli Ci się podobało, zawsze cieszymy się, gdy widzimy nowe numery/PR/gwiazdy na GitHubie, gdzie, przy okazji, możesz znaleźć inne ciekawe projekty. Wśród nich warto wyróżnić operator dodatków, który jest starszym bratem operatora powłoki. To narzędzie wykorzystuje wykresy Helm do instalowania dodatków, może dostarczać aktualizacje i monitorować różne parametry/wartości wykresów, kontroluje proces instalacji wykresów, a także może je modyfikować w odpowiedzi na zdarzenia w klastrze.

Iść? Grzmotnąć! Poznaj operatora powłoki (recenzja i relacja wideo z KubeCon EU'2020)

Filmy i slajdy

Film z występu (~23 minuty):


Prezentacja raportu:

PS

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz