Eliminiamo il branch feature obsoleto nel cluster Kubernetes

Eliminiamo il branch feature obsoleto nel cluster Kubernetes

Ciao! Ramo funzionale (alias anteprima di distribuzione, app di revisione) — è quando viene distribuita non solo la branch master, ma anche ogni pull request su un URL unico. Puoi verificare se il codice funziona nell'ambiente di produzione, puoi mostrare la funzione ad altri programmatori o product manager. Mentre lavori su un pull request, ogni nuovo commit elimina la distribuzione attuale del vecchio codice, e una nuova distribuzione per il nuovo codice viene eseguita. Possono sorgere domande quando hai fuso il pull request nella branch master. Il ramo funzionale non ti serve più, ma le risorse Kubernetes sono ancora nel cluster.

Ancora sui rami funzionali

Uno degli approcci per creare rami funzionali in Kubernetes è utilizzare i namespace. In breve, la configurazione di produzione appare così:

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

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

Per il ramo funzionale viene creato un namespace con il suo identificativo (ad esempio, il numero del pull request) e un prefisso/suffisso (ad esempio, -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
...

In generale, ho scritto Operatore Kubernetes (un'applicazione che ha accesso alle risorse del cluster), link al progetto su Github. Essa rimuove i namespace relativi ai vecchi rami funzionali. In Kubernetes, se elimini un namespace, altre risorse in quel namespace vengono anch'esse eliminate automaticamente.

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

Per sapere come integrare i rami funzionali nel cluster, puoi leggere qui e qui.

Motivazione

Diamo un'occhiata al tipico ciclo di vita di un pull request con integrazione continua (continuous integration):

  1. Pushiamo un nuovo commit nella branch.
  2. Durante la build, vengono eseguiti linters e/o test.
  3. Le configurazioni di Kubernetes del pull request vengono generate al volo (ad esempio, il suo numero viene inserito in un modello pronto).
  4. Utilizzando kubectl apply, le configurazioni vengono caricate nel cluster (distribuzione).
  5. Il pull request viene unito nella branch master.

Mentre lavori su un pull request, ogni nuovo commit elimina la distribuzione attuale del vecchio codice, e una nuova distribuzione per il nuovo codice viene eseguita. Ma quando il pull request viene fuso nella branch master, verrà buildato solo il ramo master. Ne consegue che dimentichiamo ormai il pull request, ma le sue risorse Kubernetes sono ancora nel cluster.

Come utilizzare

Installa il progetto con il comando seguente:

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

Crea un file con il seguente contenuto e installalo tramite kubectl apply -f:

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

Parametro namespaceSubstring necessario per filtrare i namespace per le pull request provenienti da altri namespace. Ad esempio, se nel cluster ci sono i seguenti namespace: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, allora i candidati per la rimozione saranno habr-back-end-pr-17, habr-back-end-pr-33.

Parametro afterDaysWithoutDeploy necessario per rimuovere i vecchi namespace. Ad esempio, se un namespace è stato creato 3 giorni 1 ora fa, e nel parametro è specificato 3 giorni, questo namespace verrà rimosso. Funziona anche al contrario, se un namespace è stato creato 2 giorni 23 ore fa, e nel parametro è specificato 3 giorni, questo namespace non verrà rimosso.

C'è un altro parametro, che determina con quale frequenza scansionare tutti i namespace e verificare i giorni senza deploy — checkEveryMinutes. Per impostazione predefinita è uguale a 30 minuti.

Come funziona

Nella pratica, servirà:

  1. Docker per lavorare in un ambiente isolato.
  2. Minikube alzerà il cluster Kubernetes localmente.
  3. kubectl — interfaccia della riga di comando per gestire il cluster.

Alziamo il cluster Kubernetes localmente:

$ minikube start --vm-driver=docker
minikube v1.11.0 su Darwin 10.15.5
Utilizzando il driver docker basato sul profilo esistente.
Avviando il nodo del piano di controllo minikube nel cluster minikube.

Specifichiamo kubectl utilizzare il cluster locale per impostazione predefinita:

$ kubectl config use-context minikube
Passato al contesto "minikube".

Scarichiamo le configurazioni per l'ambiente di produzione:

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

Poiché le configurazioni di produzione sono impostate per controllare i vecchi namespace, e nel nostro nuovo cluster non ce ne sono, sostituiremo la variabile d'ambiente IS_DEBUG in true. Con questo valore, il parametro afterDaysWithoutDeploy non viene considerato e i namespace non vengono controllati per i giorni senza deploy, solo per la presenza della sottostringa (-pr-).

Se sei su Linux:

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

Se sei su macOS:

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

Installiamo il progetto:

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

Controlliamo che nel cluster sia apparso il risorsa StaleFeatureBranch:

$ kubectl api-resources | grep stalefeaturebranches
NOME                 ... APIGROUP                             ... TYPE
stalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch

Controlliamo che nel cluster sia apparso l'operatore:

$ kubectl get pods --namespace stale-feature-branch-operator
NOME                                           ... STATO  ... ETÀ
stale-feature-branch-operator-6bfbfd4df8-m7sch ... In esecuzione ... 38s

Se si guarda nei suoi log, è pronto a gestire le risorse StaleFeatureBranch:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Versione Operatore: 0.0.1"}
...
... "msg":"Inizio EventSource", ... , "source":"kind source: /, Kind="}
... "msg":"Inizio Controller", ...}
... "msg":"Inizio lavori", ..., "worker count":1}

Installiamo i pronti fixtures (configurazioni pronte per la modellazione delle risorse del cluster) per la risorsa StaleFeatureBranch:

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

Nelle configurazioni si specifica di cercare i namespace contenenti la sottostringa -pr- ogni 1 minuto.:

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

L'operatore ha reagito ed è pronto a controllare i namespace:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"La feature branch obsoleta è in elaborazione.","namespaceSubstring":"-pr-","afterDaysWithoutDeploy":1,"checkEveryMinutes":1,"isDebug":"true"}

Installiamo fixtures, contenente due namespace (project-pr-1, project-pr-2) e i loro deployments, servizi, ingress, e così via:

$ 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

Verifichiamo che tutte le risorse sopra siano state create con successo:

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

Poiché abbiamo attivato debug, i namespace project-pr-1 e project-pr-2, di conseguenza tutte le altre risorse, dovranno essere eliminate immediatamente senza considerare il parametro afterDaysWithoutDeploy. Nei log dell'operatore questo è visibile:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Il namespace deve essere eliminato poiché la modalità debug è attivata.","namespaceName":"project-pr-1"}
... "msg":"Il namespace è in elaborazione.","namespaceName":"project-pr-1","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Il namespace è stato eliminato.","namespaceName":"project-pr-1"}
... "msg":"Il namespace deve essere eliminato poiché la modalità debug è attivata.","namespaceName":"project-pr-2"}
... "msg":"Il namespace è in elaborazione.","namespaceName":"project-pr-2","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Il namespace è stato eliminato.","namespaceName":"project-pr-2"}

Se controlliamo la presenza delle risorse, saranno in stato Terminating (in fase di eliminazione) o già eliminate (l'output del comando è vuoto).

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

Puoi ripetere il processo di creazione fixtures più volte e assicurarti che vengano eliminate entro un minuto.

Alternative

Cosa si può fare al posto dell'operatore che lavora in un cluster? Ci sono diversi approcci, tutti imperfetti (e i loro svantaggi sono soggettivi), e ciascuno decide cosa si adatta meglio al progetto specifico:

  1. Eliminare il branch feature durante la build del branch master di integrazione continua.

    • Per fare questo è necessario sapere quale pull request si riferisce al commit che viene compilato. Poiché il namespace del branch feature contiene l'identificativo della pull request — il suo numero o il nome del branch, sarà sempre necessario specificare l'identificatore nel commit.
    • Le build dei branch master falliscono. Ad esempio, hai i seguenti passaggi: scaricare il progetto, eseguire i test, compilare il progetto, fare il rilascio, inviare notifiche, pulire il branch feature dell'ultimo pull request. Se la build fallisce durante l'invio delle notifiche, dovrai eliminare manualmente tutte le risorse nel cluster.
    • Senza un contesto adeguato, l'eliminazione del branch feature nella build master non è ovvia.

  2. Utilizzare i webhook (un esempio).

    • Potrebbe non essere il tuo approccio. Ad esempio, in Jenkins, solo un tipo di pipeline supporta la possibilità di salvare le sue configurazioni nel codice sorgente. Quando si utilizzano i webhook, è necessario scrivere uno script personale per il loro trattamento. Questo script dovrà essere posizionato nell'interfaccia di Jenkins, il che è difficile da mantenere.

  3. Scrivere Cronjob e aggiungere il cluster Kubernetes.

    • Spesa di tempo per la scrittura e il supporto.
    • L'operatore già funziona in uno stile simile, è documentato e supportato.

Grazie per l'attenzione all'articolo. Link al progetto su Github.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster