Eliminando la rama de características obsoleta en el clúster de Kubernetes

Eliminando la rama de características obsoleta en el clúster de Kubernetes

¡Hola! Rama de características (también conocida como vista previa de implementación, aplicación de revisión) — es cuando se despliega no solo la rama master, sino también cada pull request en una URL única. Se puede verificar si el código funciona en un entorno de producción, y se puede mostrar la característica a otros programadores o a product managers. Mientras trabajas en el pull request, cada nuevo commit elimina el despliegue actual para el viejo código y se despliega el nuevo código. Pueden surgir preguntas una vez que hayas fusionado el pull request en la rama master. La rama de características ya no es necesaria, pero los recursos de Kubernetes aún permanecen en el clúster.

Más sobre ramas de características

Uno de los enfoques para crear ramas de características en Kubernetes es utilizar namespaces. En resumen, la configuración de producción se ve así:

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

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

Para la rama de características se crea un namespace con su identificador (por ejemplo, el número del pull request) y algún prefijo/sufijo (por ejemplo, -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
...

En general, escribí Kubernetes Operator (una aplicación que tiene acceso a los recursos del clúster), enlace al proyecto en Github. Elimina los namespaces que pertenecen a las viejas ramas de características. En Kubernetes, si se elimina un namespace, otros recursos en ese namespace también se borran automáticamente.

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

Sobre cómo implementar las ramas de características en el clúster, se puede leer aquí y aquí.

Motivación

Veamos el ciclo de vida típico de un pull request con integración continua (continuous integration):

  1. Hacemos push de un nuevo commit en la rama.
  2. En la compilación, se ejecutan linters y/o pruebas.
  3. Las configuraciones de Kubernetes para el pull request se generan sobre la marcha (por ejemplo, se inserta su número en una plantilla lista).
  4. Con kubectl apply, las configuraciones se envían al clúster (despliegue).
  5. El pull request se fusiona en la rama master.

Mientras trabajas en el pull request, cada nuevo commit elimina el despliegue actual para el viejo código y despliega el nuevo código. Pero cuando el pull request se fusiona en la rama master, solo se compila la rama master. Al final, resulta que olvidamos el pull request, aunque sus recursos de Kubernetes todavía permanecen en el clúster.

Cómo usar

Instala el proyecto con el siguiente comando:

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

Crea un archivo con el siguiente contenido e instálalo a través de kubectl apply -f:

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

Parámetro namespaceSubstring necesario para filtrar namespaces para pull requests de otros namespaces. Por ejemplo, si en el clúster hay los siguientes namespaces: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, entonces los candidatos para eliminación serán habr-back-end-pr-17, habr-back-end-pr-33.

Parámetro afterDaysWithoutDeploy necesario para eliminar namespaces antiguos. Por ejemplo, si el namespace fue creado hace 3 días 1 hora , y en el parámetro se indica 3 días, este namespace será eliminado. Funciona al revés: si el namespace fue creado hace 2 días 23 horas , y en el parámetro se indica 3 días, este namespace no será eliminado.

Hay otro parámetro, que se encarga de la frecuencia con que se escanean todos los namespaces y se verifica la cantidad de días sin despliegue — checkEveryMinutes. Por defecto es 30 minutos.

¿Cómo funciona?

En la práctica, será necesario:

  1. Docker para trabajar en un entorno aislado.
  2. Minikube levantará el clúster de Kubernetes localmente.
  3. kubectl — interfaz de línea de comandos para gestionar el clúster.

Levantamos el clúster de Kubernetes localmente:

$ minikube start --vm-driver=docker
minikube v1.11.0 en Darwin 10.15.5
Usando el driver docker basado en el perfil existente.
Iniciando el nodo del plano de control minikube en el clúster minikube.

Especificamos kubectl usar el clúster local por defecto:

$ kubectl config use-context minikube
Cambiado al contexto "minikube".

Descargamos configuraciones para el entorno de producción:

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

Dado que las configuraciones de producción están configuradas para verificar namespaces antiguos, y en nuestro nuevo clúster no hay, reemplazaremos la variable de entorno IS_DEBUG en true. Con este valor, el parámetro afterDaysWithoutDeploy no se tiene en cuenta y los namespaces no se verifican por días sin despliegue, solo por la inclusión de la subcadena (-pr-).

Si estás en Linux:

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

Si estás en macOS:

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

Instalamos el proyecto:

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

Verificamos que el recurso ha aparecido en el clúster StaleFeatureBranch:

$ kubectl api-resources | grep stalefeaturebranches
NOMBRE                 ... GRUPO DE APIS                             ... TIPO
stalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch

Verificamos que el operador ha aparecido en el clúster:

$ kubectl get pods --namespace stale-feature-branch-operator
NOMBRE                                           ... ESTADO  ... EDAD
stale-feature-branch-operator-6bfbfd4df8-m7sch ... Ejecutándose ... 38s

Si miramos sus registros, está listo para procesar recursos StaleFeatureBranch:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Versión del operador: 0.0.1"}
...
... "msg":"Iniciando EventSource", ... , "fuente":"tipo fuente: /, Tipo="}
... "msg":"Iniciando Controlador", ...}
... "msg":"Iniciando trabajadores", ..., "número de trabajadores":1}

Instalamos los fixtures (configuraciones listas para modelar los recursos del clúster) para el recurso StaleFeatureBranch:

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

En las configuraciones se indica buscar namespaces con un subtérmino -pr- cada 1 minuto.:

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

El operador ha respondido y está listo para verificar los namespaces:

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

Instalamos fixtures, que contienen dos namespaces (project-pr-1, project-pr-2) y sus despliegues, servicios, ingress, y así sucesivamente:

$ 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

Verificamos que todos los recursos anteriores se han creado con éxito:

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

Dado que hemos habilitado debug, los namespaces project-pr-1 y project-pr-2, por lo tanto, todos los demás recursos deben eliminarse de inmediato sin tener en cuenta el parámetro afterDaysWithoutDeploy. En los logs del operador esto se puede ver:

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

Si se verifica la existencia de recursos, estarán en estado Terminating (proceso de eliminación) o ya eliminados (la salida del comando está vacía).

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

Puedes repetir el proceso de creación fixtures varias veces y asegurarte de que serán eliminados en un minuto.

Alternativas

¿Qué se puede hacer en lugar de un operador que trabaja en un clúster? Hay varios enfoques, todos ellos son imperfectos (y sus desventajas son subjetivas), y cada uno decide qué es lo que mejor se adapta a su proyecto específico:

  1. Eliminar la rama de características durante la integración continua de la rama master.

    • Para esto, es necesario saber qué pull request está relacionado con el commit que se está construyendo. Dado que el espacio de nombres de la rama de características contiene el identificador del pull request: su número o el nombre de la rama, siempre será necesario especificar este identificador en el commit.
    • Las construcciones de las ramas master fallan. Por ejemplo, si tienes los siguientes pasos: descargar el proyecto, ejecutar pruebas, compilar el proyecto, hacer un lanzamiento, enviar notificaciones, limpiar la rama de características del último pull request. Si la construcción falla al enviar la notificación, tendrás que eliminar todos los recursos en el clúster manualmente.
    • Sin el contexto adecuado, eliminar la rama de características en la construcción master no es evidente.

  2. Uso de webhooks (ejemplo).

    • Puede que este no sea tu enfoque. Por ejemplo, en Jenkins, solo un tipo de pipeline admite la posibilidad de guardar sus configuraciones en el código fuente. Al utilizar webhooks, es necesario escribir tu propio script para su procesamiento. Este script deberá ser alojado en la interfaz de Jenkins, lo cual es difícil de mantener.

  3. Escribir Cronjob y añadir el clúster de Kubernetes.

    • El tiempo dedicado a escribir y mantener.
    • El operador ya funciona en un estilo similar, está documentado y es mantenido.

Gracias por tu atención al artículo. Enlace al proyecto en Github.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster