El problema de la limpieza “inteligente” de imágenes de contenedores y su solución en werf

El problema de la limpieza “inteligente” de imágenes de contenedores y su solución en werf

El artículo analiza los problemas de limpieza de imágenes que se acumulan en los registros de contenedores (Docker Registry y sus análogos) en las realidades de las canalizaciones CI/CD modernas para aplicaciones nativas de la nube entregadas a Kubernetes. Se dan los principales criterios para la relevancia de las imágenes y las dificultades resultantes para automatizar la limpieza, ahorrar espacio y satisfacer las necesidades de los equipos. Finalmente, usando el ejemplo de un proyecto Open Source específico, te contamos cómo se pueden superar estas dificultades.

introducción

La cantidad de imágenes en un registro de contenedor puede crecer rápidamente, ocupando más espacio de almacenamiento y, por lo tanto, aumentando significativamente su costo. Para controlar, limitar o mantener un crecimiento aceptable del espacio ocupado en el registro, se acepta:

  1. utilice una cantidad fija de etiquetas para las imágenes;
  2. Limpiar las imágenes de alguna manera.


La primera limitación es a veces aceptable para equipos pequeños. Si los desarrolladores tienen suficientes etiquetas permanentes (latest, main, test, boris etc.), el registro no aumentará de tamaño y durante mucho tiempo no tendrás que pensar en limpiarlo en absoluto. Después de todo, todas las imágenes irrelevantes se borran y simplemente no queda trabajo de limpieza (todo lo hace un recolector de basura normal).

Sin embargo, este enfoque limita en gran medida el desarrollo y rara vez es aplicable a proyectos CI/CD modernos. Una parte integral del desarrollo fue automatización, que le permite probar, implementar y ofrecer nuevas funciones a los usuarios mucho más rápido. Por ejemplo, en todos nuestros proyectos, se crea automáticamente una canalización de CI con cada confirmación. En él, la imagen se ensambla, se prueba, se implementa en varios circuitos de Kubernetes para su depuración y comprobaciones restantes y, si todo está bien, los cambios llegan al usuario final. Y esto ya no es ciencia espacial, sino algo que ocurre todos los días para muchos, muy probablemente para usted, ya que está leyendo este artículo.

Dado que la corrección de errores y el desarrollo de nuevas funciones se llevan a cabo en paralelo, y los lanzamientos se pueden realizar varias veces al día, es obvio que el proceso de desarrollo va acompañado de una cantidad significativa de confirmaciones, lo que significa una gran cantidad de imágenes en el registro. Como resultado, surge la cuestión de organizar una limpieza eficaz del registro, es decir, eliminando imágenes irrelevantes.

Pero, ¿cómo se puede siquiera determinar si una imagen es relevante?

Criterios de relevancia de la imagen.

En la gran mayoría de los casos, los criterios principales serán:

1. La primera (la más obvia y crítica de todas) son las imágenes que utilizado actualmente en Kubernetes. Eliminar estas imágenes puede generar importantes costos de tiempo de inactividad de producción (por ejemplo, es posible que sea necesario replicar las imágenes) o anular los esfuerzos del equipo de depuración en cualquiera de los bucles. (Por esta razón incluso hicimos un especial exportadores de Prometheus, que rastrea la ausencia de dichas imágenes en cualquier clúster de Kubernetes).

2. Segundo (menos obvio, pero también muy importante y nuevamente relacionado con la explotación): imágenes que necesario para la reversión en caso de detección de problemas graves en la versión actual. Por ejemplo, en el caso de Helm, se trata de imágenes que se utilizan en versiones guardadas del lanzamiento. (Por cierto, de forma predeterminada en Helm el límite es 256 revisiones, pero es poco probable que alguien realmente necesite guardar tales ¿una gran cantidad de versiones?...) Después de todo, nosotros, en particular, almacenamos versiones para poder usarlas más tarde, es decir, “retroceder” hacia ellos si es necesario.

3. Tercero - necesidades del desarrollador: Todas las imágenes que están relacionadas con su trabajo actual. Por ejemplo, si estamos considerando un PR, entonces tiene sentido dejar una imagen correspondiente a la última confirmación y, digamos, a la confirmación anterior: de esta manera el desarrollador puede volver rápidamente a cualquier tarea y trabajar con los últimos cambios.

4. Cuarto: imágenes que Corresponden a las versiones de nuestra aplicación., es decir. son el producto final: v1.0.0, 20.04.01/XNUMX/XNUMX, sierra, etc.

NB: Los criterios definidos aquí se formularon en base a la experiencia de interactuar con docenas de equipos de desarrollo de diferentes empresas. Sin embargo, por supuesto, dependiendo de las particularidades de los procesos de desarrollo y de la infraestructura utilizada (por ejemplo, no se utiliza Kubernetes), estos criterios pueden diferir.

Elegibilidad y soluciones existentes

Los servicios populares con registros de contenedores, por regla general, ofrecen sus propias políticas de limpieza de imágenes: en ellas se pueden definir las condiciones bajo las cuales se elimina una etiqueta del registro. Sin embargo, estas condiciones están limitadas por parámetros como nombres, hora de creación y número de etiquetas*.

* Depende de implementaciones de registro de contenedores específicas. Consideramos las posibilidades de las siguientes soluciones: Azure CR, Docker Hub, ECR, GCR, GitHub Packages, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io, a partir de septiembre de 2020.

Este conjunto de parámetros es suficiente para satisfacer el cuarto criterio, es decir, seleccionar imágenes que correspondan a las versiones. Sin embargo, para todos los demás criterios, hay que elegir algún tipo de solución de compromiso (una política más dura o, por el contrario, más indulgente), dependiendo de las expectativas y las capacidades financieras.

Por ejemplo, el tercer criterio, relacionado con las necesidades de los desarrolladores, se puede resolver organizando procesos dentro de los equipos: denominación específica de imágenes, mantenimiento de listas de permisos especiales y acuerdos internos. Pero, en última instancia, todavía es necesario automatizarlo. Y si las posibilidades de las soluciones ya preparadas no son suficientes, hay que hacer algo por su cuenta.

La situación con los dos primeros criterios es similar: no pueden cumplirse sin recibir datos de un sistema externo, aquel donde se implementan las aplicaciones (en nuestro caso, Kubernetes).

Ilustración del flujo de trabajo en Git

Digamos que estás trabajando en algo como esto en Git:

El problema de la limpieza “inteligente” de imágenes de contenedores y su solución en werf

El icono con una cabeza en el diagrama indica imágenes de contenedores que están actualmente implementadas en Kubernetes para cualquier usuario (usuarios finales, evaluadores, administradores, etc.) o que los desarrolladores utilizan para depuración y fines similares.

¿Qué sucede si las políticas de limpieza solo permiten conservar (no eliminar) imágenes? por nombres de etiquetas dados?

El problema de la limpieza “inteligente” de imágenes de contenedores y su solución en werf

Evidentemente, semejante escenario no hará feliz a nadie.

¿Qué cambiará si las políticas permiten que las imágenes no se eliminen? según un intervalo de tiempo determinado/número de últimas confirmaciones?

El problema de la limpieza “inteligente” de imágenes de contenedores y su solución en werf

El resultado ha mejorado mucho, pero aún está lejos de ser ideal. Después de todo, todavía tenemos desarrolladores que necesitan imágenes en el registro (o incluso implementadas en K8) para depurar errores...

Resumiendo la situación actual del mercado: las funciones disponibles en los registros de contenedores no ofrecen suficiente flexibilidad a la hora de limpiar, y la razón principal de esto es No hay forma de interactuar con el mundo exterior.. Resulta que los equipos que requieren tal flexibilidad se ven obligados a implementar de forma independiente la eliminación de imágenes "desde el exterior", utilizando la API de Docker Registry (o la API nativa de la implementación correspondiente).

Sin embargo, estábamos buscando una solución universal que automatizara la limpieza de imágenes para diferentes equipos usando diferentes registros...

Nuestro camino hacia la limpieza de imágenes universal

¿De dónde viene esta necesidad? El hecho es que no somos un grupo separado de desarrolladores, sino un equipo que atiende a muchos de ellos a la vez, ayudándolos a resolver de manera integral los problemas de CI/CD. Y la principal herramienta técnica para esto es la utilidad Open Source. patio. Su peculiaridad es que no realiza una única función, sino que acompaña los procesos de entrega continua en todas las etapas: desde el montaje hasta el despliegue.

Publicar imágenes en el registro* (inmediatamente después de crearlas) es una función obvia de dicha utilidad. Y dado que las imágenes se colocan allí para su almacenamiento, entonces, si su almacenamiento no es ilimitado, usted debe ser responsable de su limpieza posterior. Se discutirá más adelante cómo logramos el éxito en esto, cumpliendo todos los criterios especificados.

* Aunque los registros en sí pueden ser diferentes (Docker Registry, GitLab Container Registry, Harbor, etc.), sus usuarios enfrentan los mismos problemas. La solución universal en nuestro caso no depende de la implementación del registro, porque se ejecuta fuera de los propios registros y ofrece el mismo comportamiento para todos.

Aunque utilizamos werf como ejemplo de implementación, esperamos que los enfoques utilizados sean útiles para otros equipos que enfrentan dificultades similares.

Así que nos pusimos a trabajar externo implementación de un mecanismo para limpiar imágenes, en lugar de aquellas capacidades que ya están integradas en los registros de contenedores. El primer paso fue utilizar la API de Docker Registry para crear las mismas políticas primitivas para la cantidad de etiquetas y el momento de su creación (mencionadas anteriormente). añadido a ellos lista de permitidos basada en imágenes utilizadas en la infraestructura implementada, es decir. Kubernetes. Para este último, fue suficiente usar la API de Kubernetes para recorrer todos los recursos implementados y obtener una lista de valores. image.

Esta solución trivial resolvió el problema más crítico (criterio n.° 1), pero fue solo el comienzo de nuestro viaje para mejorar el mecanismo de limpieza. El siguiente paso, y mucho más interesante, fue la decisión asociar imágenes publicadas con el historial de Git.

Esquemas de etiquetado

Para empezar, elegimos un enfoque en el que la imagen final debería almacenar la información necesaria para la limpieza y basamos el proceso en esquemas de etiquetado. Al publicar una imagen, el usuario seleccionó una opción de etiquetado específica (git-branch, git-commit o git-tag) y utilizó el valor correspondiente. En los sistemas CI, estos valores se establecían automáticamente en función de variables de entorno. De hecho la imagen final se asoció con una primitiva Git específica, almacenando en etiquetas los datos necesarios para la limpieza.

Este enfoque dio como resultado un conjunto de políticas que permitieron utilizar Git como única fuente de verdad:

  • Al eliminar una rama/etiqueta en Git, las imágenes asociadas en el registro se eliminaron automáticamente.
  • La cantidad de imágenes asociadas con etiquetas y confirmaciones de Git se puede controlar mediante la cantidad de etiquetas utilizadas en el esquema seleccionado y el momento en que se creó la confirmación asociada.

En general, la implementación resultante satisfizo nuestras necesidades, pero pronto nos esperaba un nuevo desafío. El hecho es que al utilizar esquemas de etiquetado basados ​​en primitivas de Git, encontramos una serie de deficiencias. (Dado que su descripción está más allá del alcance de este artículo, todos pueden familiarizarse con los detalles. aquí.) Por lo tanto, al decidir cambiar a un enfoque de etiquetado más eficiente (etiquetado basado en contenido), tuvimos que reconsiderar la implementación de la limpieza de imágenes.

Nuevo algoritmo

¿Por qué? Con el etiquetado basado en contenido, cada etiqueta puede satisfacer múltiples confirmaciones en Git. Al limpiar imágenes, ya no puede asumir sólo desde la confirmación donde se agregó la nueva etiqueta al registro.

Para el nuevo algoritmo de limpieza, se decidió alejarse de los esquemas de etiquetado y construir proceso de metaimagen, cada uno de los cuales almacena un montón de:

  • la confirmación en la que se realizó la publicación (no importa si la imagen se agregó, cambió o permaneció igual en el registro del contenedor);
  • y nuestro identificador interno correspondiente a la imagen ensamblada.

En otras palabras, se proporcionó vincular etiquetas publicadas con confirmaciones en Git.

Configuración final y algoritmo general.

Al configurar la limpieza, los usuarios ahora tienen acceso a políticas que seleccionan imágenes actuales. Cada una de estas políticas se define:

  • muchas referencias, es decir Etiquetas Git o ramas Git que se utilizan durante el escaneo;
  • y el límite de imágenes buscadas para cada referencia del conjunto.

A modo de ilustración, así es como empezó a verse la configuración de política predeterminada:

cleanup:
  keepPolicies:
  - references:
      tag: /.*/
      limit:
        last: 10
  - references:
      branch: /.*/
      limit:
        last: 10
        in: 168h
        operator: And
    imagesPerReference:
      last: 2
      in: 168h
      operator: And
  - references:  
      branch: /^(main|staging|production)$/
    imagesPerReference:
      last: 10

Esta configuración contiene tres políticas que cumplen con las siguientes reglas:

  1. Guarde la imagen de las últimas 10 etiquetas de Git (por fecha de creación de la etiqueta).
  2. Guarde no más de 2 imágenes publicadas en la última semana para no más de 10 hilos con actividad en la última semana.
  3. Guarde 10 imágenes para sucursales. main, staging и production.

El algoritmo final se reduce a los siguientes pasos:

  • Recuperar manifiestos del registro de contenedores.
  • Excluyendo imágenes utilizadas en Kubernetes, porque Ya los hemos preseleccionado sondeando la API de K8s.
  • Escaneando el historial de Git y excluyendo imágenes según políticas específicas.
  • Eliminando imágenes restantes.

Volviendo a nuestra ilustración, esto es lo que ocurre con werf:

El problema de la limpieza “inteligente” de imágenes de contenedores y su solución en werf

Sin embargo, incluso si no utiliza werf, se puede aplicar un enfoque similar a la limpieza avanzada de imágenes, en una implementación u otra (según el enfoque preferido para el etiquetado de imágenes), a otros sistemas/utilidades. Para ello, basta con recordar los problemas que surgen y encontrar en su stack aquellas oportunidades que le permitan integrar su solución de la forma más fluida posible. Esperamos que el camino recorrido le ayude a mirar su caso particular con nuevos detalles y reflexiones.

Conclusión

  • Tarde o temprano, la mayoría de los equipos se enfrentan al problema del desbordamiento del registro.
  • A la hora de buscar soluciones, primero es necesario determinar los criterios de relevancia de la imagen.
  • Las herramientas que ofrecen los populares servicios de registro de contenedores permiten organizar una limpieza muy sencilla que no tiene en cuenta el "mundo exterior": las imágenes utilizadas en Kubernetes y las peculiaridades de los flujos de trabajo del equipo.
  • Un algoritmo flexible y eficiente debe comprender los procesos de CI/CD y operar no solo con datos de imágenes de Docker.

PS

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario