Idea operatora powłoki jest dość prosta: subskrybuj zdarzenia z obiektów Kubernetesa, a po odebraniu tych zdarzeń uruchom zewnętrzny program, dostarczając mu informacji o zdarzeniu:
Potrzeba tego pojawiła się, gdy w trakcie działania klastrów zaczęły pojawiać się drobne zadania, które bardzo chcieliśmy w odpowiedni sposób zautomatyzować. Wszystkie te drobne zadania rozwiązano za pomocą prostych skryptów bashowych, chociaż, jak wiadomo, operatory lepiej pisać w Golangu. Oczywiście inwestowanie w pełnowymiarowy rozwój operatora dla każdego tak małego zadania byłoby nieefektywne.
Operator w 15 minut
Przyjrzyjmy się przykładowi tego, co można zautomatyzować w klastrze Kubernetes i jak operator powłoki może w tym pomóc. Przykładem może być następujący: replikacja klucza tajnego w celu uzyskania dostępu do rejestru dokera.
Pody korzystające z obrazów z rejestru prywatnego muszą zawierać w swoim manifeście link do sekretu z danymi umożliwiającymi dostęp do rejestru. Ten sekret należy utworzyć w każdej przestrzeni nazw przed utworzeniem zasobników. Można to zrobić ręcznie, ale jeśli skonfigurujemy środowiska dynamiczne, to przestrzeni nazw dla jednej aplikacji będzie sporo. A jeśli nie ma też 2-3 aplikacji... liczba sekretów staje się bardzo duża. I jeszcze jedno odnośnie sekretów: chciałbym od czasu do czasu zmienić klucz dostępu do rejestru. W końcu, operacje ręczne jako rozwiązanie całkowicie nieskuteczne — musimy zautomatyzować tworzenie i aktualizację sekretów.
Prosta automatyzacja
Napiszmy skrypt powłoki, który będzie uruchamiany co N sekund i sprawdza przestrzenie nazw pod kątem obecności sekretu, a jeśli sekretu nie ma, to zostanie on utworzony. Zaletą tego rozwiązania jest to, że wygląda jak skrypt powłoki w cron - klasyczne i zrozumiałe dla każdego podejście. Minusem jest to, że w przerwie między jego uruchomieniami może zostać utworzona nowa przestrzeń nazw i przez jakiś czas pozostanie ona bez tajemnicy, co będzie prowadzić do błędów w uruchamianiu podów.
Automatyka z operatorem powłoki
Aby nasz skrypt działał poprawnie, klasyczne uruchomienie crona należy zastąpić uruchomieniem po dodaniu przestrzeni nazw: w tym przypadku możesz utworzyć sekret przed jego użyciem. Zobaczmy, jak to zaimplementować za pomocą operatora powłoki.
Najpierw spójrzmy na skrypt. Skrypty w terminologii operatora powłoki nazywane są hakami. Każdy hak, gdy jest uruchamiany z flagą --config informuje operatora powłoki o jego powiązaniach, tj. na jakich wydarzeniach powinien zostać uruchomiony. W naszym przypadku skorzystamy onKubernetesEvent:
#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
"event":["add"]
}
]}
EOF
fi
Tutaj opisano, że jesteśmy zainteresowani dodaniem wydarzeń (add) obiekty typu namespace.
Teraz musisz dodać kod, który zostanie wykonany po wystąpieniu zdarzenia:
#!/bin/bash
if [[ $1 == "--config" ]] ; then
# конфигурация
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
"event":["add"]
}
]}
EOF
else
# реакция:
# узнать, какой namespace появился
createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
# создать в нём нужный секрет
kubectl create -n ${createdNamespace} -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
...
data:
...
EOF
fi
Świetnie! W rezultacie powstał mały, piękny scenariusz. Aby go „ożywić” pozostały dwa kroki: przygotować obraz i uruchomić go w klastrze.
Przygotowanie obrazu za pomocą haka
Jeśli spojrzysz na skrypt, zobaczysz, że polecenia są używane kubectl и jq. Oznacza to, że obraz musi zawierać następujące elementy: nasz hak, operator powłoki, który będzie monitorował zdarzenia i uruchamiał hak, oraz polecenia używane przez hak (kubectl i jq). Hub.docker.com ma już gotowy obraz, w którym spakowany jest operator powłoki, kubectl i jq. Pozostaje tylko dodać prosty haczyk Dockerfile:
Przyjrzyjmy się hakowi jeszcze raz i tym razem napiszmy jakie akcje i z jakimi obiektami wykonuje w klastrze:
subskrybuje zdarzenia tworzenia przestrzeni nazw;
tworzy sekret w przestrzeniach nazw innych niż ta, w której jest uruchamiany.
Okazuje się, że pod, w którym zostanie uruchomiony nasz obraz, musi mieć uprawnienia do wykonywania tych czynności. Można tego dokonać tworząc własne konto ServiceAccount. Zezwolenie musi zostać wydane w formie ClusterRole i ClusterRoleBinding, ponieważ interesują nas obiekty z całej gromady.
Ostateczny opis w YAML będzie wyglądał mniej więcej tak:
To wszystko: operator powłoki uruchomi się, zasubskrybuje zdarzenia tworzenia przestrzeni nazw i uruchomi hak, jeśli zajdzie taka potrzeba.
Tak więc, prosty skrypt powłoki zamieniony w prawdziwego operatora dla Kubernetesa i działa w ramach klastra. A wszystko to bez skomplikowanego procesu rozwijania operatorów w Golang:
Jest jeszcze jedna ilustracja na ten temat...
Jego znaczenie zdradzimy szerzej w jednej z kolejnych publikacji.
filtracja
Śledzenie obiektów jest dobre, ale często trzeba na nie reagować zmianę niektórych właściwości obiektuna przykład, aby zmienić liczbę replik we wdrożeniu lub zmienić etykiety obiektów.
Po nadejściu zdarzenia operator powłoki otrzymuje manifest JSON obiektu. Możemy wybrać właściwości, które nas interesują w tym JSON-ie i uruchomić hak tylko kiedy się zmieniają. Jest na to pole jqFilter, gdzie należy określić wyrażenie jq, które zostanie zastosowane do manifestu JSON.
Na przykład, aby odpowiedzieć na zmiany w etykietach obiektów wdrożenia, należy przefiltrować pole labels poza polem metadata. Konfiguracja będzie następująca:
Mała dygresja: tak, obsługuje operatora powłoki uruchamianie skryptów w stylu crontab. Więcej szczegółów można znaleźć w dokumentacja.
Aby rozróżnić, dlaczego hak został uruchomiony, operator powłoki tworzy plik tymczasowy i przekazuje ścieżkę do niego w zmiennej do haka BINDING_CONTEXT_TYPE. Plik zawiera opis JSON powodu uruchomienia haka. Na przykład co 10 minut będzie uruchamiany hak z następującą zawartością:
Zawartość pól można zrozumieć na podstawie ich nazw, a więcej szczegółów można odczytać dokumentacja. Przykład pobrania nazwy zasobu z pola resourceName użycie jq zostało już pokazane w haku replikującym sekrety:
jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH
W podobny sposób możesz uzyskać inne pola.
Co dalej?
W repozytorium projektu w /przykładowe katalogi, istnieją przykłady haków, które są gotowe do uruchomienia w klastrze. Pisząc własne hooki, możesz wykorzystać je jako bazę.
Istnieje wsparcie dla zbierania metryk za pomocą Prometheusa - dostępne metryki opisano w sekcji METRYKA.
Jak można się domyślić, operator powłoki jest napisany w Go i rozpowszechniany na licencji Open Source (Apache 2.0). Będziemy wdzięczni za każdą pomoc rozwojową projekt na GitHubie: i gwiazdki, problemy i żądania ściągnięcia.
Podnosząc zasłonę tajemnicy, poinformujemy Cię również, że Shell-operator jest mały część naszego systemu, która może na bieżąco aktualizować dodatki zainstalowane w klastrze Kubernetes i wykonywać różne automatyczne akcje. Przeczytaj więcej o tym systemie powiedział dosłownie w poniedziałek na targach HighLoad++ 2019 w St. Petersburgu – wkrótce opublikujemy wideo i transkrypcję tej relacji.
Mamy plan otwarcia reszty tego systemu: operatora dodatków oraz naszej kolekcji haków i modułów. Nawiasem mówiąc, operator dodatków już jest dostępne na github, ale dokumentacja do niego jest wciąż w drodze. Wydanie kolekcji modułów planowane jest na lato.