Etiquetado basado en contenido en el recolector werf: ¿por qué y cómo funciona?

Etiquetado basado en contenido en el recolector werf: ¿por qué y cómo funciona?

werf — nuestra herramienta CLI de GitOps de código abierto para la construcción y entrega de aplicaciones en Kubernetes. En la versión v1.1 se presentó una nueva característica en el generador de imágenes: etiquetado de imágenes basado en el contenido o etiquetado basado en contenido. Hasta ahora, el esquema típico de etiquetado en werf presumía etiquetar imágenes de Docker por etiqueta de Git, rama de Git o commit de Git. Pero todos estos esquemas tienen desventajas, que son completamente resueltas por la nueva estrategia de etiquetado. Los detalles sobre esto y por qué es tan buena se encuentran a continuación.

Despliegue de un conjunto de microservicios desde un solo repositorio de Git

A menudo se da la situación en la que una aplicación está dividida en múltiples servicios más o menos independientes. Las versiones de estos servicios pueden ocurrir de forma independiente: se puede liberar uno o varios servicios a la vez, mientras que los demás deben seguir funcionando sin ningún cambio. Pero desde el punto de vista del almacenamiento del código y la gestión del proyecto, es más conveniente mantener tales servicios de aplicación en un único repositorio.

Hay situaciones en las que los servicios son realmente independientes y no están relacionados con una única aplicación. En tal caso, estarán ubicados en proyectos separados y su lanzamiento se realizará a través de procesos CI/CD separados en cada uno de los proyectos.

Sin embargo, en la realidad, los desarrolladores a menudo dividen una sola aplicación en varios microservicios, pero crear un repositorio y proyecto separados para cada uno... es un claro exceso. Precisamente sobre esta situación trataremos a continuación: varios de estos microservicios están en un único repositorio del proyecto y los lanzamientos ocurren a través de un único proceso en CI/CD.

Etiquetado por rama de Git y etiqueta de Git

Supongamos que se utiliza la estrategia de etiquetado más común — etiqueta-o-rama. Para las ramas de Git, las imágenes se etiquetan con el nombre de la rama; para una rama, en un momento dado, solo existe una imagen publicada con el nombre de esa rama. Para las etiquetas de Git, las imágenes se etiquetan según el nombre de la etiqueta.

Al crear una nueva etiqueta de Git — por ejemplo, al lanzarse una nueva versión — se creará una nueva etiqueta de Docker para todas las imágenes del proyecto en el Docker Registry:

  • myregistry.org/myproject/frontend:v1.1.10
  • myregistry.org/myproject/myservice1:v1.1.10
  • myregistry.org/myproject/myservice2:v1.1.10
  • myregistry.org/myproject/myservice3:v1.1.10
  • myregistry.org/myproject/myservice4:v1.1.10
  • myregistry.org/myproject/myservice5:v1.1.10
  • myregistry.org/myproject/database:v1.1.10

Estos nuevos nombres de imagen se integran a través de plantillas de Helm en la configuración de Kubernetes. Al iniciar el despliegue con el comando werf deploy se actualiza el campo image en los manifiestos de recursos de Kubernetes y se reinician los recursos correspondientes debido al cambio del nombre de la imagen.

Problema: en caso de que el contenido de la imagen no haya cambiado desde el despliegue anterior (etiqueta de Git), y solo se haya modificado su etiqueta de Docker, ocurre un reinicio innecesario de esta aplicación y, por lo tanto, puede haber algún tiempo de inactividad. Aunque no había razones reales para realizar este reinicio.

Como consecuencia, con el esquema actual de etiquetado, es necesario crear varios repositorios de Git, y surge el problema de organizar el despliegue de estos múltiples repositorios. En términos generales, este esquema resulta ser sobrecargado y complicado. Es mejor agrupar muchos servicios en un solo repositorio y crear etiquetas de Docker de manera que no haya reinicios innecesarios.

Etiquetado por commit de Git

En werf también existe una estrategia de etiquetado relacionada con los commits de Git.

El commit de Git es un identificador del contenido del repositorio de Git y depende del historial de modificaciones de archivos en el repositorio de Git, por lo que parece lógico utilizarlo para etiquetar imágenes en el Docker Registry.

Sin embargo, el etiquetado por commit de Git tiene las mismas desventajas que el etiquetado por ramas de Git o etiquetas de Git:

  • Se podría haber creado un commit vacío que no modifica archivos, pero la etiqueta de Docker de la imagen se cambiaría.
  • Se podría haber creado un commit de fusión que no modifica archivos, pero la etiqueta de Docker de la imagen se cambiaría.
  • Se podría haber creado un commit que modifica archivos en Git que no se importan en la imagen, y nuevamente se modificaría la etiqueta de Docker de la imagen.

El etiquetado por nombre de rama de Git no refleja la versión de la imagen

Hay otro problema relacionado con la estrategia de etiquetado por ramas de Git.

El etiquetado por nombre de rama funciona siempre que los commits de esa rama se atañan secuencialmente en orden cronológico.

Si en el esquema actual el usuario inicia la reconstrucción de un commit antiguo relacionado con alguna rama, werf sobrescribirá la imagen con la versión recién construida de la imagen para el commit antiguo correspondiente. Las implementaciones que utilizan esta etiqueta a partir de ese momento corren el riesgo, durante el reinicio de los pods, de extraer otra versión de la imagen, lo que resultaría en que nuestra aplicación perdería conexión con el sistema CI y se desincronizaría.

Además, al realizar push acumulados en una misma rama con un intervalo de tiempo corto, un commit antiguo puede ser construido después que uno más reciente: la versión anterior de la imagen puede sobrescribir la nueva bajo la etiqueta de la rama de Git. Estos problemas pueden ser solucionados por un sistema CI/CD (por ejemplo, en GitLab CI, se ejecuta un pipeline para la serie de commits más reciente). Sin embargo, no todos los sistemas lo soportan y debe haber una forma más confiable de prevenir un problema tan fundamental.

¿Qué es el etiquetado basado en contenido?

Así que, ¿qué es el etiquetado basado en contenido? Se trata de etiquetar imágenes según su contenido.

Para crear etiquetas de Docker, no se utilizan primitivas de Git (rama de Git, etiqueta de Git…), sino un hash relacionado con:

  • el contenido de la imagen. El identificador-etiqueta de la imagen refleja su contenido. Al construir una nueva versión, este identificador no cambiará, si no se modifican los archivos en la imagen;
  • la historia de creación de esta imagen en Git. Las imágenes asociadas a diferentes ramas de Git y diferentes historias de construcción a través de werf tendrán diferentes identificadores de etiqueta.

Como tal identificador de etiqueta se utiliza la conocida firma de las etapas de la imagen.

Cada imagen consta de un conjunto de etapas: from, before-install, git-archive, install, imports-after-install, before-setup,… git-latest-patch etc. Cada etapa tiene un identificador que refleja su contenido: firma de la etapa (stage signature).

La imagen final, compuesta por estas etapas, es etiquetada con la llamada firma del conjunto de estas etapas — stages signature, que es general para todas las etapas de la imagen.

Cada imagen de la configuración werf.yaml en general tendrá su propia firma y, por lo tanto, una etiqueta de Docker correspondiente.

La firma de las etapas resuelve todos los problemas mencionados:

  • Es resistente a commits vacíos de Git.
  • Es resistente a commits de Git que modifican archivos que no son relevantes para la imagen.
  • No conlleva el problema de sobrescribir la versión más actual de la imagen al reiniciar construcciones para commits antiguos de la rama de Git.

Ahora esta es la estrategia de etiquetado recomendada y se utiliza por defecto en werf para todos los sistemas CI.

Cómo habilitar y usar en werf

La opción correspondiente ha sido añadida al comando werf publish: --tag-by-stages-signature=true|false

En el sistema CI, la estrategia de etiquetado se establece con el comando werf ci-env. Anteriormente, se definía un parámetro werf ci-env --tagging-strategy=tag-or-branch. Ahora, si se especifica werf ci-env --tagging-strategy=stages-signature o no especificar esta opción, werf por defecto utilizará la estrategia de etiquetado stages-signature. El comando werf ci-env automáticamente establecerá las banderas requeridas para el comando werf build-and-publish (o werf publish), por lo que no es necesario especificar opciones adicionales para estos comandos.

Por ejemplo, el comando:

werf publish --stages-storage :local --images-repo registry.hello.com/web/core/system --tag-by-stages-signature

… puede crear las siguientes imágenes:

  • registry.hello.com/web/core/system/backend:4ef339f84ca22247f01fb335bb19f46c4434014d8daa3d5d6f0e386d
  • registry.hello.com/web/core/system/frontend:f44206457e0a4c8a54655543f749799d10a9fe945896dab1c16996c6

Aquí 4ef339f84ca22247f01fb335bb19f46c4434014d8daa3d5d6f0e386d — es la firma de las etapas de la imagen backend, y f44206457e0a4c8a54655543f749799d10a9fe945896dab1c16996c6 — firma de las etapas de la imagen frontend.

Al usar funciones especiales werf_container_image y werf_container_env en las plantillas de Helm no es necesario cambiar nada: estas funciones generarán automáticamente los nombres correctos de las imágenes.

Ejemplo de configuración en un sistema CI:

type multiwerf && source <(multiwerf use 1.1 beta)
type werf && source <(werf ci-env gitlab)
werf build-and-publish|deploy

Más información sobre la configuración está disponible en la documentación:

Total

  • La nueva opción werf publish --tag-by-stages-signature=true|false.
  • Nuevo valor de opción werf ci-env --tagging-strategy=stages-signature|tag-or-branch (si no se especifica, por defecto será stages-signature).
  • Si anteriormente se usaron opciones de etiquetado por Git-commits (WERF_TAG_GIT_COMMIT o la opción werf publish --tag-git-commit COMMIT), es necesario cambiar a la estrategia de etiquetado stages-signature.
  • Es mejor cambiar nuevos proyectos a la nueva esquema de etiquetado desde el inicio.
  • Los proyectos antiguos al migrar a werf 1.1 deberían idealmente cambiar a la nueva esquema de etiquetado, sin embargo, la antigua etiqueta-o-rama sigue siendo compatible.

El etiquetado basado en contenido resuelve todos los problemas expuestos en el artículo:

  • Resiliencia del nombre de la etiqueta Docker ante Git-commits vacíos.
  • Resiliencia del nombre de la etiqueta Docker ante Git-commits que modifican archivos no relevantes para la imagen.
  • No provoca problemas de sobrescritura de la versión actual de la imagen al reiniciar compilaciones para antiguos Git-commits de ramas de Git.

¡Úsalos! Y no olvides visitarnos en GitHub, para crear un issue o encontrar uno existente, dar un thumbs up, crear un PR o simplemente observar el desarrollo del proyecto.

P.D.

También puedes leer en nuestro blog:

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