Construcción y despliegue dinámico de imágenes Docker con werf usando el ejemplo de un sitio de documentación versionada

Ya hemos hablado varias veces sobre nuestra herramienta GitOps. werf, y esta vez nos gustaría compartir nuestra experiencia sobre cómo construir un sitio web con la documentación del propio proyecto — werf.io (su versión en español es — es.werf.io). Es un sitio estático común, sin embargo, su construcción es interesante ya que se basa en una cantidad dinámica de artefactos.

Construcción y despliegue dinámico de imágenes Docker con werf usando el ejemplo de un sitio de documentación versionada

No profundizaremos en los matices de la estructura del sitio: la generación de un menú común para todas las versiones, páginas con información sobre lanzamientos, etc. — sino que nos centraremos en las cuestiones y características de la construcción dinámica y un poco en los procesos CI/CD relacionados.

Introducción: cómo está estructurado el sitio

Empecemos por decir que la documentación de werf se almacena junto con su código. Esto impone ciertos requisitos para el desarrollo, que en general escapan al alcance de este artículo, pero al menos se puede decir que:

  • Las nuevas funciones de werf no deben lanzarse sin actualizar la documentación y, a la inversa, cualquier cambio en la documentación implica el lanzamiento de una nueva versión de werf;
  • El proyecto tiene un desarrollo bastante intenso: nuevas versiones pueden lanzarse varias veces al día;
  • Cualquier operación manual para desplegar el sitio con una nueva versión de documentación es como mínimo tediosa;
  • El proyecto adopta un enfoque de versionado semántico, con 5 canales de estabilidad. El proceso de lanzamiento implica el paso secuencial de las versiones a través de los canales de acuerdo con el aumento de la estabilidad: de alpha a rock-solid;
  • El sitio tiene una versión en español que "vive y se desarrolla" (es decir, cuyo contenido se actualiza) paralelamente a la versión principal (es decir, la versión en inglés).

Para ocultar al usuario toda esta "cocina interna", ofreciéndole lo que "simplemente funciona", hemos creado una herramienta separada para instalar y actualizar werf — es multiwerf. Basta con indicar el número de versión y el canal de estabilidad que está dispuesto a utilizar, y multiwerf verificará si hay una nueva versión en el canal y la descargará si es necesario.

En el menú de selección de versiones del sitio, están disponibles las últimas versiones de werf en cada canal. Por defecto, en la dirección werf.io/documentation se abre la versión del canal más estable para el último lanzamiento — que también es indexada por los motores de búsqueda. La documentación para el canal está disponible en direcciones separadas (por ejemplo, werf.io/v1.0-beta/documentation para el lanzamiento beta 1.0).

En total, el sitio tiene las siguientes versiones disponibles:

  1. raíz (se abre por defecto),
  2. para cada canal activo de actualizaciones de cada versión (por ejemplo, werf.io/v1.0-beta).

Para generar una versión específica del sitio, generalmente es suficiente compilarla usando Jekyll, ejecutando el comando correspondiente en el directorio /docs del repositorio werf (jekyll build), cambiando previamente a la etiqueta de Git de la versión necesaria.

Solo queda agregar que:

  • se utiliza la propia herramienta (werf) para la compilación;
  • los procesos de CI/CD se basan en GitLab CI;
  • y todo esto, por supuesto, funciona en Kubernetes.

Tareas

Ahora formularemos las tareas que consideran toda la especificidad descrita:

  1. Después de cambiar la versión de werf en cualquier canal de actualizaciones la documentación del sitio debe actualizarse automáticamente.
  2. Para el desarrollo, es necesario poder ver versiones preliminares del sitio a veces.

La recompilación del sitio debe realizarse después de cambiar la versión en cualquier canal desde las etiquetas de Git correspondientes, pero durante el proceso de construcción de la imagen obtendremos las siguientes características:

  • Dado que la lista de versiones en los canales cambia, solo se debe recompilar la documentación para los canales donde ha cambiado la versión. No es muy conveniente recompilar todo de nuevo.
  • El propio conjunto de canales para las versiones puede cambiar. En algún momento, por ejemplo, puede no haber versión en los canales más estables que la versión early-access 1.1, pero con el tiempo aparecerán — ¿acaso vamos a cambiar la compilación manualmente en este caso?

Resulta que la compilación depende de datos externos cambiantes.

Implementación

Selección de enfoque

Como opción, se pueden ejecutar cada versión necesaria en un pod separado en Kubernetes. Esta opción implica un mayor número de objetos en el clúster, que aumentará con el número de versiones estables de werf. Y esto a su vez implica un mantenimiento más complejo: cada versión tiene su propio servidor HTTP, aunque con una carga pequeña. Por supuesto, esto también conlleva mayores gastos de recursos.

Nosotros optamos por compilar todas las versiones necesarias en una sola imagen. La estática compilada de todas las versiones del sitio se encuentra en un contenedor con NGINX, y el tráfico hacia el Deployment correspondiente llega a través de NGINX Ingress. La estructura simple —aplicación sin estado— permite escalar fácilmente el Deployment (dependiendo de la carga) utilizando Kubernetes.

Para ser más precisos, estamos recopilando dos imágenes: una para el entorno de producción y otra adicional para el entorno de desarrollo. La imagen adicional se utiliza (se inicia) solo en el entorno de desarrollo junto con la principal y contiene la versión del sitio del commit de revisión, y la ruta entre ellas se realiza a través de recursos Ingress.

werf vs git clone y artefactos

Como se mencionó anteriormente, para generar la estática del sitio para una versión específica de la documentación, es necesario realizar una construcción cambiando a la etiqueta correspondiente del repositorio. Se podría hacer esto clonando el repositorio cada vez que se construye y seleccionando las etiquetas adecuadas de la lista. Sin embargo, esta es una operación bastante intensiva en recursos y, además, requiere la redacción de instrucciones no triviales... Otro gran inconveniente es que con este enfoque no hay posibilidad de almacenar en caché durante la construcción.

Aquí nos ayuda la propia utilidad werf, que implementa almacenamiento en caché inteligente y permite utilizar repositorios externos. Usar werf para agregar código desde el repositorio acelerará significativamente la construcción, ya que werf en esencia clona el repositorio una vez y luego realiza solo fetch si es necesario. Además, al agregar datos desde el repositorio podemos seleccionar solo los directorios necesarios (en nuestro caso, este es el directorio docs), lo que reduce significativamente el volumen de datos añadidos.

Dado que Jekyll es una herramienta destinada a compilar estática y no es necesaria en la imagen final, sería lógico realizar la compilación en el artefacto werf, y en la imagen final importar solo el resultado de la compilación..

Escribimos werf.yaml

Así que hemos decidido que compilaremos cada versión en un artefacto werf separado. Sin embargo, no sabemos cuántos de estos artefactos habrá durante la construcción, por lo que no podemos escribir una configuración de construcción fija (técnicamente podemos, pero no sería del todo eficiente).

werf permite usar plantillas Go en su archivo de configuración (werf.yaml), y esto da la oportunidad de generar la configuración "sobre la marcha" dependiendo de datos externos (¡lo que necesitamos!). En nuestro caso, los datos externos son la información sobre las versiones y lanzamientos, sobre la cual recopilamos la cantidad necesaria de artefactos y obtenemos como resultado dos imágenes: werf-doc y werf-dev para ejecutar en diferentes entornos.

Los datos externos se transmiten a través de variables de entorno. Su composición es la siguiente:

  • RELEASES — una cadena con la lista de lanzamientos y su versión actual de werf correspondiente, en forma de lista separada por espacios en el formato %. Ejemplo: 1.0%v1.0.4-beta.20
  • CHANNELS — una cadena con la lista de canales y su versión actual de werf correspondiente, en forma de lista separada por espacios en el formato %. Ejemplo: 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • ROOT_VERSION — la versión del lanzamiento de werf que se mostrará por defecto en el sitio (no siempre es necesario mostrar la documentación de la versión más alta). Ejemplo: v1.0.4-beta.20
  • REVIEW_SHA — el hash del commit de revisión desde el cual se debe compilar la versión para el entorno de prueba.

Estas variables se llenarán en el pipeline de GitLab CI, y cómo exactamente — se explica a continuación.

Primero, para conveniencia, definimos en werf.yaml variables de plantillas Go, asignándoles valores de las variables de entorno:

{{ $_ := set . "WerfVersions" (cat (env "CHANNELS") (env "RELEASES") | splitList " ") }}
{{ $Root := . }}
{{ $_ := set . "WerfRootVersion" (env "ROOT_VERSION") }}
{{ $_ := set . "WerfReviewCommit" (env "REVIEW_SHA") }}

La descripción del artefacto para compilar la estática de la versión del sitio es en general igual para todos los casos necesarios (incluyendo, la generación de la versión raíz, así como la versión para el entorno dev). Por lo tanto, la llevaremos a un bloque separado utilizando la función define — para reutilizarla posteriormente a través de include. Al template se le pasarán los siguientes argumentos:

  • Versión — la versión generada (nombre de la etiqueta);
  • Channel — el nombre del canal de actualizaciones para el cual se genera el artefacto;
  • Commit — el hash del commit, si el artefacto se genera para un commit de revisión;
  • contexto.

Descripción del template del artefacto

{{- define "doc_artifact" -}}
{{- $Root := index . "Root" -}}
artifact: doc-{{ .Channel }}
from: jekyll/builder:3
mount:
- from: build_dir
  to: /usr/local/bundle
ansible:
  install:
  - shell: |
      export PATH=/usr/jekyll/bin/:$PATH
  - name: "Instalar Dependencias"
    shell: bundle install
    args:
      executable: /bin/bash
      chdir: /app/docs
  beforeSetup:
{{- if .Commit }}
  - shell: echo "Revisar SHA - {{ .Commit }}."
{{- end }}
{{- if eq .Channel "root" }}
  - name: "releases.yml HASH: {{ $Root.Files.Get "releases.yml" | sha256sum }}"
    copy:
      content: |
{{ $Root.Files.Get "releases.yml" | indent 8 }}
      dest:  /app/docs/_data/releases.yml
{{- else }}
  - file:
      path: /app/docs/_data/releases.yml
      state: touch
{{- end }}
  - file:
      path: "{{`{{ item }}`}}"
      state: directory
      mode: 0777
    with_items:
    - /app/main_site/
    - /app/es_site/
  - file:
      dest: /app/docs/pages_es/cli
      state: link
      src: /app/docs/pages/cli
  - shell: |
      echo -e "werfVersion: {{ .Version }}nwerfChannel: {{ .Channel }}" > /tmp/_config_additional.yml
      export PATH=/usr/jekyll/bin/:$PATH
{{- if and (ne .Version "review") (ne .Channel "root") }}
{{- $_ := set . "BaseURL" ( printf "v%s" .Channel ) }}
{{- else if ne .Channel "root" }}
{{- $_ := set . "BaseURL" .Channel }}
{{- end }}
      jekyll build -s /app/docs  -d /app/_main_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/tmp/_config_additional.yml
      jekyll build -s /app/docs  -d /app/_es_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/app/docs/_config_es.yml,/tmp/_config_additional.yml
    args:
      executable: /bin/bash
      chdir: /app/docs
git:
- url: https://github.com/flant/werf.git
  to: /app/
  owner: jekyll
  group: jekyll
{{- if .Commit }}
  commit: {{ .Commit }}
{{- else }}
  tag: {{ .Version }}
{{- end }}
  stageDependencies:
    install: ['docs/Gemfile','docs/Gemfile.lock']
    beforeSetup: '**/*'
  includePaths: 'docs'
  excludePaths: '**/*.sh'
{{- end }}

El nombre del artefacto debe ser único. Podemos lograr esto, por ejemplo, añadiendo el nombre del canal (el valor de la variable .Channel) como sufijo del nombre del artefacto: artifact: doc-{{ .Channel }}. Pero hay que entender que al importar desde los artefactos, será necesario referirse a los mismos nombres.

Al describir el artefacto se utiliza la opción de werf de montaje. El montaje especificando el directorio de trabajo build_dir permite conservar la caché de Jekyll entre ejecuciones del pipeline, lo que acelera significativamente la reconstrucción.

También habrás notado el uso del archivo releases.yml es un archivo YAML con información sobre los lanzamientos, solicitado desde github.com (el artefacto generado al ejecutar el pipeline). Es necesario para la compilación del sitio, pero en el contexto del artículo nos interesa porque su estado afecta la reconstrucción de un solo artefacto el artefacto de la versión raíz del sitio (en otros artefactos no es necesario).

Esto se implementa mediante un operador condicional if de plantillas Go y la construcción {{ $Root.Files.Get "releases.yml" | sha256sum }} en la etapa de la fase. Funciona de la siguiente manera: al crear el artefacto para la versión raíz (variable .Channel es root) el hash del archivo releases.yml afecta la firma de toda la etapa, ya que es un componente del nombre de la tarea de Ansible (parámetro name). Por lo tanto, al cambiar el contenido, archivos releases.yml el artefacto correspondiente será reconstruido.

También tenga en cuenta el trabajo con un repositorio externo. En la imagen del artefacto desde el repositorio werf,solo se agrega el directorio, /docsdependiendo de los parámetros proporcionados, se añaden datos del tag necesario o del commit de revisión.

Para utilizar la plantilla del artefacto para generar la descripción del artefacto de las versiones de canales y lanzamientos proporcionados, organizamos un ciclo basado en la variable .WerfVersions en werf.yaml:

{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ dict "Version" $VersionsDict._1 "Channel" $VersionsDict._0 "Root" $Root | include "doc_artifact" }}
---
{{ end -}}

Dado que el ciclo generará varios artefactos (eso esperamos), es necesario considerar el separador entre ellos: la secuencia --- (más información sobre la sintaxis del archivo de configuración se puede encontrar en la documentación). Como se definió anteriormente, al llamar a la plantilla en el ciclo se pasan los parámetros de versión, URL y contexto raíz.

De manera similar, pero ya sin ciclo, llamamos a la plantilla del artefacto para 'casos especiales': para la versión raíz, así como para la versión del commit de revisión:

{{ dict "Version" .WerfRootVersion "Channel" "root" "Root" $Root  | include "doc_artifact" }}
---
{{- if .WerfReviewCommit }}
{{ dict "Version" "review" "Channel" "review" "Commit" .WerfReviewCommit "Root" $Root  | include "doc_artifact" }}
{{- end }}

Tenga en cuenta que el artefacto para el commit de revisión solo se construirá si se establece la variable .WerfReviewCommit.

¡Los artefactos están listos, es hora de importar!

La imagen final, destinada a ejecutarse en Kubernetes, es un NGINX común, al que se le ha añadido un archivo de configuración del servidor nginx.conf y estática de artefactos. Además del artefacto de la versión raíz del sitio, necesitamos repetir el ciclo en la variable .WerfVersions para importar los artefactos de las versiones de canales y lanzamientos + cumplir con la regla de nombramiento de artefactos que adoptamos anteriormente. Dado que cada artefacto almacena versiones del sitio en dos idiomas, los importamos en los lugares previstos por la configuración.

Descripción de la imagen final werf-doc

imagen: werf-doc
desde: nginx:stable-alpine
ansible:
  configuración:
  - nombre: "Configuración \/etc\/nginx\/nginx.conf"
    copiar:
      contenido: |
{{ .Files.Get ".werf\/nginx.conf" | indent 8 }}
      destino: \/etc\/nginx\/nginx.conf
  - archivo:
      ruta: "{{`{{ item }}`}}"
      estado: directorio
      modo: 0777
    con_elementos:
    - \/app\/main_site\/assets
    - \/app\/ru_site\/assets
importar:
- artefacto: doc-root
  agregar: \/app\/_main_site
  a: \/app\/main_site
  antes: configuración
- artefacto: doc-root
  agregar: \/app\/_ru_site
  a: \/app\/ru_site
  antes: configuración
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ $Channel := $VersionsDict._0 -}}
{{ $Version := $VersionsDict._1 -}}
- artefacto: doc-{{ $Channel }}
  agregar: \/app\/_main_site
  a: \/app\/main_site\/v{{ $Channel }}
  antes: configuración
{{ end -}}
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ $Channel := $VersionsDict._0 -}}
{{ $Version := $VersionsDict._1 -}}
- artefacto: doc-{{ $Channel }}
  agregar: \/app\/_ru_site
  a: \/app\/ru_site\/v{{ $Channel }}
  antes: configuración
{{ end -}}

Una imagen adicional que se ejecuta junto con la principal en el contorno de desarrollo contiene solo dos versiones del sitio: la versión del commit de revisión y la versión raíz del sitio (ahí están los activos comunes y, si recuerdas, los datos de las versiones). Así, la imagen adicional se diferenciará de la principal solo en la sección de importación (y, por supuesto, en el nombre):

imagen: werf-dev
...
importar:
- artefacto: doc-root
  agregar: \/app\/_main_site
  a: \/app\/main_site
  antes: configuración
- artefacto: doc-root
  agregar: \/app\/_ru_site
  a: \/app\/ru_site
  antes: configuración
{{- if .WerfReviewCommit  }}
- artefacto: doc-review
  agregar: \/app\/_main_site
  a: \/app\/main_site\/revisión
  antes: configuración
- artefacto: doc-review
  agregar: \/app\/_ru_site
  a: \/app\/ru_site\/revisión
  antes: configuración
{{- end }}

Como se mencionó anteriormente, el artefacto para el commit de revisión solo se generará al activar la variable de entorno establecida REVIEW_SHA. En realidad, no habría necesidad de generar la imagen werf-dev si no hay variable de entorno REVIEW_SHA, pero para que la limpieza por políticas de imágenes Docker en werf funcione para la imagen werf-dev, la dejaremos compilarse solo con el artefacto de la versión raíz (de todos modos, ya está compilada), para simplificar la estructura del pipeline.

¡La compilación está lista! Pasemos a CI\/CD y aspectos importantes.

Pipeline en GitLab CI y características de la compilación dinámica

Al iniciar la compilación, necesitamos establecer las variables de entorno utilizadas en werf.yaml. Esto no se aplica a la variable REVIEW_SHA, que configuraremos al invocar el pipeline desde el webhook de GitHub.

Llevar la generación de datos externos necesarios a un script Bash generate_artifacts, que generará dos artefactos del pipeline de GitLab:

  • archivo releases.yml con datos sobre versiones,
  • archivo common_envs.sh, que contiene variables de entorno para exportar.

El contenido del archivo generate_artifacts lo encontrarás en nuestro repositorio con ejemplos. La obtención de datos en sí no es el tema del artículo, pero el archivo common_envs.sh es importante para nosotros, ya que de él depende el funcionamiento de werf. Un ejemplo de su contenido:

export RELEASES='1.0%v1.0.6-4'
export CHANNELS='1.0-alpha%v1.0.7-1 1.0-beta%v1.0.7-1 1.0-ea%v1.0.6-4 1.0-stable%v1.0.6-4 1.0-rock-solid%v1.0.6-4'
export ROOT_VERSION='v1.0.6-4'

La salida de este script se puede utilizar, por ejemplo, con una función de Bash source.

Y ahora lo más interesante. Para que tanto la construcción como el despliegue de la aplicación funcionen correctamente, es necesario que werf.yaml fue sean iguales como mínimo dentro de un mismo pipeline. Si esta condición no se cumple, las firmas de las etapas que calcula werf durante la construcción y, por ejemplo, el despliegue, serán diferentes. Esto provocará un error en el despliegue, ya que la imagen necesaria para el despliegue estará ausente.

En otras palabras, si durante la construcción de la imagen del sitio la información sobre lanzamientos y versiones es una, y en el momento del despliegue sale una nueva versión y las variables de entorno tienen otros valores, el despliegue terminará con un error: dado que el artefacto de la nueva versión aún no se ha construido.

Si la generación werf.yaml depende de datos externos (por ejemplo, una lista de versiones actuales, como en nuestro caso), la composición y los valores de tales datos deben fijarse dentro del pipeline. Esto es especialmente importante si los parámetros externos cambian con bastante frecuencia.

Vamos a obtener y fijar datos externos en la primera etapa del pipeline en GitLab (Prebuild) y pasarlos a continuación como artefacto de GitLab CI. Esto permitirá ejecutar y reiniciar tareas del pipeline (construcción, despliegue, limpieza) con la misma configuración en werf.yaml.

Contenido de la etapa Prebuild archivos .gitlab-ci.yml:

Prebuild:
  stage: prebuild
  script:
    - bash .\/generate_artifacts 1> common_envs.sh
    - cat .\/common_envs.sh
  artifacts:
    paths:
      - releases.yml
      - common_envs.sh
    expire_in: 2 semanas

Al fijar los datos externos en el artefacto, se puede realizar la construcción y el despliegue utilizando las etapas estándar del pipeline de GitLab CI: Build y Deploy. El pipeline lo iniciamos a través de hooks desde el repositorio de GitHub werf (es decir, al realizar cambios en el repositorio en GitHub). Los datos para ellos se pueden tomar en las propiedades del proyecto de GitLab en la sección Configuración de CI / CD -> Desencadenadores de pipeline, y luego crearemos en GitHub el correspondiente Webhook (Configuración -> Webhooks).

La etapa de construcción se verá de la siguiente manera:

Build:
  stage: build
  script:
    - type multiwerf && . $(multiwerf use 1.0 alpha --as-file)
    - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
    - source common_envs.sh
    - werf build-and-publish --stages-storage :local
  except:
    refs:
      - schedules
  dependencies:
    - Prebuild

GitLab añadirá dos artefactos de la etapa Prebuild, así que exportamos las variables con los datos de entrada preparados utilizando la construcción source common_envs.sh. Iniciamos la etapa de compilación en todos los casos, excepto al ejecutar el pipeline según lo programado. Según lo programado, tendremos un pipeline para limpieza; no es necesario realizar la compilación en este caso.

En la etapa de despliegue describiremos dos tareas, una para el despliegue en producción y otra para el entorno de desarrollo, utilizando una plantilla YAML:

.base_deploy: &base_deploy
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.0 alpha --as-file)
    - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
    - source common_envs.sh
    - werf deploy --stages-storage :local
  dependencies:
    - Prebuild
  except:
    refs:
      - schedules

Despliegue en Producción:
  <<: *base_deploy
  variables:
    WERF_KUBE_CONTEXT: prod
  environment:
    name: production
    url: werf.io
  only:
    refs:
      - master
  except:
    variables:
      - $REVIEW_SHA
    refs:
      - schedules

Despliegue en Pruebas:
  <<: *base_deploy
  variables:
    WERF_KUBE_CONTEXT: dev
  environment:
    name: test
    url: werf.test.flant.com
  except:
    refs:
      - schedules
  only:
    variables:
      - $REVIEW_SHA

Las tareas, en esencia, solo difieren en la especificación del contexto del clúster en el que werf debe realizar el despliegue (WERF_KUBE_CONTEXT), y la configuración de las variables de entorno del entorno (environment.name y environment.url), que se utilizan luego en las plantillas del gráfico de Helm. No incluiré el contenido de las plantillas, ya que no hay nada interesante para el tema que estamos tratando, pero puedes encontrarlas en el repositorio del artículo.

Toque final

Dado que las versiones de werf se lanzan con bastante frecuencia, también se compilarán nuevas imágenes a menudo, y el Docker Registry seguirá creciendo. Por lo tanto, es esencial configurar la limpieza automática de imágenes según políticas. Hacer esto es bastante sencillo.

Para llevar a cabo esto, se requiere:

  • Agregar una etapa de limpieza en .gitlab-ci.yml;
  • Agregar una ejecución periódica de la tarea de limpieza;
  • Configurar una variable de entorno con el token de acceso para escritura.

Agregamos la etapa de limpieza en .gitlab-ci.yml:

Cleanup:
  stage: cleanup
  script:
    - type multiwerf && . $(multiwerf use 1.0 alpha --as-file)
    - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
    - source common_envs.sh
    - docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
    - werf cleanup --stages-storage :local
  only:
    refs:
      - schedules

Casi todo esto ya lo hemos visto un poco más arriba; solo que para la limpieza necesitas autenticarte previamente en el Docker Registry con un token que tenga los permisos para eliminar imágenes en el Docker Registry (el token que se otorga automáticamente para las tareas de GitLab CI no tiene tales permisos). Debes crear el token en GitLab previamente y especificar su valor en la variable de entorno WERF_IMAGES_CLEANUP_PASSWORD el proyecto (Configuración de CI/CD -> Variables).

La adición de la tarea de limpieza con el horario necesario se realiza en CI/CD ->
Horarios
.

Todo: el proyecto en Docker Registry ya no crecerá continuamente debido a imágenes no utilizadas.

Para concluir la parte práctica, recuerdo que las listas completas del artículo están disponibles en Git:

Resultado

  1. Hemos logrado una estructura de construcción lógica: un artefacto por versión.
  2. La construcción es universal y no requiere cambios manuales al lanzar nuevas versiones de werf: la documentación en el sitio se actualiza automáticamente.
  3. Se construyen dos imágenes para diferentes entornos.
  4. Funciona rápido, ya que se maximiza la utilización de la caché: al lanzar una nueva versión de werf o al invocar un hook de GitHub para el commit de revisión, solo se recompila el artefacto correspondiente con la versión modificada.
  5. No es necesario preocuparse por eliminar imágenes no utilizadas: la limpieza según las políticas de werf mantendrá el orden en Docker Registry.

Conclusiones

  • El uso de werf permite que la construcción funcione rápidamente gracias a la caché tanto de la propia construcción como a la caché al trabajar con repositorios externos.
  • Trabajar con repositorios Git externos elimina la necesidad de clonar el repositorio completamente cada vez o inventar la rueda con lógica de optimización complicada. werf utiliza caché y clona solo una vez, luego usa fetch y solo según sea necesario.
  • La posibilidad de utilizar plantillas de Go en el archivo de configuración de la construcción werf.yaml permite describir una construcción cuyo resultado depende de datos externos.
  • El uso de montajes en werf acelera significativamente la construcción de artefactos, gracias a la caché que es compartida entre todos los pipelines.
  • werf permite configurar fácilmente la limpieza, lo que es especialmente relevante en construcciones dinámicas.

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