
Cześć! Gałąź funkcjonalna (znana jako podgląd wdrożenia, aplikacja przeglądowa) — to sytuacja, w której wdrażany jest nie tylko główny branch, ale także każdy pull request pod unikalnym URL. Można sprawdzić, czy kod działa w środowisku produkcyjnym, a funkcję można zaprezentować innym programistom lub produktowcom. Gdy pracujesz w pull request’cie, każdy nowy commit aktualizuje wdrożenie dla starego kodu, a nowe wdrożenie dla nowego kodu jest publikowane. Problemy mogą się pojawić, gdy zintegrowałeś pull request z głównym branchem. Gałąź funkcjonalna nie jest już potrzebna, ale zasoby Kubernetes wciąż pozostają w klastrze.
Więcej o gałęziach funkcjonalnych
Jednym z podejść do tworzenia gałęzi funkcjonalnych w Kubernetes jest użycie namespace’ów. W skrócie, konfiguracje produkcyjne wyglądają 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
...Dla gałęzi funkcjonalnej tworzy się namespace z jej identyfikatorem (na przykład numer pull request’a) i jakimś prefiksem/podpisem (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
...Ogólnie rzecz biorąc, napisałem Operator Kubernetes (aplikacja, która ma dostęp do zasobów klastra), . Usuwa namespace’y związane ze starymi gałęziami funkcjonalnymi. W Kubernetes, jeśli usuniesz namespace, inne zasoby w tym namespace również są automatycznie usuwane.
$ kubectl get pods --all-namespaces | grep -e "-pr-"
NAMESPACE ... AGE
habr-back-end-pr-264 ... 4d8h
habr-back-end-pr-265 ... 5d7hO tym, jak wdrożyć gałęzie funkcjonalne w klastrze, można przeczytać i .
Motywacja
Przyjrzyjmy się typowemu cyklowi życia pull request’a z ciągłą integracją (ciągła integracja):
- Wysyłamy nowy commit do gałęzi.
- Podczas budowy uruchamiane są linty i/lub testy.
- Na bieżąco tworzone są konfiguracje Kubernetes dla pull request’a (na przykład numer jest podstawiany w gotowy szablon).
- Za pomocą kubectl apply konfiguracje trafiają do klastra (wdrożenie).
- Pull request jest scalany z gałęzią główną.
Podczas pracy w pull request’cie każdy nowy commit aktualizuje wdrożenie dla starego kodu, a nowe wdrożenie dla nowego kodu jest publikowane. Jednak gdy pull request jest scalany z gałęzią główną, będzie budowany tylko główny branch. W efekcie możemy zapomnieć o pull request’cie, podczas gdy jego zasoby Kubernetes wciąż pozostają w klastrze.
Jak używać
Zainstaluj projekt poniższą komendą:
$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/configs/production.ymlUtwórz plik z następującą zawartością i zainstaluj za pomocą kubectl apply -f:
apiVersion: feature-branch.dmytrostriletskyi.com/v1
kind: StaleFeatureBranch
metadata:
name: stale-feature-branch
spec:
namespaceSubstring: -pr-
afterDaysWithoutDeploy: 3Parametr namespaceSubstring jest potrzebny, aby filtrować namespace'y dla pull requestów z innych namespace'y. Na przykład, jeśli w klastrze są następujące namespace'y: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, wtedy kandydatami do usunięcia będą habr-back-end-pr-17, habr-back-end-pr-33.
Parametr afterDaysWithoutDeploy jest potrzebny, aby usuwać stare namespace'y. Na przykład, jeśli namespace został utworzony 3 dni 1 godzina temu, a w parametrze wskazano 3 dni, ten namespace zostanie usunięty. Działa to również w drugą stronę, jeśli namespace został utworzony 2 dni 23 godziny temu, a w parametrze wskazano 3 dni, ten namespace nie zostanie usunięty.
Jest jeszcze jeden parametr, który odpowiada za to, jak często skanować wszystkie namespace'y i sprawdzać dni bez deploy’a — checkEveryMinutes. Domyślnie wynosi 30 minut.
Jak to działa
W praktyce, potrzebne będzie:
- do pracy w izolowanym środowisku.
- uruchomi lokalnie klaster Kubernetes.
- — interfejs wiersza poleceń do zarządzania klastrem.
Uruchamiamy lokalnie klaster Kubernetes:
$ minikube start --vm-driver=docker
minikube v1.11.0 na Darwin 10.15.5
Używając sterownika docker opartego na istniejącym profilu.
Rozpoczynanie węzła kontrolnego minikube w klastrze minikube.Ustawiamy kubectl użyj lokalnego klastra domyślnie:
$ kubectl config use-context minikube
Przełączono kontekst na "minikube".Pobieramy konfiguracje dla środowiska produkcyjnego:
$ curl https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/configs/production.yml > stale-feature-branch-production-configs.ymlPonieważ produkcyjne konfiguracje są skonfigurowane do sprawdzania starych namespace'ów, a w naszym nowo uruchomionym klastrze ich nie ma, zmienimy zmienną środowiskową IS_DEBUG na true. Przy takim ustawieniu parametr afterDaysWithoutDeploy nie jest brany pod uwagę i namespace'y nie są sprawdzane pod kątem dni bez deploy’a, tylko na obecność podciągu (-pr-).
Jeśli jesteś na Linuxa:
$ sed -i 's|false|true|g' stale-feature-branch-production-configs.ymlJeśli jesteś na macOS:
$ sed -i "" 's|false|true|g' stale-feature-branch-production-configs.ymlInstalujemy projekt:
$ kubectl apply -f stale-feature-branch-production-configs.ymlSprawdzamy, czy w klastrze pojawił się zasób StaleFeatureBranch:
$ kubectl api-resources | grep stalefeaturebranches
NAZWA ... APIGROUP ... RODZAJ
stalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranchSprawdzamy, czy w klastrze pojawił się operator:
$ kubectl get pods --namespace stale-feature-branch-operator
NAZWA ... STATUS ... WIEK
stale-feature-branch-operator-6bfbfd4df8-m7sch ... Działa ... 38sJeśli zajrzymy do jego logów, jest gotowy do przetwarzania zasobów StaleFeatureBranch:
$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Wersja operatora: 0.0.1"}
...
... "msg":"Rozpoczynam źródło zdarzeń", ... , "source":"rodzaj źródła: /, Rodzaj="}
... "msg":"Rozpoczynam kontrolera", ...}
... "msg":"Rozpoczynam pracowników", ..., "liczba pracowników":1}Instalujemy 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.ymlW konfiguracjach wskazano, aby szukać namespace’ów zawierających podciąg -pr- raz na 1 minutę.:
apiVersion: feature-branch.dmytrostriletskyi.com/v1
kind: StaleFeatureBranch
metadata:
name: stale-feature-branch
spec:
namespaceSubstring: -pr-
afterDaysWithoutDeploy: 1
checkEveryMinutes: 1Operator zareagował i jest gotowy do sprawdzenia namespace’ów:
$ 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"}Instalujemy fixtures, zawierających dwa namespace’y (project-pr-1, project-pr-2) i ich 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 createdSprawdzamy, 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ż włączyliśmy debug, namespace’y project-pr-1 i project-pr-2, co oznacza, że wszystkie pozostałe zasoby powinny zostać natychmiast usunięte niezależnie od parametru afterDaysWithoutDeploy. W logach operatora widać to:
$ 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 sprawdzimy dostępność zasobów, będą one w statusie Terminating (proces usuwania) lub już usunięte (wyjście z polecenia 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 upewnij się, że zostaną usunięte w ciągu minuty.
Alternatywy
Co można zrobić zamiast operatora działającego w klastrze? Istnieje kilka podejść, wszystkie są niedoskonałe (ich wady są subiektywne), a każdy sam decyduje, co najlepiej pasuje do konkretnego projektu:
Usunąć gałąź feature podczas budowy ciągłej integracji gałęzi master.
- Aby to zrobić, należy wiedzieć, który pull request dotyczy commit’u, który jest budowany. Ponieważ przestrzeń nazw gałęzi feature zawiera identyfikator pull request’a — jego numer lub nazwę gałęzi, identyfikator zawsze trzeba będzie podać w commit’cie.
- Budowy gałęzi master kończą się niepowodzeniem. Na przykład, masz następujące etapy: pobierz projekt, uruchom testy, zbuduj projekt, wydaj wersję, wyślij powiadomienia, oczyść gałąź feature ostatniego pull request’a. Jeśli budowa zakończy się niepowodzeniem podczas wysyłania powiadomień, będziesz musiał ręcznie usunąć wszystkie zasoby w klastrze.
- Bez odpowiedniego kontekstu, usunięcie gałęzi feature w budowie master nie jest oczywiste.
Wykorzystanie webhooków ().
- Może to nie jest twoje podejście. Na przykład w , tylko jeden rodzaj pipeline’u obsługuje możliwość przechowywania jego konfiguracji w kodzie źródłowym. Przy użyciu webhooków należy napisać własny skrypt do ich przetwarzania. Skrypt ten będzie trzeba umieścić w interfejsie Jenkins’a, co jest trudne do utrzymania.
Napisać i dodać klaster Kubernetes.
- Czas poświęcony na pisanie i utrzymanie.
- Operator już działa w podobnym stylu, jest udokumentowany i wspierany.
Dziękuję za uwagę do artykułu. .
Źródło: habr.com
