Veraltete Feature-Branch im Kubernetes-Cluster löschen

Veraltete Feature-Branch im Kubernetes-Cluster löschen

Hallo! Feature-Branch (auch bekannt als Deploy-Vorschau, Überprüfungs-App) — das ist, wenn nicht nur der Master-Zweig, sondern auch jeder Pull-Request auf einer einzigartigen URL bereitgestellt wird. Man kann testen, ob der Code in der Produktionsumgebung funktioniert, oder das Feature anderen Programmierern oder Produktmanagern zeigen. Während Sie an einem Pull-Request arbeiten, wird jedes neue Commit zu einem aktuellen Deployment des alten Codes gelöscht und ein neues Deployment für den neuen Code wird bereitgestellt. Fragen können auftreten, wenn Sie den Pull-Request in den Master-Zweig zusammengeführt haben. Der Feature-Branch ist nicht mehr erforderlich, aber die Ressourcen von Kubernetes befinden sich weiterhin im Cluster.

Weitere Informationen zu Feature-Branches

Einer der Ansätze, wie man Feature-Branches in Kubernetes erstellen kann, ist die Verwendung von Namespaces. Kurz gesagt, die Produktionskonfigurationen sehen so aus:

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

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

Für den Feature-Branch wird ein Namespace mit seiner Kennung (zum Beispiel der Nummer des Pull-Requests) und einem bestimmten Präfix/Suffix (zum Beispiel, -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
...

Insgesamt habe ich geschrieben Kubernetes Operator (eine Anwendung, die Zugriff auf die Cluster-Ressourcen hat), Link zum Projekt auf Github. Es entfernt die Namespaces, die sich auf alte Feature-Branches beziehen. In Kubernetes werden beim Löschen eines Namespaces auch andere Ressourcen in diesem Namespace automatisch gelöscht.

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

Wie man Feature-Branches in den Cluster integriert, kann man lesen hier und hier.

Motivation

Lassen Sie uns den typischen Lebenszyklus eines Pull-Requests mit kontinuierlicher Integration anschauen (kontinuierliche Integration):

  1. Wir pushen ein neues Commit in den Branch.
  2. Beim Build werden Linter und/oder Tests ausgeführt.
  3. Die Kubernetes-Konfigurationen des Pull-Requests werden dynamisch erstellt (zum Beispiel wird seine Nummer in eine bereitgestellte Vorlage eingesetzt).
  4. Die Konfigurationen gelangen mit kubectl apply in den Cluster (Deployment).
  5. Der Pull-Request wird in den Master-Zweig zusammengeführt.

Während Sie an einem Pull-Request arbeiten, wird jedes neue Commit zu einem aktuellen Deployment des alten Codes gelöscht und ein neues Deployment für den neuen Code wird bereitgestellt. Aber wenn der Pull-Request in den Master-Zweig zusammengeführt wird, wird nur der Master-Zweig gebaut. Das führt dazu, dass wir den Pull-Request bereits vergessen haben, während seine Kubernetes-Ressourcen weiterhin im Cluster vorhanden sind.

Wie man es verwendet

Installieren Sie das Projekt mit dem folgenden Befehl:

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

Erstellen Sie eine Datei mit folgendem Inhalt und installieren Sie sie über kubectl apply -f:

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

Parameter namespaceSubstring benötigt, um Namespaces für Pull Requests von anderen Namespaces zu filtern. Zum Beispiel, wenn im Cluster die folgenden Namespaces existieren: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, dann wären die Kandidaten für die Löschung habr-back-end-pr-17, habr-back-end-pr-33.

Parameter afterDaysWithoutDeploy benötigt, um alte Namespaces zu löschen. Zum Beispiel, wenn der Namespace vor 3 Tagen 1 Stunde erstellt wurde, und der Parameter angibt 3 Tage, wird dieser Namespace gelöscht. Es funktioniert auch in umgekehrter Richtung, wenn der Namespace vor 2 Tagen 23 Stunden erstellt wurde, und der Parameter angibt 3 Tage, wird dieser Namespace nicht gelöscht.

Es gibt einen weiteren Parameter, der angibt, wie oft alle Namespaces gescannt werden, um die Tage ohne Deploy zu überprüfen — checkEveryMinutes. Standardmäßig beträgt er 30 Minuten.

Die DANE-Spezifikation ist in

In der Praxis wird Folgendes benötigt:

  1. Docker um in einer isolierten Umgebung zu arbeiten.
  2. Minikube hebt den Kubernetes-Cluster lokal an.
  3. kubectl — Kommandozeileninterface zur Verwaltung des Clusters.

Heben wir den Kubernetes-Cluster lokal an:

$ minikube start --vm-driver=docker
minikube v1.11.0 auf Darwin 10.15.5
Verwendung des Docker-Treibers basierend auf dem vorhandenen Profil.
Starte Steuerungsplane-Node minikube im Cluster minikube.

Geben Sie an kubectl lokalen Cluster standardmäßig verwenden:

$ kubectl config use-context minikube
Wechsel zu Kontext "minikube".

Laden Sie Konfigurationen für die Produktionsumgebung herunter:

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

Da die Produktionskonfigurationen so eingestellt sind, dass sie alte Namespaces überprüfen, und in unserem neu angehobenen Cluster keine vorhanden sind, ersetzen wir die Umgebungsvariable IS_DEBUG auf true. Bei diesem Wert wird der Parameter afterDaysWithoutDeploy nicht berücksichtigt und die Namespaces werden nicht auf Tage ohne Deploy überprüft, sondern nur auf das Vorhandensein der Unterzeichenfolge (-pr-).

Wenn Sie auf Linux:

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

Wenn Sie auf macOS:

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

Installieren Sie das Projekt:

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

Überprüfen Sie, ob die Ressource im Cluster erschienen ist StaleFeatureBranch:

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

Überprüfen Sie, ob der Operator im Cluster erschienen ist:

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

Wenn Sie in seine Protokolle schauen, ist es bereit, Ressourcen zu verarbeiten StaleFeatureBranch:

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

Installieren Sie die fertigen fixtures (fertige Konfigurationen zur Modellierung von Clusterressourcen) für die Ressource StaleFeatureBranch:

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

In den Konfigurationen ist angegeben, nach Namespaces mit dem Substring zu suchen -pr- alle 1 Minute.:

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

Der Operator hat reagiert und ist bereit, die Namespaces zu überprüfen:

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

Installation fixtures, die zwei Namespaces enthalten (project-pr-1, project-pr-2) und deren deployments, Dienste, ingress, und so weiter:

$ 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 erstellt
deployment.apps/project-pr-1 erstellt
service/project-pr-1 erstellt
horizontalpodautoscaler.autoscaling/project-pr-1 erstellt
secret/project-pr-1 erstellt
configmap/project-pr-1 erstellt
ingress.extensions/project-pr-1 erstellt
namespace/project-pr-2 erstellt
deployment.apps/project-pr-2 erstellt
service/project-pr-2 erstellt
horizontalpodautoscaler.autoscaling/project-pr-2 erstellt
secret/project-pr-2 erstellt
configmap/project-pr-2 erstellt
ingress.extensions/project-pr-2 erstellt

Überprüfen wir, ob alle oben genannten Ressourcen erfolgreich erstellt wurden:

$ 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
...

Da wir die debug, Namespaces project-pr-1 und project-pr-2, müssen auch alle anderen Ressourcen sofort gelöscht werden, unabhängig vom Parameter afterDaysWithoutDeploy. In den Logs des Operators ist das sichtbar:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Namespace sollte gelöscht werden, da der Debug-Modus aktiviert ist.","namespaceName":"project-pr-1"}
... "msg":"Namespace wird verarbeitet.","namespaceName":"project-pr-1","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Namespace wurde gelöscht.","namespaceName":"project-pr-1"}
... "msg":"Namespace sollte gelöscht werden, da der Debug-Modus aktiviert ist.","namespaceName":"project-pr-2"}
... "msg":"Namespace wird verarbeitet.","namespaceName":"project-pr-2","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Namespace wurde gelöscht.","namespaceName":"project-pr-2"}

Wenn wir die Verfügbarkeit der Ressourcen überprüfen, werden sie sich im Status Terminating (Löschprozess) oder bereits gelöscht (der Befehl gibt nichts aus).

$ 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
...

Sie können den Erstellungsprozess fixtures mehrmals wiederholen und sich vergewissern, dass sie innerhalb einer Minute gelöscht werden.

Alternativen

Was kann man statt eines Operators machen, der im Cluster arbeitet? Es gibt mehrere Ansätze, die alle nicht perfekt sind (und ihre Mängel subjektiv sind), und jeder entscheidet selbst, was am besten für ein bestimmtes Projekt geeignet ist:

  1. Den Feature-Branch während des Builds des Master-Branches löschen.

    • Dafür muss man wissen, welcher Pull-Request zu dem Commit gehört, der gerade gebaut wird. Da der Feature-Branch-Namespace die ID des Pull-Requests enthält – seine Nummer oder den Namen des Branches – muss die ID immer im Commit angegeben werden.
    • Builds des Master-Branches schlagen fehl. Zum Beispiel haben Sie folgende Schritte: Projekt herunterladen, Tests starten, Projekt bauen, Release erstellen, Benachrichtigungen senden, Feature-Branch des letzten Pull-Requests bereinigen. Wenn der Build beim Senden der Benachrichtigung fehlschlägt, müssen Sie alle Ressourcen im Cluster manuell löschen.
    • Ohne den richtigen Kontext ist das Löschen des Feature-Branches im Master-Build nicht offensichtlich.

  2. Verwendung von Webhooks (Beispiel).

    • Vielleicht ist das nicht Ihr Ansatz. Zum Beispiel in Jenkins, unterstützt nur eine Art von Pipeline die Möglichkeit, ihre Konfigurationen im Quellcode zu speichern. Bei der Verwendung von Webhooks müssen Sie Ihr eigenes Skript zu deren Verarbeitung schreiben. Dieses Skript muss in der Jenkins-Oberfläche untergebracht werden, was schwer zu warten ist.

  3. Eintragen Cronjob und Kubernetes-Cluster hinzufügen.

    • Zeitaufwand für das Schreiben und die Wartung.
    • Der Operator arbeitet bereits in einem ähnlichen Stil, ist dokumentiert und wird unterstützt.

Vielen Dank für Ihre Aufmerksamkeit zu diesem Artikel. Link zum Projekt auf Github.

Quelle: habr.com

60GB SSD 8Gb DDR4