Eliminar una rama de funciones obsoleta en un clúster de Kubernetes

Eliminar una rama de funciones obsoleta en un clúster de Kubernetes

Hi! Rama de características (también conocido como vista previa de implementación, revisión de aplicación): aquí es cuando no solo se implementa la rama maestra, sino también cada solicitud de extracción en una URL única. Puede comprobar si el código funciona en un entorno de producción; la función se puede mostrar a otros programadores o especialistas del producto. Mientras trabaja en una solicitud de extracción, cada nueva implementación actual de confirmación para el código anterior se elimina y se implementa la nueva implementación para el código nuevo. Pueden surgir preguntas cuando fusionó una solicitud de extracción en la rama maestra. Ya no necesita la rama de funciones, pero los recursos de Kubernetes todavía están en el clúster.

Más sobre ramas de características

Un método para crear ramas de funciones en Kubernetes es utilizar espacios de nombres. 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 una rama de características, se crea un espacio de nombres con su identificador (por ejemplo, el número de solicitud de extracción) y algún tipo de prefijo/postfijo (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í Operador de Kubernetes (una aplicación que tiene acceso a los recursos del clúster), enlace al proyecto en Github. Elimina espacios de nombres que pertenecen a ramas de funciones antiguas. En Kubernetes, si elimina un espacio de nombres, otros recursos en ese espacio de nombres también se eliminan automáticamente.

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

Puede leer sobre cómo implementar ramas de funciones en un clúster. aquí и aquí.

Motivación

Veamos un ciclo de vida típico de una solicitud de extracción con integración continua (continuous integration):

  1. Impulsamos un nuevo compromiso con la sucursal.
  2. En la compilación, se ejecutan linters y/o pruebas.
  3. Las configuraciones de solicitud de extracción de Kubernetes se generan sobre la marcha (por ejemplo, su número se inserta en la plantilla terminada).
  4. Usando kubectl apply, se agregan configuraciones al clúster (implementación).
  5. La solicitud de extracción se fusiona en la rama maestra.

Mientras trabaja en una solicitud de extracción, cada nueva implementación actual de confirmación para el código anterior se elimina y se implementa la nueva implementación para el código nuevo. Pero cuando una solicitud de extracción se fusiona en la rama maestra, solo se creará la rama maestra. Como resultado, resulta que ya nos hemos olvidado de la solicitud de extracción y sus recursos de Kubernetes todavía están en el clúster.

Modo de empleo

Instale el proyecto con el siguiente comando:

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

Cree un archivo con el siguiente contenido e instálelo 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 espacio de nombresSubcadena necesario para filtrar espacios de nombres para solicitudes de extracción de otros espacios de nombres. Por ejemplo, si el clúster tiene los siguientes espacios de nombres: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, entonces los candidatos para la eliminación serán habr-back-end-pr-17, habr-back-end-pr-33.

Parámetro después de días sin implementación necesario para eliminar espacios de nombres antiguos. Por ejemplo, si se crea un espacio de nombres 3 дня 1 час atrás y el parámetro indica 3 дня, este espacio de nombres se eliminará. También funciona en la dirección opuesta si se crea el espacio de nombres. 2 дня 23 часа atrás y el parámetro indica 3 дня, este espacio de nombres no se eliminará.

Hay un parámetro más, es responsable de la frecuencia con la que se escanean todos los espacios de nombres y verifica los días sin implementación: comprobarCadaMinutos. Por defecto es igual 30 минутам.

¿Cómo funciona esto

En la práctica, necesitarás:

  1. Docker para trabajar en un ambiente aislado.
  2. Minikube generará un clúster de Kubernetes localmente.
  3. kubectl — interfaz de línea de comando para la gestión de clústeres.

Levantamos un clúster de 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.

Especificar kubectl use el clúster local de forma predeterminada:

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

Descargue 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 espacios de nombres antiguos y nuestro clúster recién creado no los tiene, reemplazaremos la variable de entorno. IS_DEBUG en true. Con este valor el parámetro afterDaysWithoutDeploy no se tiene en cuenta y los espacios de nombres no se verifican durante los días sin implementación, solo para la aparición de la subcadena (-pr-).

si estas en Linux:

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

si estas en macOS:

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

Instalación del proyecto:

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

Comprobando que ha aparecido un recurso en el cluster StaleFeatureBranch:

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

Comprobamos que ha aparecido un operador en el cluster:

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

Si observa sus registros, está listo para procesar recursos. 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}

Instalamos listos para usar. fixtures (configuraciones listas para usar para modelar recursos de clúster) para un recurso StaleFeatureBranch:

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

Las configuraciones indican buscar espacios de nombres con una subcadena. -pr- una vez en 1 минуту.:

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 comprobar los espacios de nombres:

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

Establecer fixtures, que contiene dos espacios de nombres (project-pr-1, project-pr-2) y ellos deployments, services, ingress, etcétera:

$ 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

Comprobamos que todos los recursos anteriores se han creado correctamente:

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

Desde que incluimos debug, espacios de nombres project-pr-1 и project-pr-2, por lo tanto, todos los demás recursos deberán eliminarse inmediatamente sin tener en cuenta el parámetro afterDaysWithoutDeploy. Esto se puede ver en los registros del operador:

$ 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 verifica la disponibilidad de recursos, estarán en el estado Terminating (proceso de eliminación) o ya eliminado (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 asegúrese de que se eliminen en un minuto.

Alternativas

¿Qué se puede hacer en lugar de un operador que trabaja en cluster? Hay varios enfoques, todos son imperfectos (y sus deficiencias son subjetivas), y cada uno decide por sí mismo cuál es mejor para un proyecto en particular:

  1. Eliminar rama de funciones durante la compilación de integración continua de la rama maestra.

    • Para hacer esto, necesita saber qué solicitud de extracción se relaciona con la confirmación que se está creando. Dado que el espacio de nombres de la rama de características contiene el identificador de la solicitud de extracción (su número o el nombre de la rama), el identificador siempre deberá especificarse en la confirmación.
    • Las compilaciones de la rama maestra están fallando. Por ejemplo, tiene las siguientes etapas: descargar el proyecto, ejecutar pruebas, compilar el proyecto, realizar un lanzamiento, enviar notificaciones, borrar la rama de funciones de la última solicitud de extracción. Si la compilación falla al enviar una notificación, deberá eliminar todos los recursos del clúster manualmente.
    • Sin el contexto adecuado, eliminar ramas de funciones en la compilación maestra no es obvio.

  2. Usando webhooks (ejemplo).

    • Puede que este no sea su enfoque. Por ejemplo, en Jenkins, solo un tipo de canalización admite la capacidad de guardar sus configuraciones en el código fuente. Cuando utilice webhooks, deberá escribir su propio script para procesarlos. Este script deberá colocarse en la interfaz de Jenkins, que es difícil de mantener.

  3. Escribir cronjob y agregue un clúster de Kubernetes.

    • Dedicar tiempo a escribir y apoyar.
    • El operador ya trabaja de forma similar, está documentado y soportado.

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

Fuente: habr.com

Añadir un comentario