Usuwanie przestarzałej gałęzi funkcji w klastrze Kubernetes

Usuwanie przestarzałej gałęzi funkcji w klastrze Kubernetes

Hi! Oddział funkcji (inaczej wdrażanie podglądu, przeglądanie aplikacji) — wtedy wdrażana jest nie tylko gałąź główna, ale także każde żądanie ściągnięcia pod unikalny adres URL. Możesz sprawdzić, czy kod działa w środowisku produkcyjnym, funkcjonalność można pokazać innym programistom lub specjalistom ds. produktu. Podczas pracy z żądaniem ściągnięcia każde nowe wdrożenie zatwierdzenia starego kodu jest usuwane i wdrażane jest nowe wdrożenie nowego kodu. Pytania mogą pojawić się, gdy połączysz żądanie ściągnięcia z gałęzią główną. Nie potrzebujesz już gałęzi funkcji, ale zasoby Kubernetes nadal znajdują się w klastrze.

Więcej o gałęziach funkcji

Jednym ze sposobów tworzenia gałęzi funkcji w Kubernetesie jest użycie przestrzeni nazw. W skrócie konfiguracja produkcyjna wygląda następująco:

kind: Namespace
apiVersion: v1
metadata:
  name: habr-back-end
...

kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: habr-back-end
spec:
  replicas: 3
...

W przypadku gałęzi funkcji tworzona jest przestrzeń nazw z jej identyfikatorem (na przykład numerem żądania ściągnięcia) i pewnym rodzajem przedrostka/postfiksu (na przykład -pr-):

kind: Namespace
apiVersion: v1
metadata:
  name: habr-back-end-pr-17
...

kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: habr-back-end-pr-17
spec:
  replicas: 1
...

Generalnie napisałem Operator Kubernetesa (aplikacja mająca dostęp do zasobów klastra), link do projektu na Githubie. Usuwa przestrzenie nazw należące do starych gałęzi funkcji. W Kubernetes, jeśli usuniesz przestrzeń nazw, inne zasoby w tej przestrzeni nazw również zostaną automatycznie usunięte.

$ kubectl get pods --all-namespaces | grep -e "-pr-"
NAMESPACE            ... AGE
habr-back-end-pr-264 ... 4d8h
habr-back-end-pr-265 ... 5d7h

Możesz przeczytać o tym, jak wdrożyć gałęzie funkcji w klastrze tutaj и tutaj.

Motywacja

Przyjrzyjmy się typowemu cyklowi życia żądania ściągnięcia z ciągłą integracją (continuous integration):

  1. Wrzucamy nowe zatwierdzenie do gałęzi.
  2. Podczas kompilacji uruchamiane są lintery i/lub testy.
  3. Konfiguracje Kubernetes pull request są generowane na bieżąco (np. ich numer wstawiany jest do gotowego szablonu).
  4. Za pomocą kubectl Apply konfiguracje są dodawane do klastra (wdrażane).
  5. Żądanie ściągnięcia jest scalane z gałęzią główną.

Podczas pracy z żądaniem ściągnięcia każde nowe wdrożenie zatwierdzenia starego kodu jest usuwane i wdrażane jest nowe wdrożenie nowego kodu. Kiedy jednak żądanie ściągnięcia zostanie scalone z gałęzią główną, zbudowana zostanie tylko gałąź główna. W efekcie okazuje się, że o pull requestie już zapomnieliśmy, a jego zasoby Kubernetesa nadal znajdują się w klastrze.

Jak korzystać

Zainstaluj projekt za pomocą poniższego polecenia:

$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/configs/production.yml

Utwórz plik o następującej zawartości i zainstaluj za pośrednictwem kubectl apply -f:

apiVersion: feature-branch.dmytrostriletskyi.com/v1
kind: StaleFeatureBranch
metadata:
  name: stale-feature-branch
spec:
  namespaceSubstring: -pr-
  afterDaysWithoutDeploy: 3

Parametr przestrzeń nazwPodciąg potrzebne do filtrowania przestrzeni nazw dla żądań ściągnięcia z innych przestrzeni nazw. Na przykład, jeśli klaster ma następujące przestrzenie nazw: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, wówczas będą kandydaci do usunięcia habr-back-end-pr-17, habr-back-end-pr-33.

Parametr poDaysBez wdrożenia potrzebne do usunięcia starych przestrzeni nazw. Na przykład, jeśli utworzono przestrzeń nazw 3 дня 1 час z powrotem, a parametr wskazuje 3 дня, ta przestrzeń nazw zostanie usunięta. Działa to również w odwrotnym kierunku, jeśli utworzona jest przestrzeń nazw 2 дня 23 часа z powrotem, a parametr wskazuje 3 дня, ta przestrzeń nazw nie zostanie usunięta.

Jest jeszcze jeden parametr, który odpowiada za częstotliwość skanowania wszystkich przestrzeni nazw i sprawdzania dni bez wdrożenia - sprawdźCoMinuty. Domyślnie jest równa 30 минутам.

Jak to działa

W praktyce będziesz potrzebować:

  1. Doker do pracy w izolowanym środowisku.
  2. Minikube utworzy lokalnie klaster Kubernetes.
  3. kubectl — interfejs wiersza poleceń do zarządzania klastrem.

Lokalnie podnosimy klaster Kubernetes:

$ minikube start --vm-driver=docker
minikube v1.11.0 on Darwin 10.15.5
Using the docker driver based on existing profile.
Starting control plane node minikube in cluster minikube.

Sprecyzować kubectl domyślnie używaj klastra lokalnego:

$ kubectl config use-context minikube
Switched to context "minikube".

Pobierz konfiguracje dla środowiska produkcyjnego:

$ curl https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/configs/production.yml > stale-feature-branch-production-configs.yml

Ponieważ konfiguracje produkcyjne są skonfigurowane do sprawdzania starych przestrzeni nazw, a nasz nowo utworzony klaster ich nie posiada, zastąpimy zmienną środowiskową IS_DEBUG na true. Przy tej wartości parametr afterDaysWithoutDeploy nie jest brany pod uwagę, a przestrzenie nazw nie są sprawdzane przez dni bez wdrożenia, tylko pod kątem wystąpienia podciągu (-pr-).

Jeśli jesteś włączony Linux:

$ sed -i 's|false|true|g' stale-feature-branch-production-configs.yml

Jeśli jesteś włączony macOS:

$ sed -i "" 's|false|true|g' stale-feature-branch-production-configs.yml

Instalacja projektu:

$ kubectl apply -f stale-feature-branch-production-configs.yml

Sprawdzanie, czy zasób pojawił się w klastrze StaleFeatureBranch:

$ kubectl api-resources | grep stalefeaturebranches
NAME                 ... APIGROUP                             ... KIND
stalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch

Sprawdzamy, czy w klastrze pojawił się operator:

$ kubectl get pods --namespace stale-feature-branch-operator
NAME                                           ... STATUS  ... AGE
stale-feature-branch-operator-6bfbfd4df8-m7sch ... Running ... 38s

Jeśli spojrzysz na jego logi, jest on gotowy do przetwarzania zasobów StaleFeatureBranch:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Operator Version: 0.0.1"}
...
... "msg":"Starting EventSource", ... , "source":"kind source: /, Kind="}
... "msg":"Starting Controller", ...}
... "msg":"Starting workers", ..., "worker count":1}

Montujemy gotowe fixtures (gotowe konfiguracje do modelowania zasobów klastra) dla zasobu StaleFeatureBranch:

$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/fixtures/stale-feature-branch.yml

Konfiguracje wskazują na wyszukiwanie przestrzeni nazw za pomocą podłańcucha -pr- raz w 1 минуту.:

apiVersion: feature-branch.dmytrostriletskyi.com/v1
kind: StaleFeatureBranch
metadata:
  name: stale-feature-branch
spec:
  namespaceSubstring: -pr-
  afterDaysWithoutDeploy: 1 
  checkEveryMinutes: 1

Operator odpowiedział i jest gotowy do sprawdzenia przestrzeni nazw:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Stale feature branch is being processing.","namespaceSubstring":"-pr-","afterDaysWithoutDeploy":1,"checkEveryMinutes":1,"isDebug":"true"}

zainstalować fixtures, zawierający dwie przestrzenie nazw (project-pr-1, project-pr-2) i oni deployments, services, ingress, i tak dalej:

$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/fixtures/first-feature-branch.yml -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/fixtures/second-feature-branch.yml
...
namespace/project-pr-1 created
deployment.apps/project-pr-1 created
service/project-pr-1 created
horizontalpodautoscaler.autoscaling/project-pr-1 created
secret/project-pr-1 created
configmap/project-pr-1 created
ingress.extensions/project-pr-1 created
namespace/project-pr-2 created
deployment.apps/project-pr-2 created
service/project-pr-2 created
horizontalpodautoscaler.autoscaling/project-pr-2 created
secret/project-pr-2 created
configmap/project-pr-2 created
ingress.extensions/project-pr-2 created

Sprawdzamy, czy wszystkie powyższe zasoby zostały pomyślnie utworzone:

$ kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 && kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2
...
NAME                              ... READY ... STATUS  ... AGE
pod/project-pr-1-848d5fdff6-rpmzw ... 1/1   ... Running ... 67s

NAME                         ... READY ... AVAILABLE ... AGE
deployment.apps/project-pr-1 ... 1/1   ... 1         ... 67s
...

Ponieważ uwzględniliśmy debug, przestrzenie nazw project-pr-1 и project-pr-2, dlatego wszystkie inne zasoby będą musiały zostać natychmiast usunięte bez uwzględnienia parametru afterDaysWithoutDeploy. Można to zobaczyć w logach operatora:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Namespace should be deleted due to debug mode is enabled.","namespaceName":"project-pr-1"}
... "msg":"Namespace is being processing.","namespaceName":"project-pr-1","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Namespace has been deleted.","namespaceName":"project-pr-1"}
... "msg":"Namespace should be deleted due to debug mode is enabled.","namespaceName":"project-pr-2"}
... "msg":"Namespace is being processing.","namespaceName":"project-pr-2","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Namespace has been deleted.","namespaceName":"project-pr-2"}

Jeśli sprawdzisz dostępność zasobów, będą one miały status Terminating (proces usuwania) lub już usunięty (wyjście polecenia jest puste).

$ kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 && kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2
...

Możesz powtórzyć proces tworzenia fixtures kilka razy i upewnij się, że zostaną usunięte w ciągu minuty.

Alternatywy

Co można zrobić zamiast operatora pracującego w klastrze? Podejść jest kilka, wszystkie są niedoskonałe (a ich wady są subiektywne) i każdy sam decyduje, co jest najlepsze dla konkretnego projektu:

  1. Usuń gałąź funkcji podczas kompilacji ciągłej integracji gałęzi głównej.

    • Aby to zrobić, musisz wiedzieć, które żądanie ściągnięcia odnosi się do budowanego zatwierdzenia. Ponieważ przestrzeń nazw gałęzi funkcji zawiera identyfikator żądania ściągnięcia - jego numer lub nazwę gałęzi, identyfikator zawsze będzie musiał zostać określony w zatwierdzeniu.
    • Kompilacje gałęzi głównych kończą się niepowodzeniem. Na przykład masz następujące etapy: pobranie projektu, uruchomienie testów, zbudowanie projektu, wydanie wydania, wysłanie powiadomień, wyczyszczenie gałęzi funkcji z ostatniego żądania ściągnięcia. Jeśli kompilacja nie powiedzie się podczas wysyłania powiadomienia, konieczne będzie ręczne usunięcie wszystkich zasobów w klastrze.
    • Bez odpowiedniego kontekstu usunięcie gałęzi funkcji w kompilacji głównej nie jest oczywiste.

  2. Korzystanie z webhooków (przykład).

    • To może nie być Twoje podejście. Na przykład w Jenkins, tylko jeden typ potoku obsługuje możliwość zapisywania jego konfiguracji w kodzie źródłowym. Korzystając z webhooków, musisz napisać własny skrypt, aby je przetworzyć. Skrypt ten będzie musiał zostać umieszczony w interfejsie Jenkinsa, który jest trudny w utrzymaniu.

  3. Napisz cronjob i dodaj klaster Kubernetes.

    • Spędzanie czasu na pisaniu i wsparciu.
    • Operator już pracuje w podobnym stylu, jest udokumentowany i wspierany.

Dziękuję za uwagę na artykuł. Link do projektu na Githubie.

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

Dodaj komentarz