Construction dynamique et déploiement des images Docker avec werf sur l'exemple d'un site de documentation versionnée

Nous avons dĂ©jĂ  parlĂ© Ă  plusieurs reprises de notre outil GitOps werf, et cette fois, nous aimerions partager notre expĂ©rience de la construction du site avec la documentation du projet lui-mĂȘme — werf.io (sa version en russe est — ru.werf.io). Il s'agit d'un site statique ordinaire, cependant, sa construction est intĂ©ressante car elle est rĂ©alisĂ©e en utilisant un nombre dynamique d'artefacts.

Construction dynamique et déploiement des images Docker avec werf sur l'exemple d'un site de documentation versionnée

Nous ne plongerons pas dans les dĂ©tails de la structure du site : gĂ©nĂ©ration du menu commun pour toutes les versions, pages d'information sur les versions, etc. — mais nous allons plutĂŽt nous concentrer sur les questions et les particularitĂ©s de la construction dynamique et un peu sur les processus connexes CI/CD.

Introduction : comment le site est organisé

Commençons par le fait que la documentation de werf est stockée avec son code. Cela impose certaines exigences au développement, qui dépassent globalement le cadre de cet article, mais nous pouvons au moins dire que :

  • Les nouvelles fonctionnalitĂ©s de werf ne doivent pas ĂȘtre publiĂ©es sans mise Ă  jour de la documentation et, inversĂ©ment, tout changement dans la documentation implique la sortie d'une nouvelle version de werf ;
  • Le projet fait l'objet d'un dĂ©veloppement assez intensif : de nouvelles versions peuvent sortir plusieurs fois par jour ;
  • Toute opĂ©ration manuelle de dĂ©ploiement du site avec une nouvelle version de la documentation est au minimum fastidieuse ;
  • Le projet adopte une approche de versioning sĂ©mantique versionning, avec 5 canaux de stabilitĂ©. Le processus de publication implique le passage successif des versions par les canaux dans l'ordre croissant de stabilitĂ© : d'alpha Ă  rock-solid ;
  • Le site dispose d'une version en russe, qui « vit et Ă©volue » (c'est-Ă -dire que son contenu est mis Ă  jour) parallĂšlement Ă  la version principale (c'est-Ă -dire la version anglaise).

Pour cacher Ă  l'utilisateur toute cette « cuisine interne », en lui proposant ce qui « fonctionne simplement », nous avons créé un outil sĂ©parĂ© pour installer et mettre Ă  jour werf — ce sont des multiwerf. Il suffit d'indiquer le numĂ©ro de version et le canal de stabilitĂ© que vous ĂȘtes prĂȘt Ă  utiliser, et multiwerf vĂ©rifiera s'il existe une nouvelle version sur le canal et la tĂ©lĂ©chargera si nĂ©cessaire.

Dans le menu de sĂ©lection des versions sur le site, les derniĂšres versions de werf sont disponibles dans chaque canal. Par dĂ©faut, Ă  l'adresse werf.io/documentation , s'ouvre la version du canal le plus stable pour la derniĂšre version — elle est Ă©galement indexĂ©e par les moteurs de recherche. La documentation pour le canal est disponible Ă  des adresses distinctes (par exemple, werf.io/v1.0-beta/documentation pour la version beta 1.0).

En résumé, le site propose les versions suivantes :

  1. racine (s'ouvre par défaut),
  2. pour chaque canal actif de mises Ă  jour de chaque version (par exemple, werf.io/v1.0-beta).

Pour gĂ©nĂ©rer une version spĂ©cifique du site, il suffit en gĂ©nĂ©ral de le compiler avec Jekyll, en exĂ©cutant dans le rĂ©pertoire /docs du dĂ©pĂŽt werf la commande correspondante (jekyll build), aprĂšs s'ĂȘtre prĂ©alablement configurĂ© sur le Git-tag de la version nĂ©cessaire.

Il ne reste plus qu'Ă  ajouter que :

  • pour la compilation, on utilise l'outil lui-mĂȘme (werf) ;
  • les processus CI/CD sont basĂ©s sur GitLab CI ;
  • et tout cela fonctionne, bien sĂ»r, dans Kubernetes.

Objectifs

Formulons maintenant les tùches en tenant compte de toute cette spécificité :

  1. AprĂšs le changement de version de werf sur n'importe quel canal de mise Ă  jour, la documentation sur le site doit se mettre Ă  jour automatiquement..
  2. Pour le dĂ©veloppement, il doit parfois ĂȘtre possible de consulter les versions prĂ©liminaires du site..

La recompilation du site doit ĂȘtre effectuĂ©e aprĂšs le changement de version sur n'importe quel canal Ă  partir des Git-tags correspondants, mais pendant le processus de construction de l'image, nous obtiendrons les particularitĂ©s suivantes :

  • Étant donnĂ© que la liste des versions sur les canaux change, il est nĂ©cessaire de ne recompter que la documentation pour les canaux oĂč la version a changĂ©. Recompiler tout de nouveau n'est pas trĂšs Ă©lĂ©gant.
  • L'ensemble des canaux de release peut changer. À un moment donnĂ©, par exemple, il peut ne pas y avoir de version sur des canaux plus stables que la release early-access 1.1, mais au fil du temps, elles apparaĂźtront — ne pas changer la construction Ă  la main dans ce cas ?

Il en résulte qu'un seul image provenant d'un registre lent peut bloquer le déploiement la construction dépend de données externes changeantes.

Mise en Ɠuvre

Choix de l'approche

Comme option, on peut lancer chaque version nécessaire sur un pod séparé dans Kubernetes. Cette option implique un plus grand nombre d'objets dans le cluster, qui augmentera avec le nombre croissant de releases stables de werf. Cela entraßne à son tour une maintenance plus complexe : chaque version a son propre serveur HTTP, avec une charge par faible. Bien sûr, cela entraßne également des coûts en ressources plus élevés.

Nous avons cependant optĂ© pour la compilation de toutes les versions nĂ©cessaires dans une seule image. La static compilĂ©e de toutes les versions du site se trouve dans un conteneur NGINX, et le trafic vers le Deployment correspondant passe par NGINX Ingress. Cette structure simple — application sans Ă©tat — permet une scalabilitĂ© facile du Deployment (en fonction de la charge) grĂące aux moyens de Kubernetes lui-mĂȘme.

Pour ĂȘtre plus prĂ©cis, nous gĂ©nĂ©rons deux images : l'une pour l'environnement de production, l'autre pour l'environnement de dĂ©veloppement. L'image supplĂ©mentaire est utilisĂ©e (lancĂ©e) uniquement dans l'environnement de dĂ©veloppement en parallĂšle avec la principale et contient la version du site issue du commit de rĂ©vision, tandis que le routage entre elles est effectuĂ© Ă  l'aide des ressources Ingress.

werf vs git clone et artefacts

Comme mentionné précédemment, pour générer la version statique du site pour une version spécifique de la documentation, il faut effectuer une compilation en basculant vers la balise correspondante du dépÎt. On pourrait également le faire en clonant le dépÎt chaque fois qu'une compilation est effectuée, en choisissant les balises appropriées selon une liste. Cependant, cela est une opération assez gourmande en ressources et nécessite également la rédaction d'instructions non triviales... Un autre inconvénient majeur est que cette approche ne permet pas de mettre quoi que ce soit en cache pendant la compilation.

Ici, nous avons le soutien de l'outil werf qui réalise le caching intelligent et permet d'utiliser des dépÎts externes. L'utilisation de werf pour ajouter du code provenant du dépÎt accélérera considérablement la compilation, car werf clone essentiellement le dépÎt une fois, puis exécute uniquement fetch si nécessaire. De plus, lors de l'ajout de données depuis le dépÎt, nous pouvons sélectionner uniquement les répertoires nécessaires (dans notre cas, c'est le dossier docs), ce qui réduit considérablement le volume des données ajoutées.

Puisque Jekyll est un outil destinĂ© Ă  la compilation de sites statiques et qu'il n'est pas nĂ©cessaire dans l'image finale, il serait logique de rĂ©aliser la compilation dans l'artefact werf, et d'importer dans l'image finale seulement le rĂ©sultat de la compilation Écrivons werf.yaml.

Donc, nous avons décidé de compiler chaque version dans un artefact werf séparé. Cependant, nous

ne savons pas combien d'artefacts cela fera lors de la compilation , donc nous ne pouvons pas écrire une configuration de compilation fixe (strictement parlant, nous le pouvons, mais ce ne sera pas trÚs efficace).werf permet d'utiliser

des modÚles Go dans son fichier de configuration ( ), ce qui permet dewerf.yamlgénérer la configuration « à la volée » en fonction des données externes (ce qui est nécessaire !). Les données externes dans notre cas sont les informations sur les versions et les releases, sur la base desquelles nous assemblons le nombre nécessaire d'artefacts et obtenons en résultat deux images : werf-doc werf-dev et pour un déploiement sur différents environnements. pour le lancement sur différents contournements.

Les données externes sont transmises via des variables d'environnement. Voici leur composition :

  • PUBLICATIONS — une chaĂźne contenant la liste des publications et leur version actuelle correspondante de werf, sous la forme d'une liste sĂ©parĂ©e par des espaces avec le format %. Exemple : 1.0%v1.0.4-beta.20
  • CANAUX — une chaĂźne contenant la liste des canaux et leur version actuelle correspondante de werf, sous la forme d'une liste sĂ©parĂ©e par des espaces avec le format %. Exemple : 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • VERSION_DE_BASE — la version de la publication werf Ă  afficher par dĂ©faut sur le site (il n'est pas toujours nĂ©cessaire d'afficher la documentation pour le numĂ©ro de publication le plus Ă©levĂ©). Exemple : v1.0.4-beta.20
  • HASH_DE_REVIEW — le hash du commit de review Ă  partir duquel la version doit ĂȘtre construite pour le pipeline de test.

Ces variables seront peuplées dans le pipeline GitLab CI, et comment cela se fera est expliqué ci-dessous.

Tout d'abord, pour plus de commodité, nous allons définir dans werf.yaml les variables de modÚle Go, en leur attribuant des valeurs provenant des variables d'environnement :

{{ $_ := set . "WerfVersions" (cat (env "CANAUX") (env "PUBLICATIONS") | splitList " ") }}
{{ $Root := . }}
{{ $_ := set . "WerfRootVersion" (env "VERSION_DE_BASE") }}
{{ $_ := set . "WerfReviewCommit" (env "HASH_DE_REVIEW") }}

La description de l'artefact pour la compilation de la version statique du site est globalement la mĂȘme pour tous les cas nĂ©cessaires (y compris, la gĂ©nĂ©ration de la version racine et de la version pour le pipeline dev). Nous allons donc le dĂ©placer dans un bloc sĂ©parĂ© Ă  l'aide de la fonction define — pour une rĂ©utilisation ultĂ©rieure via include. Le modĂšle recevra les arguments suivants :

  • Version — la version gĂ©nĂ©rĂ©e (nom de la balise) ;
  • Channel — le nom du canal de mise Ă  jour pour lequel l'artefact est gĂ©nĂ©rĂ© ;
  • Commit — le hash du commit, si l'artefact est gĂ©nĂ©rĂ© pour un commit de review ;
  • contexte.

Description du modĂšle d'artefact

{{- 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: "Installer les Dépendances"
    shell: bundle install
    args:
      executable: /bin/bash
      chdir: /app/docs
  beforeSetup:
{{- if .Commit }}
  - shell: echo "Revoir 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/fr_site/
  - file:
      dest: /app/docs/pages_fr/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/_fr_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/app/docs/_config_fr.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 }}

Le nom de l'artefact doit ĂȘtre unique. Nous pouvons y parvenir, par exemple, en ajoutant le nom du canal (valeur de la variable .Channel) comme suffixe du nom de l'artefact : artifact: doc-{{ .Channel }}. Mais il est important de comprendre que lors de l'importation Ă  partir des artefacts, il faudra se rĂ©fĂ©rer Ă  ces mĂȘmes noms.

Lors de la description de l'artefact, il existe une fonctionnalité de werf appelée montage. Le montage en spécifiant le répertoire de service build_dir permet de conserver le cache de Jekyll entre les exécutions du pipeline, ce qui accélÚre considérablement la reconstruction.

Vous avez Ă©galement peut-ĂȘtre remarquĂ© l'utilisation du fichier releases.yml — il s'agit d'un fichier YAML contenant des donnĂ©es sur les versions, demandĂ© lors de github.com (artefact obtenu lors de l'exĂ©cution du pipeline). Il est nĂ©cessaire lors de la compilation du site, mais dans le contexte de cet article, il est intĂ©ressant de noter que son Ă©tat influence la reconstruction d'un seul artefact — l'artefact de la version racine du site (il n'est pas nĂ©cessaire dans d'autres artefacts).

Cela est réalisé à l'aide d'un opérateur conditionnel if dans le modÚle Go et de la construction {{ $Root.Files.Get "releases.yml" | sha256sum }} dans l'étape stade. Cela fonctionne de la maniÚre suivante : lors de la création de l'artefact pour la version racine (la variable .Channel est égale à root) le hachage du fichier releases.yml influence la signature de toute la phase, car il fait partie du nom de la tùche Ansible (paramÚtre nom). Ainsi, lorsqu'il y a un changement de contenu, fichiers releases.yml l'artefact correspondant sera reconstruit.

Notez également le travail avec le dépÎt externe. Dans l'image de l'artefact du dépÎt werf, seul le répertoire est ajouté, /docset selon les paramÚtres passés, les données du tag ou du commit de révision nécessaires sont ajoutées immédiatement.

Pour utiliser le modÚle d'artefact afin de générer la description de l'artefact des versions et des canaux transmis, organisons une boucle sur la variable .WerfVersions dans werf.yaml:

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

Comme la boucle gĂ©nĂ©rera plusieurs artefacts (nous l'espĂ©rons), il faut prendre en compte le sĂ©parateur entre eux — la sĂ©quence --- (voir plus sur la syntaxe du fichier de configuration dans documentation). Comme nous l'avons dĂ©terminĂ© prĂ©cĂ©demment, lors de l'appel du modĂšle dans la boucle, nous passons les paramĂštres de version, l'URL et le contexte racine.

De maniÚre similaire, mais sans boucle, nous appelons le modÚle d'artefact pour les « cas particuliers » : pour la version racine, ainsi que pour la version du commit de révision :

{{ 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 }}

Remarque : l'artefact pour le commit de révision ne sera construit que si la variable .WerfReviewCommit.

Les artefacts sont prĂȘts — il est temps de s'occuper de l'importation !

L'image finale, destinĂ©e Ă  ĂȘtre exĂ©cutĂ©e dans Kubernetes, est un NGINX standard, auquel est ajoutĂ© un fichier de configuration de serveur nginx.conf et la partie statique de l'artefact. En plus de l'artefact de la version racine du site, nous devons rĂ©pĂ©ter la boucle sur la variable .WerfVersions pour importer les artefacts des versions et des canaux, tout en respectant la rĂšgle de nommage des artefacts que nous avons adoptĂ©e prĂ©cĂ©demment. Puisque chaque artefact conserve les versions du site pour deux langues, nous les importons dans les emplacements prĂ©vus par la configuration.

Description de l'image finale werf-doc

image: werf-doc
from: nginx:stable-alpine
ansible:
  setup:
  - name: "Setup /etc/nginx/nginx.conf"
    copy:
      content: |
{{ .Files.Get ".werf/nginx.conf" | indent 8 }}
      dest: /etc/nginx/nginx.conf
  - file:
      path: "{{`{{ item }}`}}"
      state: directory
      mode: 0777
    with_items:
    - /app/main_site/assets
    - /app/ru_site/assets
import:
- artifact: doc-root
  add: /app/_main_site
  to: /app/main_site
  before: setup
- artifact: doc-root
  add: /app/_ru_site
  to: /app/ru_site
  before: setup
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ $Channel := $VersionsDict._0 -}}
{{ $Version := $VersionsDict._1 -}}
- artifact: doc-{{ $Channel }}
  add: /app/_main_site
  to: /app/main_site/v{{ $Channel }}
  before: setup
{{ end -}}
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ $Channel := $VersionsDict._0 -}}
{{ $Version := $VersionsDict._1 -}}
- artifact: doc-{{ $Channel }}
  add: /app/_ru_site
  to: /app/ru_site/v{{ $Channel }}
  before: setup
{{ end -}}

Une image supplĂ©mentaire qui s'exĂ©cute avec l'image principale sur le contour de dĂ©veloppement contient uniquement deux versions du site : la version du commit de rĂ©vision et la version racine du site (oĂč se trouvent les assets communs et, si vous vous en souvenez, les donnĂ©es de version). Ainsi, l'image supplĂ©mentaire diffĂšre de l'image principale uniquement par la section d'importation (et bien sĂ»r, le nom) :

image: werf-dev
...
import:
- artifact: doc-root
  add: /app/_main_site
  to: /app/main_site
  before: setup
- artifact: doc-root
  add: /app/_ru_site
  to: /app/ru_site
  before: setup
{{- if .WerfReviewCommit  }}
- artifact: doc-review
  add: /app/_main_site
  to: /app/main_site/review
  before: setup
- artifact: doc-review
  add: /app/_ru_site
  to: /app/ru_site/review
  before: setup
{{- end }}

Comme mentionnĂ© prĂ©cĂ©demment, l'artĂ©fact pour le commit de rĂ©vision ne sera gĂ©nĂ©rĂ© que lors de l'exĂ©cution de la variable d'environnement configurĂ©e. HASH_DE_REVIEW. On aurait pu ne pas gĂ©nĂ©rer l'image werf-dev, s'il n'y a pas de variable d'environnement HASH_DE_REVIEW, mais pour que le nettoyage selon les politiques des images Docker dans werf fonctionne pour l'image werf-dev, nous laisserons celle-ci ĂȘtre construite uniquement avec l'artĂ©fact de la version racine (de toute façon, elle est dĂ©jĂ  construite), pour simplifier la structure du pipeline.

La construction est prĂȘte ! Passons au CI/CD et aux dĂ©tails importants.

Le pipeline dans GitLab CI et les particularités de la construction dynamique

Lors du démarrage de la construction, nous devons configurer les variables d'environnement utilisées dans werf.yaml. Cela ne concerne pas la variable REVIEW_SHA, que nous allons configurer lors de l'appel du pipeline à partir du webhook GitHub.

La génération des données externes nécessaires sera effectuée dans un script Bash generate_artifacts, qui générera deux artefacts pour le pipeline GitLab :

  • fichier releases.yml avec des informations sur les versions,
  • fichier common_envs.sh, contenant les variables d'environnement pour l'exportation.

Contenu du fichier generate_artifacts que vous trouverez dans notre dĂ©pĂŽt d'exemples. L'obtention des donnĂ©es elle-mĂȘme n'est pas le sujet de l'article, mais le fichier common_envs.sh est important pour nous, car il affecte le fonctionnement de werf. Exemple de son contenu :

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'

Vous pouvez utiliser la sortie d'un tel script, par exemple, avec une fonction Bash source.

Et maintenant, la partie la plus intéressante. Pour que la construction et le déploiement de l'application fonctionnent correctement, il est nécessaire de faire en sorte que werf.yaml était identique au moins dans le cadre d'un pipeline. Si cette condition n'est pas remplie, les signatures des étapes, que werf calcule lors de la construction et, par exemple, lors du déploiement, seront différentes. Cela entraßnera une erreur de déploiement, car l'image requise pour le déploiement sera absente.

En d'autres termes, si pendant la construction de l'image du site les informations sur les versions et releases sont identiques, mais qu'au moment du déploiement une nouvelle version sort et que les variables d'environnement prennent d'autres valeurs, alors le déploiement échouera : l'artefact de la nouvelle version n'est pas encore construit.

Si la gĂ©nĂ©ration werf.yaml dĂ©pend de donnĂ©es externes (par exemple, d'une liste de versions actuelles, comme dans notre cas), alors la composition et les valeurs de ces donnĂ©es doivent ĂȘtre fixĂ©es dans le cadre du pipeline. Cela est particuliĂšrement important si les paramĂštres externes changent assez souvent.

Nous allons obtenir et fixer des donnĂ©es externes au cours de la premiĂšre Ă©tape du pipeline dans GitLab (Prebuild) et les transmettre ensuite sous forme de d'artefact GitLab CI. Cela permettra de lancer et de relancer les tĂąches du pipeline (construction, dĂ©ploiement, nettoyage) avec la mĂȘme configuration dans werf.yaml.

Le contenu de l'étape Prebuild fichiers .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 weeks

Ayant fixĂ© les donnĂ©es externes dans l'artefact, nous pouvons effectuer la construction et le dĂ©ploiement, en utilisant les Ă©tapes standard du pipeline GitLab CI : Build et Deploy. Nous lançons le pipeline via des webhooks Ă  partir du dĂ©pĂŽt GitHub de werf (c'est-Ă -dire lors de changements dans le dĂ©pĂŽt GitHub). Les donnĂ©es pour cela peuvent ĂȘtre trouvĂ©es dans les propriĂ©tĂ©s du projet GitLab dans la section CI / CD Settings -> Pipeline triggers, puis nous crĂ©erons dans GitHub un Webhook correspondant (Settings -> Webhooks).

L'étape de construction ressemblera à ceci :

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 ajoutera dans l'Ă©tape de construction deux artefacts de l'Ă©tape Prebuild, donc nous exportons les variables avec les donnĂ©es d'entrĂ©e prĂ©parĂ©es Ă  l'aide de la construction source common_envs.sh. Nous lançons l’étape de construction dans tous les cas, sauf pour le dĂ©marrage du pipeline programmĂ©. Un pipeline sera lancĂ© pour le nettoyage - il n'est pas nĂ©cessaire d’exĂ©cuter la construction dans ce cas.

À l’étape de dĂ©ploiement, nous allons dĂ©crire deux tĂąches - sĂ©parĂ©ment pour le dĂ©ploiement vers les environnements de production et de dĂ©veloppement, en utilisant un modĂšle 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

Déployer en Production :
  <<: *base_deploy
  variables:
    WERF_KUBE_CONTEXT: prod
  environment:
    name: production
    url: werf.io
  only:
    refs:
      - master
  except:
    variables:
      - $REVIEW_SHA
    refs:
      - schedules

Déployer en Test :
  <<: *base_deploy
  variables:
    WERF_KUBE_CONTEXT: dev
  environment:
    name: test
    url: werf.test.flant.com
  except:
    refs:
      - schedules
  only:
    variables:
      - $REVIEW_SHA

Les tĂąches diffĂšrent essentiellement par la spĂ©cification du contexte du cluster dans lequel werf doit effectuer le dĂ©ploiement (WERF_KUBE_CONTEXT), et par la dĂ©finition des variables d'environnement pour le contour (environment.name et environment.url), qui sont ensuite utilisĂ©es dans les modĂšles de Helm chart. Nous ne fournirons pas le contenu des modĂšles car il n’y a rien d’intĂ©ressant pour le sujet en question, mais vous pouvez les trouver dans le dĂ©pĂŽt liĂ© Ă  l’article.

DerniĂšre touche

Étant donnĂ© que les versions de werf sortent assez frĂ©quemment, de nouvelles images seront souvent construites, et le registre Docker continuera Ă  croĂźtre. Il est donc impĂ©ratif de configurer un nettoyage automatique des images selon des politiques. C'est trĂšs simple Ă  mettre en Ɠuvre.

Pour cela, il faudra :

  • Ajouter une Ă©tape de nettoyage dans .gitlab-ci.yml;
  • Ajouter l'exĂ©cution pĂ©riodique de la tĂąche de nettoyage ;
  • Configurer une variable d'environnement avec le token d'accĂšs en Ă©criture.

Nous ajoutons une étape de nettoyage dans .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

Nous avons dĂ©jĂ  vu presque tout cela un peu plus haut - juste pour le nettoyage, il est nĂ©cessaire de se connecter au registre Docker avec un token ayant des droits pour supprimer des images dans le registre Docker (le token gĂ©nĂ©rĂ© automatiquement pour les tĂąches GitLab CI n'a pas ces droits). Il faut obtenir le token Ă  l’avance dans GitLab et en spĂ©cifier la valeur dans la variable d'environnement WERF_IMAGES_CLEANUP_PASSWORD du projet (CI/CD Settings -> Variables).

L'ajout de la tĂąche de nettoyage avec le programme requis se fait dans CI/CD ->
Schedules
.

Tout : le projet dans Docker Registry ne va plus croßtre constamment à cause des images inutilisées.

En conclusion de la partie pratique, je rappelle que les listings complets de l'article sont disponibles dans Git:

Résultat

  1. Nous avons obtenu une structure de construction logique : un artefact pour une version.
  2. La construction est universelle et n'exige pas de modifications manuelles lors de la sortie de nouvelles versions de werf : la documentation sur le site est mise Ă  jour automatiquement.
  3. Deux images sont construites pour différents contours.
  4. Cela fonctionne rapidement, car le caching est utilisé au maximum : lors de la sortie d'une nouvelle version de werf ou d'un appel du webhook GitHub pour le commit de revue, seul l'artefact correspondant à la version modifiée est reconstruit.
  5. Il n'est pas nécessaire de penser à la suppression des images inutilisées : le nettoyage selon les politiques de werf maintiendra l'ordre dans Docker Registry.

Conclusions

  • L'utilisation de werf permet Ă  la construction d'ĂȘtre rapide grĂące Ă  la mise en cache de la construction elle-mĂȘme ainsi qu'Ă  la mise en cache lors du travail avec des dĂ©pĂŽts externes.
  • Travailler avec des dĂ©pĂŽts Git externes Ă©vite de devoir cloner le dĂ©pĂŽt en entier Ă  chaque fois ou d'inventer la roue avec une logique d'optimisation compliquĂ©e. werf utilise le cache et ne clone qu'une seule fois, puis utilise fetch et uniquement si nĂ©cessaire.
  • La possibilitĂ© d'utiliser des modĂšles Go dans le fichier de configuration de la construction werf.yaml permet de dĂ©crire une construction dont le rĂ©sultat dĂ©pend de donnĂ©es externes.
  • L'utilisation du montage dans werf accĂ©lĂšre considĂ©rablement la construction des artefacts - grĂące Ă  un cache qui est commun Ă  tous les pipelines.
  • werf permet de configurer facilement le nettoyage, ce qui est particuliĂšrement pertinent lors de constructions dynamiques.

P.S.

Lisez aussi dans notre blog :

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster