Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

8 kwietnia na konferencji Święty HighLoad++ 2019w ramach sekcji „DevOps i Operations” przekazany został raport „Rozszerzanie i uzupełnianie Kubernetes”, w tworzeniu którego uczestniczyło trzech pracowników firmy Flant. Opowiadamy w nim o licznych sytuacjach, w których chcieliśmy rozszerzyć i uzupełnić możliwości Kubernetesa, ale na które nie znaleźliśmy gotowego i prostego rozwiązania. Mamy niezbędne rozwiązania w postaci projektów Open Source i im także poświęcone jest to wystąpienie.

Tradycyjnie mamy przyjemność zaprezentować wideo z raportu (50 minut, znacznie więcej informacji niż artykuł) i główne streszczenie w formie tekstowej. Iść!

Rdzeń i dodatki w K8s

Kubernetes zmienia branżę i ugruntowane od dawna podejście do administracji:

  • Dzięki niemu abstrakcje, nie operujemy już koncepcjami takimi jak konfiguracja konfiguracji lub uruchamianie poleceń (Chef, Ansible...), ale korzystamy z grupowania kontenerów, usług itp.
  • Aplikacje możemy przygotowywać bez zastanawiania się nad niuansami konkretna witryna, na którym zostanie uruchomiony: bare metal, chmura jednego z dostawców itp.
  • Dzięki K8 nigdy nie byłeś bardziej dostępny najlepsze praktyki na temat organizacji infrastruktury: techniki skalowania, samonaprawy, odporności na uszkodzenia itp.

Jednak oczywiście nie wszystko jest takie gładkie: Kubernetes przyniósł także nowe wyzwania.

Kubernetes nie to kombajn, który rozwiązuje wszystkie problemy wszystkich użytkowników. Rdzeń Kubernetes odpowiada jedynie za zestaw minimalnych niezbędnych funkcji, które są obecne w każdy grupa:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Rdzeń Kubernetes definiuje podstawowy zestaw elementów podstawowych do grupowania kontenerów, zarządzania ruchem i tak dalej. Szerzej o nich pisaliśmy w raport 2 lata temu.

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Z drugiej strony K8s oferuje ogromne możliwości rozbudowy dostępnych funkcji, które pomagają zamknąć inne - konkretny — potrzeby użytkowników. Za dodatki do Kubernetes odpowiadają administratorzy klastrów, którzy muszą zainstalować i skonfigurować wszystko, co niezbędne, aby klaster był „we właściwym stanie” [aby rozwiązać ich specyficzne problemy]. Jakiego rodzaju są to dodatki? Spójrzmy na kilka przykładów.

Przykłady dodatków

Po zainstalowaniu Kubernetesa możemy się zdziwić, że networking tak niezbędny do interakcji podów zarówno w obrębie węzła, jak i pomiędzy węzłami, nie działa samodzielnie. Jądro Kubernetesa nie gwarantuje niezbędnych połączeń, zamiast tego określa sieć interfejs (CNI) w przypadku dodatków innych firm. Musimy zainstalować jeden z tych dodatków, który będzie odpowiedzialny za konfigurację sieci.

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Bliskim przykładem są rozwiązania do przechowywania danych (dysk lokalny, urządzenie blokowe sieci, Ceph...). Początkowo były w rdzeniu, ale wraz z nadejściem CSI sytuacja zmienia się na podobną do już opisanej: interfejs jest w Kubernetesie, a jego implementacja w modułach firm trzecich.

Inne przykłady obejmują:

  • Ingres-kontrolery (zobacz ich recenzję w nasz najnowszy artykuł).
  • dyrektor zarządzający:

    Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

  • Operatorzy to cała klasa dodatków (w tym wspomniany menedżer certyfikatów), definiują one prymityw(y) i kontroler(y). Logika ich działania jest ograniczona jedynie naszą wyobraźnią i pozwala zamienić gotowe elementy infrastruktury (np. DBMS) w prymitywy, z którymi znacznie łatwiej jest pracować (niż z zestawem kontenerów i ich ustawieniami). Napisano ogromną liczbę operatorów – nawet jeśli wiele z nich nie jest jeszcze gotowych do produkcji, to tylko kwestia czasu:

    Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

  • Metryka - kolejna ilustracja tego, jak Kubernetes oddzielił interfejs (Metrics API) od implementacji (dodatki innych firm, takie jak adapter Prometheus, agent klastra Datadog...).
  • dla monitorowanie i statystyki, gdzie w praktyce nie tylko są potrzebne Prometeusz i Grafana, ale także kube-state-metrics, eksporter węzłów itp.

A to nie jest pełna lista dodatków... Na przykład my w firmie Flant aktualnie instalujemy 29 dodatków (wszystkie tworzą łącznie 249 obiektów Kubernetes). Mówiąc najprościej, nie możemy wyobrazić sobie życia klastra bez dodatków.

Automatyzacja

Operatory mają za zadanie automatyzować rutynowe operacje, z którymi spotykamy się na co dzień. Oto przykłady z życia wzięte, dla których napisanie operatora byłoby doskonałym rozwiązaniem:

  1. Istnieje prywatny (tj. wymagający logowania) rejestr z obrazami dla aplikacji. Zakłada się, że każdemu podowi przypisany jest specjalny sekret umożliwiający uwierzytelnienie w rejestrze. Naszym zadaniem jest dopilnowanie, aby ten sekret znalazł się w przestrzeni nazw, aby pody mogły pobierać obrazy. Aplikacji może być wiele (każda potrzebuje sekretu) i warto regularnie aktualizować same sekrety, dzięki czemu eliminuje się możliwość ręcznego układania sekretów. Tutaj z pomocą przychodzi operator: tworzymy kontroler, który poczeka na pojawienie się przestrzeni nazw i na podstawie tego zdarzenia doda sekret do przestrzeni nazw.
  2. Domyślnie dostęp z podów do Internetu jest zabroniony. Ale czasami może to być wymagane: logiczne jest, aby mechanizm uprawnień dostępu działał prosto, bez konieczności posiadania określonych umiejętności, na przykład obecności określonej etykiety w przestrzeni nazw. Jak operator może nam tutaj pomóc? Tworzony jest kontroler, który czeka na pojawienie się etykiety w przestrzeni nazw i dodaje odpowiednią politykę dostępu do Internetu.
  3. Podobna sytuacja: załóżmy, że musimy dodać pewną rzecz skaza, jeśli ma podobną etykietę (z jakimś przedrostkiem). Działania z operatorem są oczywiste...

W każdym klastrze należy rozwiązać rutynowe zadania i prawidłowo można to zrobić za pomocą operatorów.

Podsumowując wszystkie opisane historie, doszliśmy do wniosku, że do komfortowej pracy w Kubernetesie potrzebujesz: a) zainstaluj dodatki, B) rozwijać operatorów (do rozwiązywania codziennych zadań administracyjnych).

Jak napisać oświadczenie dla Kubernetesa?

Ogólnie schemat jest prosty:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

...ale potem okazuje się, że:

  • Kubernetes API to dość nietrywialna rzecz, której opanowanie zajmuje dużo czasu;
  • programowanie też nie jest dla każdego (jako preferowany został wybrany język Go, ponieważ istnieje dla niego specjalny framework - Pakiet SDK operatora);
  • Podobnie jest z samym frameworkiem.

Podsumowując: napisać kontroler (operator) musi wydawać znaczne środki studiować materiał. Byłoby to uzasadnione w przypadku „dużych” operatorów - powiedzmy, w przypadku DBMS MySQL. Ale jeśli przypomnimy sobie przykłady opisane powyżej (odkrywanie sekretów, uzyskiwanie dostępu do strąków do Internetu...), które również chcemy zrobić poprawnie, wtedy zrozumiemy, że włożony wysiłek przewyższy wynik, którego potrzebujemy teraz:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Generalnie pojawia się dylemat: wydać dużo środków i znaleźć odpowiednie narzędzie do pisania wypowiedzi, czy zrobić to w staromodny sposób (ale szybko). Aby go rozwiązać - znaleźć kompromis pomiędzy tymi skrajnościami - stworzyliśmy własny projekt: operator powłoki (zobacz także jego niedawne ogłoszenie na koncentratorze).

Operator powłoki

Jak on pracuje? Klaster zawiera kapsułę zawierającą plik binarny Go z operatorem powłoki. Obok niego znajduje się zestaw haki (więcej szczegółów na ich temat - patrz poniżej). Sam operator powłoki subskrybuje pewne wydarzenia w Kubernetes API, po wystąpieniu którego uruchamia odpowiednie hooki.

Skąd operator powłoki wie, które haki wywołać i które zdarzenia? Informacje te są przekazywane operatorowi powłoki za pomocą samych haków, a on robi to w bardzo prosty sposób.

Hak to skrypt Bash lub dowolny inny plik wykonywalny, który akceptuje pojedynczy argument --config i odpowiada za pomocą JSON. Ten ostatni określa, które obiekty go interesują i na jakie zdarzenia (w przypadku tych obiektów) należy zareagować:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Zilustruję implementację na operatorze powłoki jednego z naszych przykładów - rozkładanie sekretów dostępu do prywatnego rejestru z obrazami aplikacji. Składa się z dwóch etapów.

Przećwicz: 1. Napisz hak

Przede wszystkim w haku będziemy przetwarzać --config, wskazując, że interesują nas przestrzenie nazw, a konkretnie moment ich powstania:

[[ $1 == "--config" ]] ; then
  cat << EOF
{
  "onKubernetesEvent": [
    {
      "kind": "namespace",
      "event": ["add"]
    }
  ]
}
EOF
…

Jak wyglądałaby logika? Również całkiem proste:

…
else
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  kubectl create -n ${createdNamespace} -f - << EOF
Kind: Secret
...
EOF
fi

Pierwszym krokiem jest sprawdzenie, jaka przestrzeń nazw została utworzona, a drugim utworzenie jej przy użyciu kubectl sekret dla tej przestrzeni nazw.

Sprawdź swoją wiedzę: 2. Składanie obrazu

Pozostaje tylko przekazać utworzony hak operatorowi powłoki - jak to zrobić? Sam operator powłoki jest obrazem Dockera, więc naszym zadaniem jest dodanie haka do specjalnego katalogu na tym obrazie:

FROM flant/shell-operator:v1.0.0-beta.1
ADD my-handler.sh /hooks

Pozostaje tylko złożyć go i popchnąć:

$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

Ostatnim akcentem jest wdrożenie obrazu w klastrze. Aby to zrobić, napiszmy Rozlokowanie:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1 # 1
      serviceAccountName: my-operator              # 2

Należy zwrócić uwagę na dwa punkty:

  1. wskazanie nowo utworzonego obrazu;
  2. Jest to komponent systemu, który potrzebuje (przynajmniej) uprawnień do subskrybowania zdarzeń w Kubernetesie i przydzielania sekretów do przestrzeni nazw, dlatego tworzymy ServiceAccount (i zestaw reguł) dla hooka.

Wynik - rozwiązaliśmy nasz problem krewni dla Kubernetes w sposób tworzący operator rozkładający tajemnice.

Inne funkcje operatora powłoki

Aby ograniczyć obiekty wybranego typu, z którymi będzie współpracował hak, można je filtrować, wybierając według określonych etykiet (lub używając matchExpressions):

"onKubernetesEvent": [
  {
    "selector": {
      "matchLabels": {
        "foo": "bar",
       },
       "matchExpressions": [
         {
           "key": "allow",
           "operation": "In",
           "values": ["wan", "warehouse"],
         },
       ],
     }
     …
  }
]

Pod warunkiem, że mechanizm deduplikacji, co - za pomocą filtra jq - pozwala na konwersję dużych obiektów JSON na małe, gdzie pozostają tylko te parametry, które chcemy monitorować pod kątem zmian.

Kiedy wywoływany jest hak, operator powłoki przekazuje go dane obiektu, które można wykorzystać na każdą potrzebę.

Zdarzenia wyzwalające przechwyty nie ograniczają się do zdarzeń Kubernetes: operator powłoki zapewnia obsługę wywoływanie haków według czasu (podobnie jak crontab w tradycyjnym harmonogramie), a także specjalne wydarzenie na starcie. Wszystkie te zdarzenia można połączyć i przypisać do tego samego haka.

I jeszcze dwie funkcje operatora powłoki:

  1. To działa asynchronicznie. Ponieważ odebrane zostało zdarzenie Kubernetes (takie jak utworzenie obiektu), w klastrze mogły wystąpić inne zdarzenia (takie jak usunięcie tego samego obiektu), a haki muszą to uwzględnić. Jeśli hak został wykonany z błędem, domyślnie tak się stanie przypomnienie sobie czegoś aż do pomyślnego zakończenia (to zachowanie można zmienić).
  2. Eksportuje metryka dla Prometheusa, dzięki któremu możesz sprawdzić, czy operator powłoki działa, sprawdź liczbę błędów dla każdego haka i bieżący rozmiar kolejki.

Podsumowując tę ​​część raportu:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Instalowanie dodatków

Dla komfortowej pracy z Kubernetesem wspomniano także o konieczności instalowania dodatków. Opowiem Ci o tym na przykładzie drogi naszej firmy do tego, jak robimy to obecnie.

Współpracę z Kubernetesem rozpoczęliśmy od kilku klastrów, do których jedynym dodatkiem był Ingress. Należało go zainstalować inaczej w każdym klastrze i stworzyliśmy kilka konfiguracji YAML dla różnych środowisk: bare metal, AWS...

Im więcej klastrów, tym więcej konfiguracji. Dodatkowo udoskonaliliśmy same te konfiguracje, w efekcie czego stały się dość heterogeniczne:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Aby wszystko uporządkować zaczęliśmy od skryptu (install-ingress.sh), który przyjął jako argument typ klastra, w którym będziemy wdrażać, wygenerował niezbędną konfigurację YAML i wdrożył ją do Kubernetes.

W skrócie nasza dalsza droga i związane z nią rozumowanie były następujące:

  • do pracy z konfiguracjami YAML wymagany jest silnik szablonów (na pierwszych etapach jest to prosty sed);
  • wraz ze wzrostem ilości klastrów pojawiła się potrzeba automatycznej aktualizacji (najwcześniejsze rozwiązanie polegało na umieszczeniu skryptu w Git, zaktualizowaniu go za pomocą crona i uruchomieniu);
  • podobny skrypt był wymagany dla Prometeusza (install-prometheus.sh), jednak wyróżnia się tym, że wymaga znacznie większej ilości danych wejściowych i ich przechowywania (w dobrym tego słowa znaczeniu – scentralizowanego i w klastrze), a część danych (hasła) mogłaby być generowana automatycznie:

    Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

  • ryzyko wprowadzenia czegoś błędnego do coraz większej liczby klastrów stale rosło, dlatego zdaliśmy sobie sprawę, że instalatorzy (czyli dwa skrypty: dla Ingress i Prometheus) potrzebna była staging (kilka gałęzi w Git, kilka cronów do aktualizacji w odpowiednich: stabilnych lub testowych klastrach);
  • с kubectl apply praca z nim stała się trudna, ponieważ nie jest deklaratywna i może jedynie tworzyć obiekty, ale nie podejmować decyzji o ich statusie/usuwaniu;
  • Brakowało nam niektórych funkcji, których w ogóle wtedy nie zaimplementowaliśmy:
    • pełna kontrola nad wynikiem aktualizacji klastra,
    • automatyczne określanie niektórych parametrów (wejście dla skryptów instalacyjnych) na podstawie danych, które można pozyskać z klastra (discovery),
    • jego logiczny rozwój w formie ciągłego odkrywania.

Całe to zgromadzone doświadczenie wdrożyliśmy w ramach naszego innego projektu - operator dodatków.

Operator dodatków

Opiera się na wspomnianym już operatorze powłoki. Cały system wygląda następująco:

Do haków operatora powłoki dodano:

  • przechowywanie wartości,
  • Wykres steru,
  • składnik, który monitoruje magazyn wartości i - w przypadku jakichkolwiek zmian - prosi Helma o ponowne przerzucenie mapy.

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Tym samym możemy zareagować na zdarzenie w Kubernetesie, uruchomić hak i z tego haka możemy dokonać zmian w magazynie, po czym wykres zostanie pobrany ponownie. Na powstałym diagramie rozdzielamy zbiór haków i wykres na jeden element, który nazywamy moduł:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Modułów może być wiele i dodajemy do nich globalne hooki, globalny magazyn wartości oraz komponent monitorujący ten globalny magazyn.

Teraz, gdy coś dzieje się w Kubernetesie, możemy na to zareagować za pomocą globalnego haka i zmienić coś w sklepie globalnym. Ta zmiana zostanie zauważona i spowoduje wdrożenie wszystkich modułów w klastrze:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Ten schemat spełnia wszystkie wymagania dotyczące instalowania dodatków, które podano powyżej:

  • Helm jest odpowiedzialny za szablonowanie i deklaratywność.
  • Kwestię automatycznej aktualizacji rozwiązano za pomocą globalnego haka, który zgodnie z harmonogramem trafia do rejestru i jeśli zobaczy tam nowy obraz systemu, wdraża go (tj. „sam”).
  • Przechowywanie ustawień w klastrze realizowane jest przy użyciu Mapa konfiguracji, który zawiera podstawowe dane magazynów (przy uruchomieniu są one ładowane do magazynów).
  • Problemy z generowaniem haseł, odkrywaniem i ciągłym odkrywaniem zostały rozwiązane za pomocą haków.
  • Staging odbywa się dzięki tagom, które Docker obsługuje od razu po wyjęciu z pudełka.
  • Wynik jest monitorowany za pomocą wskaźników, dzięki którym możemy zrozumieć status.

Cały ten system jest zaimplementowany w postaci pojedynczego pliku binarnego w Go, który nazywa się operatorem addon. Dzięki temu diagram wygląda na prostszy:

Rozszerzanie i uzupełnianie Kubernetesa (recenzja i relacja wideo)

Głównym elementem tego diagramu jest zestaw modułów (zaznaczone na szaro poniżej). Teraz przy odrobinie wysiłku możemy napisać moduł dla wymaganego dodatku i mieć pewność, że zostanie on zainstalowany w każdym klastrze, będzie aktualizowany i reagował na potrzebne mu zdarzenia w klastrze.

Używa „Flant”. operator dodatków w ponad 70 klastrach Kubernetes. Aktualny stan - wersja alfa. Teraz przygotowujemy dokumentację do wydania wersji beta, ale na razie w repozytorium dostępne przykłady, na podstawie którego możesz stworzyć własny dodatek.

Gdzie mogę zdobyć moduły dla operatora dodatku? Wydawnictwo naszej biblioteki to dla nas kolejny etap, planujemy to zrobić latem.

Filmy i slajdy

Film z występu (~50 minut):

Prezentacja raportu:

PS

Inne relacje na naszym blogu:

Mogą Cię zainteresować także następujące publikacje:

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

Dodaj komentarz