Construirea dinamică și implementarea imaginilor Docker cu werf prin exemplul unui site de documentație versiuni.

Am vorbit deja de mai multe ori despre instrumentul nostru GitOps werf, iar de această dată ne-am dori să împărtășim experiența construirii site-ului cu documentația proiectului în sine — werf.io (versiunea sa în limba română — ro.werf.io). Este un site static obișnuit, însă construirea sa este interesantă prin faptul că utilizează un număr dinamic de artefacte.

Construirea dinamică și implementarea imaginilor Docker cu werf prin exemplul unui site de documentație versiuni.

Nu ne vom adânci în detaliile structurii site-ului: generarea meniului comun pentru toate versiunile, pagina cu informații despre lansări etc. — ci ne vom concentra pe întrebările și particularitățile construirii dinamice și puțin pe procesele asociate CI/CD.

Introducere: cum este structurat site-ul

Vom începe prin a spune că documentația pentru werf este stocată împreună cu codul său. Acest lucru impune anumite cerințe de dezvoltare, care în general depășesc cadrul acestui articol, însă cel puțin putem spune că:

  • Noile funcții werf nu ar trebui să fie lansate fără actualizarea documentației și, invers, orice modificări în documentație implică o nouă versiune werf;
  • Proiectul are o dezvoltare destul de intensivă: noi versiuni pot fi lansate de câteva ori pe zi;
  • Orice operațiuni manuale de desfășurare a site-ului cu noua versiune a documentației sunt cel puțin obositoare;
  • În proiect a fost adoptat un model semnificativ versiunea, cu 5 canale de stabilitate. Procesul de lansare implică trecerea secvențială a versiunilor prin canale în ordinea creșterii stabilității: de la alpha la rock-solid;
  • Site-ul are o versiune în limba română, care «trăiește și se dezvoltă» (adică conținutul său este actualizat) paralel cu versiunea principală (adică cea în limba engleză).

Pentru a ascunde utilizatorului toată această «cucina interioară», oferindu-i ceea ce «functionează pur și simplu», am creat un instrument separat pentru instalarea și actualizarea werf — acesta este multiwerf. Este suficient să specificați numărul versiunii și canalul de stabilitate pe care sunteți dispus să-l utilizați, iar multiwerf va verifica dacă există o versiune nouă pe canal și o va descărca dacă este necesar.

În meniul de selecție a versiunilor de pe site sunt disponibile ultimele versiuni werf din fiecare canal. În mod implicit, la adresa werf.io/documentation se deschide versiunea celei mai stabile canale pentru ultima lansare — care, de asemenea, este indexată de motoarele de căutare. Documentația pentru canal este disponibilă la adrese separate (de exemplu, werf.io/v1.0-beta/documentation pentru lansarea beta 1.0).

În total, site-ul oferă următoarele versiuni:

  1. rădăcină (se deschide implicit),
  2. pentru fiecare canal activ de actualizări al fiecărei versiuni (de exemplu, werf.io/v1.0-beta).

Pentru a genera o versiune specifică a site-ului, în general, este suficient să îi efectuezi compilarea folosind Jekyll, rulând în directorul /docs repository-ului werf comanda corespunzătoare (jekyll build), făcând mai întâi comutarea pe Git-tagul versiunii necesare.

Rămâne de adăugat că:

  • pentru compilare se folosește utilitarul însuși (werf);
  • procesele CI/CD sunt construite pe baza GitLab CI;
  • și toate acestea, desigur, funcționează în Kubernetes.

Sarcini

Acum să formulăm sarcinile care iau în considerare toată specificația descrisă:

  1. După schimbarea versiunii werf pe orice canal de actualizări documentația de pe site trebuie să se actualizeze automat.
  2. Pentru dezvoltare este necesar să avem uneori posibilitatea de a vizualiza versiunile preliminare ale site-ului.

Recompilarea site-ului trebuie efectuată după schimbarea versiunii pe orice canal din tagurile Git corespunzătoare, dar în procesul de construire a imaginii, vom obține următoarele trăsături:

  • Deoarece lista versiunilor pe canale se schimbă, trebuie recompilată doar documentația pentru canalele unde s-a schimbat versiunea. Nu este foarte elegant să recompilăm totul din nou.
  • Însuși setul de canale pentru versiuni poate varia. La un moment dat, de exemplu, poate să nu existe o versiune pe canalele mai stabilizate ale versiunii early-access 1.1, dar cu timpul acestea vor apărea — nu-i așa că nu ar trebui să schimbăm manual compilarea în acest caz?

Deci, rezultă că compilarea depinde de date externe în schimbare.

Implementarea

Alegerea abordării

Ca opțiune, putem rula fiecare versiune necesară ca un pod separat în Kubernetes. Această opțiune implică un număr mai mare de obiecte în cluster, care va crește odată cu creșterea numărului de versiuni stabile werf. Și aceasta, la rândul său, implică întreținere mai complexă: pentru fiecare versiune apare propriul server HTTP, cu o sarcină mică. Desigur, aceasta implică și cheltuieli mai mari de resurse.

Noi am ales calea compilării tuturor versiunilor necesare într-o singură imagine. Statica compilată a tuturor versiunilor site-ului se află într-un container cu NGINX, iar traficul către Deployment-ul corespunzător vine prin NGINX Ingress. Structura simplă — aplicație stateless — permite scalarea ușoară a Deployment-ului (în funcție de sarcină) folosind Kubernetes.

Mai precis, noi generăm două imagini: una pentru mediul de producție, iar a doua, suplimentară, pentru mediul de dezvoltare. Imaginea suplimentară este utilizată (pornită) doar în mediu de dezvoltare împreună cu cea principală și conține versiunea site-ului din comit-ul de revizuire, iar rutarea între ele se face prin resurse Ingress.

werf vs git clone și artefacte

După cum am menționat, pentru a genera statica site-ului pentru o versiune specifică a documentației, trebuie să facem o compilare, schimbându-ne în eticheta corespunzătoare a repository-ului. Am putea face acest lucru prin clonarea repository-ului de fiecare dată când compilăm, alegând etichetele corespunzătoare din listă. Totuși, aceasta este o operațiune destul de consumatoare de resurse și, în plus, necesită scrierea de instrucțiuni non-triviale... Un alt dezavantaj major este că, folosind această abordare, nu avem posibilitatea de a face caching în timpul compilării.

Aici ne vine în ajutor utilitarul werf, care implementează caching inteligent și permite utilizarea repository-urilor externe. Utilizarea werf pentru a adăuga cod din repository va accelera semnificativ compilarea, deoarece werf, de fapt, clonează repository-ul o singură dată și apoi efectuează doar fetch cât este necesar. În plus, atunci când adăugăm date din repository, putem selecta doar directoarele necesare (în cazul nostru, acesta este catalogul docs), ceea ce reduce semnificativ volumul de date adăugate.

Deoarece Jekyll este un instrument destinat compilării statice și nu este necesar în imaginea finală, ar fi logic să efectuezi compilarea în artefactul werf, iar în imaginea finală să importăm doar rezultatul compilării.

Scriem werf.yaml

Deci, ne-am decis că vom compila fiecare versiune într-un artefact werf separat. Totuși, noi nu știm câte dintre aceste artefacte vor fi la compilare, așa că nu putem scrie o configurație fixă de compilare (strict vorbind, putem, dar nu va fi foarte eficient).

werf permite utilizarea șabloanelor Go în fișierul său de configurare (werf.yaml), iar aceasta oferă posibilitatea de a genera configurarea «în timp real» în funcție de datele externe (exact ce este necesar!). Datele externe în cazul nostru sunt informațiile despre versiuni și lansări, pe baza cărora generăm numărul necesar de artefacte și obținem în rezultat două imagini: werf-doc și werf-dev pentru a rula pe diferite medii.

Datele externe sunt transmise prin variabile de mediu. Iată compunerea lor:

  • VERSIUNI — un șir cu lista versiunilor și versiunea curentă a werf, sub formă de listă cu valori separate prin spațiu în formatul %. Exemplu: 1.0%v1.0.4-beta.20
  • CANALE — un șir cu lista canalelor și versiunea curentă a werf pentru acestea, sub formă de listă cu valori separate prin spațiu în formatul %. Exemplu: 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • VERSIUNE_RĂDĂCINĂ — versiunea de lansare a werf care va fi afișată implicit pe site (nu este întotdeauna necesar să afișăm documentația pentru cel mai înalt număr de versiune). Exemplu: v1.0.4-beta.20
  • REVIZIE_SHA — hash-ul reviziei commit-ului, din care trebuie să construim versiunea pentru mediu de testare.

Aceste variabile vor fi completate în pipeline-ul GitLab CI, iar cum anume — este descris mai jos.

Mai întâi, pentru comoditate, vom defini în werf.yaml variabilele șablonului Go, atribuindu-le valori din variabilele de mediu:

{{ $_ := set . "WerfVersions" (cat (env "CANALE") (env "VERSIUNI") | splitList " ") }}
{{ $Root := . }}
{{ $_ := set . "WerfRootVersion" (env "VERSIUNE_RĂDĂCINĂ") }}
{{ $_ := set . "WerfReviewCommit" (env "REVIZIE_SHA") }}

Descrierea artefactului pentru compilarea statică a versiunii site-ului este în mare parte aceeași pentru toate cazurile necesare (inclusiv generarea versiunii rădăcină, precum și versiunea pentru mediu de dezvoltare). Prin urmare, o vom extrage într-un bloc separat folosind funcția define — pentru reutilizarea ulterioară cu ajutorul include. Șablonului îi vom transmite următoarele argumente:

  • Version — versiunea generată (numele etichetei);
  • Canal — numele canalului de actualizări pentru care se generează artefactul;
  • Commit — hash-ul commit-ului, dacă artefactul este generat pentru un commit de revizuire;
  • context.

Descrierea șablonului artefactului

{{- 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: "Instalare Dependențe"
    shell: bundle install
    args:
      executable: /bin/bash
      chdir: /app/docs
  beforeSetup:
{{- if .Commit }}
  - shell: echo "Revizuire 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/ro_site/
  - file:
      dest: /app/docs/pages_ro/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/_ro_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/app/docs/_config_ro.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 }}

Numele artefactului trebuie să fie unic. Putem atinge acest lucru, de exemplu, adăugând numele canalului (valoarea variabilei .Channel) ca sufix al numelui artefactului: artifact: doc-{{ .Channel }}. Dar trebuie să înțelegem că, în timpul importului din artefacte, va trebui să facem referire la aceleași nume.

În descrierea artefactului se folosește o capacitate a werf, și anume montarea. Montarea cu indicarea directorului special build_dir permete salvarea cache-ului Jekyll între execuțiile pipeline-ului, ceea ce accelerează semnificativ reconstrucția.

De asemenea, ați putut observa utilizarea fișierului releases.yml — acesta este un fișier YAML cu date despre versiuni, solicitat de github.com (artefact obținut în timpul execuției pipeline-ului). Acesta este necesar la compilarea site-ului, dar în contextul acestui articol ne interesează de faptul că starea sa influențează reconstrucția unui singur artefact — artefactul site-ului versiunii principale (în celelalte artefacte acesta nu este necesar).

Acest lucru este realizat cu ajutorul operatorului condițional if al șabloanelor Go și a construcției {{ $Root.Files.Get "releases.yml" | sha256sum }} în etapa stadii. Aceasta funcționează în felul următor: atunci când se construiește un artifact pentru versiunea de bază (varaiblele .Channel egală cu root) hash-ul fișierului releases.yml afectează semnătura întregii etape, deoarece este o componentă a numelui sarcinii Ansible (parametrul name). Astfel, în cazul schimbării conținutului fișiere. releases.yml artifactul corespunzător va fi reconstruit.

De asemenea, aveți în vedere lucrul cu un repo extern. În imaginea artifactului din repo-ul werf, se adaugă doar catalogul /docs, în funcție de parametrii transmiși, se adaugă datele etichetei necesare sau ale commit-ului de revizuire.

Pentru a utiliza șablonul artifactului pentru a genera descrierea artifactului pentru versiunile canalelor și lansărilor transmise, organizăm un ciclu pe variabila .WerfVersions în werf.yaml:

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

Deoarece ciclu va genera mai multe artefacte (sperăm la asta), trebuie să luăm în considerare separatorul dintre ele — secvența --- (mai multe detalii privind sintaxa fișierului de configurare găsiți în documentation). Așa cum am stabilit anterior, atunci când apelăm șablonul în ciclu, transmitem parametrii versiunii, URL și contextul de bază.

În mod similar, dar fără ciclu, apelăm șablonul artifactului pentru „cazuri speciale”: pentru versiunea de bază, precum și versiunea din commit-ul de revizuire:

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

Rețineți că artifactul pentru commit-ul de revizuire va fi construit doar dacă variabila .WerfReviewCommit.

Artefactele sunt pregătite — e timpul să ne ocupăm de import!

Imaginea finală, destinată pentru rularea în Kubernetes, reprezintă un NGINX obișnuit, la care s-a adăugat un fișier de configurare a serverului nginx.conf și statica din artefacte. Pe lângă artifactul versiunii de bază a site-ului, trebuie să repetăm ciclu pe variabila .WerfVersions pentru importul artefactelor versiunilor canalelor și lansărilor + respectarea regulii de numire a artefactelor pe care am acceptat-o anterior. Deoarece fiecare artifact păstrează versiunile site-ului pentru două limbi, le importăm în locurile prevăzute de configurație.

Descrierea imaginii finale werf-doc

imagine: 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\/ro_site\/assets
import:
- artifact: doc-root
  add: \/app\/_main_site
  to: \/app\/main_site
  before: setup
- artifact: doc-root
  add: \/app\/_ro_site
  to: \/app\/ro_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\/_ro_site
  to: \/app\/ro_site\/v{{ $Channel }}
  before: setup
{{ end -}}

Imaginea suplimentară, care funcționează împreună cu imaginea principală pe contul dev, conține doar două versiuni ale site-ului: versiunea din commit-ul de revizuire și versiunea rădăcină a site-ului (acolo se află activele comune și, dacă vă amintiți, datele despre lansări). Astfel, imaginea suplimentară va diferi de imaginea principală doar prin secțiunea de import (și, desigur, numele):

imagine: werf-dev
...
import:
- artifact: doc-root
  add: \/app\/_main_site
  to: \/app\/main_site
  before: setup
- artifact: doc-root
  add: \/app\/_ro_site
  to: \/app\/ro_site
  before: setup
{{- if .WerfReviewCommit  }}
- artifact: doc-review
  add: \/app\/_main_site
  to: \/app\/main_site\/review
  before: setup
- artifact: doc-review
  add: \/app\/_ro_site
  to: \/app\/ro_site\/review
  before: setup
{{- end }}

După cum s-a observat mai sus, artefactul pentru commit-ul de revizuire va fi generat doar în timpul activării variabilei de mediu specificată. REVIZIE_SHANu ar fi fost necesar să generăm imaginea werf-dev, dacă nu ar fi fost variabila de mediu. REVIZIE_SHA, dar pentru ca curățarea pe politici Imaginii Docker în werf să funcționeze pentru imaginea werf-dev, o vom lăsa să fie construită doar cu artefactul versiunii rădăcină (oricum este deja construită), pentru simplificarea structurii pipeline-ului.

Construcția este gata! Să trecem la CI\/CD și aspectele importante.

Pipeline-ul în GitLab CI și caracteristicile construcției dinamice

La începutul construcției, este necesar să setăm variabilele de mediu utilizate în werf.yaml. Aceasta nu se referă la variabila REVIEW_SHA, pe care o vom stabili la invocarea pipeline-ului din webhook-ul GitHub.

Formarea datelor externe necesare va fi mutată în scriptul Bash generate_artifacts, care va genera două artefacte pentru pipeline-ul GitLab:

  • fișier releases.yml cu date despre lansări,
  • fișier common_envs.sh, care conține variabile de mediu pentru export.

Conținutul fișierului generate_artifacts veți găsi în repositoriul nostru cu exemple. Obținerea datelor nu este subiectul acestui articol, dar fișierul common_envs.sh ne este important, deoarece depinde de el funcționarea werf. Exemplul conținutului său:

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'

Rezultatul unui astfel de script poate fi utilizat, de exemplu, cu ajutorul unei funcții Bash source.

Și acum vine partea interesantă. Pentru ca atât compilarea, cât și implementarea aplicației să funcționeze corect, trebuie să ne asigurăm că werf.yaml a fost identic de cel puțin în cadrul aceluiași pipeline. Dacă această condiție nu este îndeplinită, semnăturile etapelor pe care le calculează werf în timpul compilării și, de exemplu, implementării, vor fi diferite. Acest lucru va duce la o eroare de implementare, deoarece imaginea necesară pentru implementare va lipsi.

Cu alte cuvinte, dacă în timpul construirii imaginii site-ului informațiile despre versiuni și actualizări sunt una, iar în momentul implementării iese o nouă versiune și variabilele de mediu au alte valori, atunci implementarea se va finaliza cu o eroare: deoarece artefactul noii versiuni nu a fost încă construit.

Dacă generarea werf.yaml depinde de date externe (de exemplu, de lista versiunilor actuale, așa cum este cazul nostru), atunci compunerea și valorile acestor date trebuie să fie fixate în cadrul pipeline-ului. Acest lucru este deosebit de important dacă parametrii externi se schimbă destul de des.

Vom obține și fixa date externe în prima etapă a pipeline-ului în GitLab (Prebuild) și le vom transmite mai departe sub formă de artefact GitLab CI. Acest lucru va permite rularea și reluarea sarcinilor pipeline-ului (compilare, implementare, curățare) cu o configurație identică în werf.yaml.

Conținutul etapei Prebuild fișiere. .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

Fixând datele externe în artefact, putem efectua compilarea și implementarea folosind etapele standard ale pipeline-ului GitLab CI: Build și Deploy. Pipeline-ul noi îl lansăm prin webhook-uri din depozitul GitHub werf (adică atunci când au loc modificări în depozitul de pe GitHub). Datele pentru acestea pot fi obținute din proprietățile proiectului GitLab în secțiunea CI / CD Settings -> Pipeline triggers, iar apoi vom crea în GitHub webhook-ul corespunzător (Settings -> Webhooks).

Etapa de construire va arăta astfel:

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 va adăuga în etapa de construire două artefacte din etapă Prebuild, astfel încât să exportăm variabilele cu datele de intrare pregătite folosind construcția source common_envs.sh. Începem etapa de construire în toate cazurile, cu excepția lansării pipeline-ului conform programului. Conform programului, va fi lansat un pipeline pentru curățare — nu este necesară construirea în acest caz.

În etapa de deploy, vom descrie două sarcini — separat pentru deploy pe contururile de producție și dev, folosind un șablon 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

Deploy to Production:
  <<: *base_deploy
  variables:
    WERF_KUBE_CONTEXT: prod
  environment:
    name: production
    url: werf.io
  only:
    refs:
      - master
  except:
    variables:
      - $REVIEW_SHA
    refs:
      - schedules

Deploy to Test:
  <<: *base_deploy
  variables:
    WERF_KUBE_CONTEXT: dev
  environment:
    name: test
    url: werf.test.flant.com
  except:
    refs:
      - schedules
  only:
    variables:
      - $REVIEW_SHA

Sarcinile se deosebesc, în esență, doar prin specificarea contextului cluster-ului în care werf trebuie să efectueze deploy (WERF_KUBE_CONTEXT), și prin setarea variabilelor de mediu pentru contur (environment.name și environment.url), care sunt folosite ulterior în șabloanele Helm-chart-ului. Nu vom include conținutul șabloanelor, deoarece nu există nimic interesant pentru tema discutată, dar le puteți găsi în repositoriul articolului.

Ultimul retuș

Având în vedere că versiunile werf apar destul de frecvent, vor fi construite noi imagini, iar Docker Registry va continua să crească. Prin urmare, este esențial să configurăm curățarea automată a imaginilor conform politicilor. Este foarte simplu de realizat.

Pentru implementare, este necesar:

  • Adăugați o etapă de curățare în .gitlab-ci.yml;
  • Adăugați execuția periodică a sarcinii de curățare;
  • Configurați o variabilă de mediu cu token-ul de acces pentru scriere.

Adăugăm etapa de curățare în .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

Aproape tot ce am văzut deja puțin mai sus — doar că pentru curățare este necesar să se autentifice în prealabil în Docker Registry cu un token care are drepturi de ștergere a imaginilor în Docker Registry (token-ul generat automat pentru job-urile GitLab CI nu are aceste drepturi). Token-ul trebuie să fie configurat în GitLab anterior și valoarea sa trebuie specificată în variabila de mediu WERF_IMAGES_CLEANUP_PASSWORD proiectului (CI/CD Settings -> Variables).

Adăugarea sarcinii de curățare cu programul necesar se efectuează în CI/CD ->
Schedules
.

Totul: proiectul din Docker Registry nu va mai crește constant din cauza imaginilor nesolicitate.

La finalul părții practice, amintesc că listele complete din articol sunt disponibile în Git:

Rezultatul

  1. Am obținut o structură logică de compilare: un artefact pentru fiecare versiune.
  2. Compilarea este universală și nu necesită modificări manuale la lansarea unor noi versiuni werf: documentația de pe site se actualizează automat.
  3. Se compilează două imagini pentru diferite contour-uri.
  4. Funcționează rapid, deoarece folosește la maximum cache-ul - la lansarea unei noi versiuni werf sau la apelarea hook-ului GitHub pentru un commit de revizuire - se recompilează doar artefactul corespunzător cu versiunea modificată.
  5. Nu trebuie să te gândești la ștergerea imaginilor nesolicitate: curățarea conform politicilor werf va menține ordinea în Docker Registry.

Conclusions

  • Utilizarea werf permite compilarea rapidă datorită caching-ului atât pentru compilare, cât și pentru activitatea cu repository-uri externe.
  • Lucrul cu repository-uri externe Git elimină necesitatea de a clona întotdeauna repository-ul complet sau de a reinventa roata cu o logică optimizată complicată. werf folosește cache și clonați doar o dată, apoi folosește fetch și doar după necesitate.
  • Posibilitatea utilizării șabloanelor Go în fișierul de configurare a compilării werf.yaml permite descrierea unei compilări al cărei rezultat depinde de date externe.
  • Utilizarea montării în werf accelerează semnificativ compilarea artefactelor - datorită cache-ului, care este comun pentru toate pipeline-urile.
  • werf permite configurarea ușoară a curățării, ceea ce este deosebit de relevant în cazul compilării dinamice.

P.S.

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster