Rimozione di un ramo di funzionalità obsoleto in un cluster Kubernetes

Rimozione di un ramo di funzionalità obsoleto in un cluster Kubernetes

Hi! Ramo di funzionalità (noto anche come anteprima di distribuzione, app di revisione): in questo caso non viene distribuito solo il ramo principale, ma anche ciascuna richiesta pull a un URL univoco. Puoi verificare se il codice funziona in un ambiente di produzione; la funzionalità può essere mostrata ad altri programmatori o specialisti di prodotto. Mentre lavori su una richiesta pull, a ogni nuovo commit, la distribuzione corrente per il vecchio codice viene eliminata e viene implementata la nuova distribuzione per il nuovo codice. Potrebbero sorgere domande quando si unisce una richiesta pull nel ramo principale. Non hai più bisogno del ramo funzionalità, ma le risorse Kubernetes sono ancora nel cluster.

Ulteriori informazioni sui rami delle funzionalità

Un approccio per creare rami di funzionalità in Kubernetes consiste nell'utilizzare gli spazi dei nomi. In breve, la configurazione di produzione è questa:

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

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

Per un ramo di funzionalità, viene creato uno spazio dei nomi con il suo identificatore (ad esempio, il numero della richiesta pull) e una sorta di prefisso/postfisso (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. Rimuove gli spazi dei nomi che appartengono ai vecchi rami di funzionalità. In Kubernetes, se elimini uno spazio dei nomi, anche le altre risorse in quello spazio dei nomi verranno eliminate automaticamente.

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

Puoi leggere come implementare i rami delle funzionalità in un cluster qui и qui.

motivazione

Diamo un'occhiata a un tipico ciclo di vita della richiesta pull con integrazione continua (continuous integration):

  1. Inviamo un nuovo commit al ramo.
  2. Durante la compilazione vengono eseguiti linter e/o test.
  3. Le configurazioni delle richieste pull Kubernetes vengono generate al volo (ad esempio, il suo numero viene inserito nel modello finito).
  4. Utilizzando kubectl apply, le configurazioni vengono aggiunte al cluster (distribuzione).
  5. La richiesta pull viene unita al ramo principale.

Mentre stai lavorando su una richiesta pull, ogni nuova distribuzione corrente di commit per il vecchio codice viene eliminata e viene implementata la nuova distribuzione per il nuovo codice. Ma quando una richiesta pull viene unita al ramo master, verrà creato solo il ramo master. Di conseguenza, risulta che ci siamo già dimenticati della richiesta pull e 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

Creare un file con il seguente contenuto e installarlo 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 gli spazi dei nomi per le richieste pull da altri spazi dei nomi. Ad esempio, se il cluster ha i seguenti spazi dei nomi: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, allora i candidati per la cancellazione saranno habr-back-end-pr-17, habr-back-end-pr-33.

Parametro afterDaysWithoutDeploy necessario per eliminare i vecchi spazi dei nomi. Ad esempio, se viene creato lo spazio dei nomi 3 дня 1 час indietro e il parametro indica 3 дня, questo spazio dei nomi verrà eliminato. Funziona anche nella direzione opposta se viene creato lo spazio dei nomi 2 дня 23 часа indietro e il parametro indica 3 дня, questo spazio dei nomi non verrà eliminato.

C'è un altro parametro, è responsabile della frequenza con cui scansionare tutti gli spazi dei nomi e verificare i giorni senza distribuzione: controlla Ogni Minuto. Per impostazione predefinita è uguale 30 минутам.

Come funziona

In pratica ti serviranno:

  1. docker per lavorare in un ambiente isolato.
  2. Minikube creerà un cluster Kubernetes localmente.
  3. kubectl — interfaccia della riga di comando per la gestione del cluster.

Creiamo un cluster Kubernetes localmente:

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

specificare kubectl utilizza il cluster locale per impostazione predefinita:

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

Scarica 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 configurate per controllare i vecchi spazi dei nomi e il nostro cluster appena generato non li possiede, sostituiremo la variabile di ambiente IS_DEBUG su true. Con questo valore il parametro afterDaysWithoutDeploy non viene preso in considerazione e i namespace non vengono controllati per giorni senza deploy, solo per la presenza della sottostringa (-pr-).

Se sei attivo Linux:

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

Se sei attivo macOS:

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

Installazione del progetto:

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

Verifica che una risorsa sia apparsa nel cluster StaleFeatureBranch:

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

Controlliamo che nel cluster sia comparso un operatore:

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

Se guardi i suoi log, è pronto per elaborare le risorse 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}

Installiamo già pronto fixtures (configurazioni già pronte per la modellazione delle risorse del cluster) per una risorsa StaleFeatureBranch:

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

Le configurazioni indicano di cercare gli spazi dei nomi con una sottostringa -pr- una volta dentro 1 минуту.:

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

L'operatore ha risposto ed è pronto a controllare gli spazi dei nomi:

$ 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"}

Set fixtures, contenente due spazi dei nomi (project-pr-1, project-pr-2) e loro deployments, services, 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

Controlliamo che tutte le risorse sopra indicate 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
...

Dal momento che abbiamo incluso debug, spazi dei nomi project-pr-1 и project-pr-2, pertanto tutte le altre risorse dovranno essere cancellate immediatamente senza tenere conto del parametro afterDaysWithoutDeploy. Questo può essere visto nei log dell'operatore:

$ 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"}

Se controlli la disponibilità delle risorse, saranno nello stato Terminating (processo di eliminazione) o già eliminato (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 assicurarsi che vengano rimossi entro un minuto.

alternative

Cosa può fare invece un operatore che lavora in un cluster? Esistono diversi approcci, tutti imperfetti (e i loro difetti sono soggettivi) e ognuno decide da solo cosa è meglio per un particolare progetto:

  1. Elimina il ramo funzionalità durante la creazione dell'integrazione continua del ramo master.

    • Per fare ciò, devi sapere quale richiesta pull si riferisce al commit che viene creato. Poiché lo spazio dei nomi del ramo della funzionalità contiene l'identificatore della richiesta pull, ovvero il suo numero o il nome del ramo, l'identificatore dovrà sempre essere specificato nel commit.
    • Le build del ramo principale stanno fallendo. Ad esempio, hai le seguenti fasi: scaricare il progetto, eseguire test, creare il progetto, effettuare un rilascio, inviare notifiche, cancellare il ramo della funzionalità dell'ultima richiesta pull. Se la compilazione fallisce durante l'invio di una notifica, dovrai eliminare manualmente tutte le risorse nel cluster.
    • Senza un contesto adeguato, l'eliminazione dei rami delle funzionalità nella build principale non è ovvia.

  2. Utilizzando webhook (esempio).

    • Questo potrebbe non essere il tuo approccio. Ad esempio, nel Jenkins, solo un tipo di pipeline supporta la possibilità di salvare le proprie configurazioni nel codice sorgente. Quando si utilizzano webhook, è necessario scrivere il proprio script per elaborarli. Questo script dovrà essere inserito nell'interfaccia Jenkins, che è difficile da mantenere.

  3. Scrivere cronjob e aggiungi un cluster Kubernetes.

    • Dedicare tempo alla scrittura e al supporto.
    • L'operatore lavora già in uno stile simile, è documentato e supportato.

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

Fonte: habr.com

Aggiungi un commento