Ndërtim dinamik dhe vendosje e imazheve Docker me werf në shembullin e një siti të dokumentacionit me versione

Ne kemi folur tashmĂ« pĂ«r mjetin tonĂ« GitOps werf, dhe kĂ«tĂ« herĂ« do tĂ« dojmĂ« tĂ« ndajmĂ« pĂ«rvojĂ«n e ndĂ«rtimit tĂ« faqes sĂ« internetit me dokumentacionin e projektit vetĂ« — werf.io (versioni i tij nĂ« gjuhĂ«n ruse — ru.werf.io). Kjo Ă«shtĂ« njĂ« faqe statike e zakonshme, megjithatĂ« ndĂ«rtimi i saj Ă«shtĂ« interesant sepse Ă«shtĂ« realizuar me njĂ« numĂ«r dinamik artefaktesh.

Ndërtim dinamik dhe vendosje e imazheve Docker me werf në shembullin e një siti të dokumentacionit me versione

Nuk do tĂ« thellohemi nĂ« nuancat e strukturĂ«s sĂ« faqes: gjenerimin e menusĂ« sĂ« pĂ«rbashkĂ«t pĂ«r tĂ« gjitha versionet, faqet me informacion mbi lĂ«shimet etj. — do tĂ« qĂ«ndrojmĂ« mĂ« mirĂ« te pyetjet dhe veçoritĂ« e ndĂ«rtimit dinamik dhe pak mbi proceset pĂ«rkatĂ«se CI/CD.

Hyrja: si është ndërtuar faqeja

Të fillojmë me faktin se dokumentacioni për werf ruhet së bashku me kodin e tij. Kjo kërkon disa kërkesa për zhvillimin, që në përgjithësi dalin jashtë përmbajtjes së këtij artikulli, por të paktën mund të thuhet se:

  • Funksionet e reja tĂ« werf nuk duhet tĂ« publikohen pa rinovimin e dokumentacionit dhe, anasjelltas, çdo ndryshim nĂ« dokumentacion nĂ«nkupton lĂ«shimin e njĂ« versioni tĂ« ri tĂ« werf;
  • Projekti ka zhvillim tĂ« intensifikuar: versionet e reja mund tĂ« lĂ«shohen disa herĂ« nĂ« ditĂ«;
  • Çdo operacion manual pĂ«r ndarjen e faqes me versionin e ri tĂ« dokumentacionit Ă«shtĂ« tĂ« paktĂ«n i lodhshĂ«m;
  • NĂ« projekt Ă«shtĂ« pranuar qasja e versionimit semanik, me 5 kanale stabiliteti. Procesi i lĂ«shimit nĂ«nkupton kalimin e versioneve pĂ«rmes kanaleve nĂ« renditjen e rritjes sĂ« stabilitetit: nga alpha deri nĂ« rock-solid;
  • Faqja ka njĂ« version nĂ« gjuhĂ«n ruse, i cili "jeton dhe zhvillohet" (dmth. pĂ«rmbajtja e saj pĂ«rditĂ«sohet) paralelisht me versionin kryesor (dmth. versionin nĂ« anglisht).

PĂ«r tĂ« fshehur nga pĂ«rdoruesi tĂ« gjithĂ« kĂ«tĂ« "kuzhinĂ« tĂ« brendshme", duke i ofruar atij atĂ« qĂ« "funksionon thjesht", ne krijuam njĂ« mjet tĂ« veçantĂ« pĂ«r instalimin dhe pĂ«rditĂ«simin e werf — Ă«shtĂ« multiwerf. Mjafton tĂ« tregoni numrin e lĂ«shimit dhe kanalin e stabilitetit qĂ« jeni tĂ« gatshĂ«m tĂ« pĂ«rdorni, dhe multiwerf do tĂ« kontrollojĂ« nĂ«se ka njĂ« version tĂ« ri nĂ« kanal dhe do ta shkarkojĂ« atĂ« nĂ« rast nevoje.

NĂ« menunĂ« e zgjedhjes sĂ« versioneve nĂ« faqe janĂ« tĂ« disponueshme versionet mĂ« tĂ« fundit tĂ« werf nĂ« çdo kanal. NĂ« mĂ«nyrĂ« qĂ«, adresa werf.io/documentation hap versionin e kanalit mĂ« tĂ« stabilizuar pĂ«r lĂ«shimin mĂ« tĂ« fundit — ai gjithashtu indeksohet nga motorĂ«t e kĂ«rkimit. Dokumentacioni pĂ«r kanalin Ă«shtĂ« i disponueshĂ«m nĂ« adresa tĂ« veçanta (pĂ«r shembull, werf.io/v1.0-beta/documentation pĂ«r lĂ«shimin beta 1.0).

Në përfundim, faqja e internetit është e disponueshme në versionet e mëposhtme:

  1. root (hapet si parazgjedhje),
  2. për çdo kanal aktiv të azhurnimeve për çdo version (p.sh., werf.io/v1.0-beta).

Për të gjeneruar një version specifik të faqes së internetit, zakonisht mjafton të kryhet kompilimi i tij me ndihmën e Jekyll, duke ekzekutuar në katalogun /docs e depozitës werf komandën përkatëse (jekyll build), pas kalimit në Git-tag-un e versionit të nevojshëm.

Nuk mbetet tjetër veçse të shtohet se:

  • pĂ«r ndĂ«rtim pĂ«rdoret vetĂ« utilitari (werf);
  • proceset CI/CD janĂ« tĂ« ndĂ«rtuara mbi bazĂ«n e GitLab CI;
  • dhe, sigurisht, gjithĂ« kjo funksionon nĂ« Kubernetes.

Detyrat

Tani do të formulojmë detyrat, duke marrë parasysh të gjitha këto karakteristika të përshkruara:

  1. Pas ndërrimit të versionit të werf në çdo kanal azhurnimi dokumentacioni në faqen e internetit duhet të azhurnohet automatikisht.
  2. Për zhvillim, duhet të kemi mundësinë ndonjëherë të shqyrtojmë versionet paraprak të faqes.

Rikompilimi i faqes duhet të kryhet pas ndërrimit të versionit në çdo kanal nga Git-tagat përkatëse, por në procesin e ndërtimit të imazhit do të marrë formën e mëposhtme:

  • Duke marrĂ« parasysh se lista e versioneve nĂ« kanale ndryshon, duhet tĂ« rikompilohet vetĂ«m dokumentacioni pĂ«r kanalet ku ka ndryshuar versioni. Sepse rikompilimi i gjithçkaje nga e para nuk Ă«shtĂ« shumĂ« i bukur.
  • VetĂ« grupi i kanaleve pĂ«r lĂ«shimet mund tĂ« ndryshojĂ«. NĂ« njĂ« moment, pĂ«r shembull, mund tĂ« mos ketĂ« version nĂ« kanalet mĂ« tĂ« qĂ«ndrueshme se lĂ«shimi i aksesit tĂ« hershĂ«m 1.1, por me kalimin e kohĂ«s ato do tĂ« shfaqen — a duhet ta ndryshojmĂ« ndĂ«rtimin manualisht nĂ« kĂ«tĂ« rast?

Kështu që ndërtimi varet nga të dhëna të jashtme në ndryshim.

Implementimi

Zgjedhja e qasjes

Si një opsion, mund të ekzekutojmë çdo version të nevojshëm si një pod të veçantë në Kubernetes. Ky opsion parashikon një numër më të madh objektesh në klaster, i cili do të rritet me rritjen e numrit të lëshimeve stabile të werf. Dhe kjo nga ana e saj parashikon një mirëmbajtje më të komplikuar: për çdo version ka një server HTTP të vetin, madje me ngarkesë të vogël. Sigurisht, kjo sjell gjithashtu dhe kosto më të larta për burimet.

Ne kemi shkuar nĂ« rrugĂ«n e ndĂ«rtimit tĂ« tĂ« gjitha versioneve tĂ« nevojshme nĂ« njĂ« imazh. Statika e kompiliuar e tĂ« gjitha versioneve tĂ« faqes Ă«shtĂ« nĂ« kontejnerin me NGINX, dhe trafiku nĂ« pĂ«rkatĂ«shin Deployment vjen pĂ«rmes NGINX Ingress. StrukturĂ« e thjeshtĂ« — aplikacion pa gjendje — lejon tĂ« lehtĂ«sojĂ« skalimin e Deployment-it (nĂ« pĂ«rputhje me ngarkesĂ«n) me ndihmĂ«n e Kubernetes-it.

Duke të qenë më të saktë, ne mbledhim dy pamje: një për konturin e prodhimit, dhe një tjetër, për konturin dev. Pamja shtesë përdoret (nisët) vetëm në konturin dev së bashku me të parën dhe përmban versionin e faqes nga komiti i rishikimit, ndërsa routing-u midis tyre bëhet përmes burimeve Ingress.

werf vs git clone dhe artefakte

Siç u pĂ«rmend, pĂ«r tĂ« gjeneruar statikĂ«n e faqes pĂ«r njĂ« version tĂ« caktuar tĂ« dokumentacionit, Ă«shtĂ« e nevojshme tĂ« kryeni njĂ« ndĂ«rtim, duke u kaluar nĂ« etiketĂ«n pĂ«rkatĂ«se tĂ« depozitĂ«s. Do tĂ« ishte e mundur ta bĂ«nit kĂ«tĂ« duke klonuar depozitĂ«n çdo herĂ« gjatĂ« ndĂ«rtimit, duke zgjedhur etiketat pĂ«rkatĂ«se nga lista. MegjithatĂ«, kjo Ă«shtĂ« njĂ« operacion mjaft i burimeve dhe, pĂ«r mĂ« tepĂ«r, kĂ«rkon shkrimin e udhĂ«zimeve tĂ« ndĂ«rlikuara
 NjĂ« minus tjetĂ«r i rĂ«ndĂ«sishĂ«m Ă«shtĂ« se me kĂ«tĂ« qasje nuk ka mundĂ«si pĂ«r tĂ« bĂ«rĂ« ndonjĂ« keĆĄim gjatĂ« ndĂ«rtimit.

Këtu na vjen në ndihmë vetë utilitari werf, i cili implementon cache të mençur dhe lejon të përdorim depozita të jashtme. Përdorimi i werf për të shtuar kod nga depozita do ta përshpejtojë ndërtimin, sepse werf në thelb e klonon depozitën një herë dhe pastaj kryen të fetch kur është e nevojshme. Përveç kësaj, gjatë shtimit të të dhënave nga depozita mund të zgjedhim vetëm direktoritë e nevojshme (në rastin tonë është katalogu docs), e cila do të ulë ndjeshëm volumin e të dhënave të shtuar.

Duke qenë se Jekyll është një mjet i destinuar për kompilimin e statikës dhe nuk është e nevojshme në pamjen përfundimtare, do ishte logjike të kryhej kompilimi në artefaktin werf, ndërsa në pamjen përfundimtare të importohet vetëm rezultati i kompilimit.

Shkruajmë werf.yaml

Pra, ne u përcaktuam që do të kompilojmë çdo version në një artefakt të veçantë werf. Megjithatë, ne nuk e dimë se sa artefakte do të ketë gjatë ndërtimit, prandaj nuk mund të shkruajmë një konfiguratë të fiksuar të ndërtimit (në të vërtetë mund të bëjmë, por nuk do të jetë efikase).

werf lejon përdorimin e shablloneve Go në dosjen e tij të konfigurimit (werf.yaml), dhe kjo jep mundësinë për të gjeneruar konfigurimin "në flakë" në varësi të të dhënave të jashtme (ato që na nevojiten!). Të dhënat e jashtme në rastin tonë janë informacioni mbi versionet dhe lëshimet, mbi të cilin ne ndërtuam numrin e nevojshëm të artefakteve dhe morëm në rezultat dy pamje: werf-doc dhe werf-dev për të nisur në konture të ndryshme.

Të dhënat e jashtme dërgohen nëpërmjet variablave të mjedisit. Këtu është përbërja e tyre:

  • RELEASES — njĂ« rrjesht me listĂ«n e lĂ«shimeve dhe versionit aktual pĂ«rkatĂ«s tĂ« werf, nĂ« formĂ«n e njĂ« liste pĂ«rmes hapĂ«sirĂ«s sĂ« vlerave nĂ« formatin %. Shembull: 1.0%v1.0.4-beta.20
  • CHANNELS — njĂ« rrjesht me listĂ«n e kanaleve dhe versionit aktual pĂ«rkatĂ«s tĂ« werf, nĂ« formĂ«n e njĂ« liste pĂ«rmes hapĂ«sirĂ«s sĂ« vlerave nĂ« formatin %. Shembull: 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • ROOT_VERSION — versioni i lĂ«shimit tĂ« werf pĂ«r t'u shfaqur si parazgjedhje nĂ« faqe (nuk Ă«shtĂ« gjithmonĂ« e nevojshme tĂ« shfaqet dokumentacioni pĂ«r numrin mĂ« tĂ« lartĂ« tĂ« lĂ«shimit). Shembuj: v1.0.4-beta.20
  • REVIEW_SHA — hash i commit-it tĂ« rishikimit, nga i cili duhet tĂ« ndĂ«rtohet versioni pĂ«r konturin testues.

KĂ«ta variabla do tĂ« plotĂ«sohen nĂ« pipeline-in GitLab CI, dhe si pikĂ«risht — Ă«shtĂ« shkruar mĂ« poshtĂ«.

Së pari, për lehtësi, do të përcaktojmë në werf.yaml variablat e Go-shablloneve, duke u dhënë atyre vlera nga variablat e mjedisit:

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

PĂ«rshkrimi i artefaktit pĂ«r kompilimin e statikĂ«s sĂ« versionit tĂ« faqes Ă«shtĂ« nĂ« thelb i njĂ«jtĂ« pĂ«r tĂ« gjitha rastet qĂ« na nevojiten (pĂ«rfshirĂ«, gjenerimin e versionit themelor, si dhe versionin pĂ«r konturin dev). Prandaj, do ta nxjerrim atĂ« nĂ« njĂ« bllok tĂ« veçantĂ« me ndihmĂ«n e funksionit define — pĂ«r ripĂ«rdorim tĂ« mĂ«tejshĂ«m me ndihmĂ«n e include. Shabllonit do t'i kalojmĂ« argumentĂ«t e mĂ«poshtĂ«m:

  • Version — versionin e gjeneruar (emri i etiketĂ«s);
  • Channel — emri i kanaleve tĂ« azhurnimit, pĂ«r tĂ« cilin gjenerohet artefakti;
  • Commit — hash i commit-it, nĂ«se artefakti gjenerohet pĂ«r commit-in e rishikimit;
  • konteksti.

Përshkrimi i shabllonit të artefaktit

{{- 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: "Instalo Mjetet"
    shell: bundle install
    args:
      executable: /bin/bash
      chdir: /app/docs
  beforeSetup:
{{- if .Commit }}
  - shell: echo "Rishikoni 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/sq_site/
  - file:
      dest: /app/docs/pages_sq/cli
      state: link
      src: /app/docs/pages/cli
  - shell: |
      echo -e "werfVersion: {{ .Version }}
werfChannel: {{ .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/_sq_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/app/docs/_config_sq.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 }}

Emri i artefaktit duhet të jetë unik. Ne mund ta arrijmë këtë, për shembull, duke shtuar emrin e kanalit (vlera e variablit .Channel) si një sufiks të emrit të artefaktit: artifact: doc-{{ .Channel }}. Por duhet të kuptojmë se gjatë importit nga artefaktet, do të duhet të referohemi në emra të tillë.

Kur përshkruhet artefakti, përdoret një mundësi e tillë e werf si montimi. Montimi me specifikimin e katalogut të shërbimit build_dir lejon ruajtjen e caches së Jekyll midis rasteve të pipeline-it, çka e përshpejton ndjeshëm rindërtimin.

Po ashtu, mund tĂ« keni vĂ«nĂ« re pĂ«rdorimin e skedarit releases.yml — ky Ă«shtĂ« njĂ« skedar YAML me tĂ« dhĂ«nat mbi lĂ«shimet, i kĂ«rkuar nga github.com (artefakti, i marrĂ« nga ekzekutimi i pipeline-it). Ai Ă«shtĂ« i nevojshĂ«m gjatĂ« kompilimit tĂ« faqes, por nĂ« kontekstin e kĂ«tij artikulli, na intereson se nga gjendja e tij varet rindĂ«rtimi i vetĂ«m njĂ« artefakti — artefakti i versionit rrĂ«njor (nĂ« artefakte tĂ« tjera nuk Ă«shtĂ« i nevojshĂ«m).

Kjo realizohet nëpërmjet operatorit kusht të nëse shablloneve Go dhe konstrukcionit {{ $Root.Files.Get "releases.yml" | sha256sum }} në fazën stadium. Kjo funksionon kështu: kur është në proces ndërtimi artefakti për versionin rrënjor (variabla .Channel është root) hash-i i skedarit releases.yml ka ndikim në nënshkrimin e të gjithë fazës, pasi përbën një komponent të emrit të punës Ansible (parametri emri). Kështu, kur ndryshon përmbajtja skedare releases.yml artefakti përkatës do të rindërtohet.

Kujdes gjithashtu me punën me repositorin e jashtëm. Në imazhin e artefaktit nga repoja werf, shtohet vetëm katalogu /docs, dhe në varësi të parametrave të kaluar, të dhënat e nevojshme të tags ose komiteteve të rishikimit shtohen menjëherë.

Për të përdorur modelin e artefaktit për të gjeneruar përshkrimin e artefaktit për versionet e kanaleve dhe rreleaseve të kaluara, organizojmë një cikël mbi variablën .WerfVersions në werf.yaml:

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

Dhe pĂ«r shkak se cikli do tĂ« gjenerojĂ« disa artefakte (shpresojmĂ« tĂ« jetĂ« kĂ«shtu), Ă«shtĂ« e nevojshme tĂ« merren parasysh ndarĂ«sit midis tyre — sekuenca --- (pĂ«r mĂ« shumĂ« rreth sintaksĂ«s sĂ« skedarit tĂ« konfigurimit shihni nĂ« dokumentacionin). Siç u pĂ«rcaktua mĂ« parĂ«, kur thĂ«rrasim modelin nĂ« cikĂ«l, ne kalojmĂ« parametrat e versionit, URL-nĂ« dhe kontekstin rrĂ«njor.

Njësoj, por tashmë pa cikël, thërrasim modelin e artefaktit për «rastet speciale»: për versionin rrënjor, si dhe versionin nga komiteti i rishikimit:

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

Kujdesi, se artefakti për komitetin e rishikimit do të ndërtohet vetëm në rast se është vendosur variabla .WerfReviewCommit.

Artefaktet janĂ« gati — Ă«shtĂ« koha pĂ«r t'u marrĂ« me importimin!

Imazhi përfundimtar, i destinuar për të funksionuar në Kubernetes, përbën një NGINX të zakonshëm, në të cilin është shtuar skedari i konfigurimit të serverit nginx.conf dhe statika nga artefaktet. Përveç artefaktit të versionit rrënjor të faqes, ne kemi nevojë të përsërisim ciklin mbi variablën .WerfVersions për importimin e artefakteve të versioneve të kanaleve dhe rreleaseve + të respektojmë rregullin e emërtesës së artefakteve që kemi pranuar më parë. Duke qenë se çdo artefakt ruan versionet e faqes për dy gjuhë, i importojmë ato në vendet e paraqitura nga konfigurimi.

Përshkrimi i imazhit përfundimtar 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 -}}

Një imazh shtesë, i cili së bashku me të parin nis në kontur dev, përmban vetëm dy versione të faqes: versionin nga commit-i i shqyrtimit dhe versionin rrënjësor të faqes (aty janë asetet e përbashkëta dhe, nëse e mbani mend, të dhënat për lëshimet). Prandaj, imazhi shtesë do të dallojë nga i pari vetëm për seksionin e importit (dhe, sigurisht, emri):

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

Siç u vërejt më parë, artefakti për commit-in e shqyrtimit do të gjenerohet vetëm kur variabli i mjedisit të jetë i vendosur. REVIEW_SHA. Mund të mos e gjeneronim fare imazhin werf-dev, nëse nuk ka variabël të mjedisit. REVIEW_SHA, por për të heqja sipas politikave të imazheve Docker në werf do të funksiononte për imazhin werf-dev, do ta lëmë atë të ndërtohet vetëm me artefaktin e versionit rrënjor (pavarësisht se ai është tashmë ndërtuar), për të thjeshtuar strukturën e pipeline-it.

Ndërtimi është përfunduar! Tani kalojmë te CI/CD dhe detajet e rëndësishme.

Pipeline në GitLab CI dhe veçoritë e ndërtimit dinamik.

Kur fillojmë ndërtimin, është e nevojshme të vendosim variablat e mjedisit që përdoren në werf.yaml. Kjo nuk i përket variablit REVIEW_SHA, të cilin do ta vendosim kur thërrasim pipeline-in nga hook-u i GitHub.

Formimin e të dhënave të nevojshme do ta hedhim në një skenë Bash generate_artifacts, i cili do të gjenerojë dy artefakte për pipeline-in GitLab:

  • skeda releases.yml me tĂ« dhĂ«nat pĂ«r lĂ«shimet,
  • skeda common_envs.sh, qĂ« pĂ«rmban variablat e mjedisit pĂ«r eksport.

Përmbajtja e skedarit generate_artifacts do t'i gjeni në repo-n tonë me shembuj. Marrja e të dhënave nuk është subjekti i artikullit, por skedari common_envs.sh na intereson, pasi prej tij varet funksionimi i werf. Një shembull i përmbajtjes së tij:

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'

Implementimi i një skenari të tillë mund të bëhet, për shembull, me një funksion në Bash source.

Tani vjen pjesa më interesante. Që ndërtimi dhe implementimi i aplikacionit të funksionojnë siç duhet, është e nevojshme që werf.yaml ishte të njëjtë të paktën brenda një pipeline. Nëse ky kusht nuk përmbushet, atëherë firmat e fazave që werf llogarit gjatë ndërtimit dhe, për shembull, implementimit, do të jenë të ndryshme. Kjo do të çojë në një gabim të implementimit, sepse imazhi i nevojshëm për implementim do të mungojë.

Me fjalë të tjera, nëse gjatë ndërtimit të imazhit të faqes informacioni mbi versionet dhe rishikimet është njësoj, ndërsa në momentin e implementimit lëshohet një version i ri dhe variablat e mjedisit kanë vlera të tjera, atëherë implementimi do të përfundojë me gabim: sepse artefakti i versionit të ri ende nuk është ndërtuar.

Nëse krijimi werf.yaml varet nga të dhëna të jashtme (për shembull, lista e versioneve të duhura, si në rastin tonë), atëherë përmbajtja dhe vlerat e këtyre të dhënave duhet të regjistrohen brenda pipeline. Kjo është veçanërisht e rëndësishme, nëse parametrat e jashtëm ndryshojnë mjaft shpesh.

Ne do të marrim dhe regjistrojmë të dhëna të jashtme në fazën e parë të pipeline në GitLab (Prebuild) dhe do t'i kalojmë ato më tej si artefakte GitLab CI. Kjo do të mundësojë që të ekzekutojmë dhe rishikojmë detyrat e pipeline-it (ndërtim, implementim, pastrim) me konfigurim të njëjtë në werf.yaml.

Përmbajtja e fazës Prebuild skedare .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

Pas regjistrimit të të dhënave të jashtme në artefakt, është e mundur të ekzekutohet ndërtimi dhe implementimi, duke përdorur fazat standarde të pipeline-it GitLab CI: Build dhe Deploy. Ne startojmë pipeline-in përmes hook-eve nga depozitës GitHub të werf (dmth. gjatë ndryshimeve në depozitën në GitHub). Të dhënat për to mund të merren në pronat e projektit në GitLab në seksionin CI / CD Settings -> Pipeline triggers, pastaj do të krijojmë në GitHub Webhook-un përkatës (Settings -> Webhooks).

Faza e ndërtimit do të duket si më poshtë:

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 do t'i shtojĂ« dy artefakte nĂ« fazĂ«n e ndĂ«rtimit nga faza Prebuild, kĂ«shtu qĂ« ne do tĂ« eksportojmĂ« variablat me tĂ« dhĂ«nat e pĂ«rgatitura pĂ«r hyrje pĂ«rmes strukturĂ«s source common_envs.sh. AktivizojmĂ« fazĂ«n e ndĂ«rtimit nĂ« tĂ« gjitha rastet, pĂ«rveç aktivizimit tĂ« pipeline sipas orarit. Sipas orarit, do tĂ« aktivizohet pipeline pĂ«r pastrimin — nuk Ă«shtĂ« e nevojshme tĂ« kryhet ndĂ«rtimi nĂ« kĂ«tĂ« rast.

NĂ« fazĂ«n e deploy-it do tĂ« pĂ«rshkruajmĂ« dy detyra — veçmas pĂ«r deploy nĂ« prodhim- dhe dev-contours, duke pĂ«rdorur njĂ« shabllon 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

Detyrat në thelb vetëm dallohen nga specifikimi i kontekstit të klasterit në të cilin werf duhet të kryejë deploy (WERF_KUBE_CONTEXT), dhe vendosja e variablave të ambientit (environment.name dhe environment.url), të cilat përdoren pastaj në shabllonat e Helm-chart. Nuk do të paraqesim përmbajtjen e shablloneve, pasi nuk ka asgjë interesante për temën e shqyrtuar, por mund t'i gjeni ato në repozitimin e artikullit.

Përfundimi i fundit

Duke pasur parasysh se versionet e werf dalin mjaft shpesh, do tĂ« krijohen shpesh edhe imazhe tĂ« reja, dhe Docker Registry do tĂ« caktohet nĂ« rritje tĂ« vazhdueshme. Prandaj Ă«shtĂ« e domosdoshme tĂ« konfigurohet pastrimi automatik i imazheve sipas politikave. ËshtĂ« shumĂ« e thjeshtĂ« ta bĂ«sh kĂ«tĂ«.

Për realizimin e kësaj do të nevojitet:

  • Shtoni njĂ« fazĂ« pastrimi nĂ« .gitlab-ci.yml;
  • Shtoni njĂ« ekzekutim periodik tĂ« detyrĂ«s pĂ«r pastrim;
  • Konfiguroni njĂ« variabĂ«l ambienti me token e aksesit pĂ«r shkrim.

Shtojmë fazën e pastrimit 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

Ajo qĂ« kemi parĂ« tashmĂ« mĂ« lart — vetĂ«m pĂ«r pastrim Ă«shtĂ« e nevojshme tĂ« autorizohemi paraprakisht nĂ« Docker Registry me njĂ« token qĂ« ka tĂ« drejta pĂ«r tĂ« fshirĂ« imazhet nĂ« Docker Registry (tokeni i lĂ«shuar automatikisht pĂ«r detyrat GitLab CI nuk ka kĂ«to tĂ« drejta). Tokeni duhet tĂ« ruhet nĂ« GitLab mĂ« parĂ« dhe tĂ« tregoni vlerĂ«n e tij nĂ« variablin e ambientit WERF_IMAGES_CLEANUP_PASSWORD i projektit (CI/CD Settings -> Variables).

Shtimi i një detyre pastrimi me një orar të nevojshëm bëhet në CI/CD ->
Oraret
.

Të gjitha: projekti në Docker Registry nuk do të rritet më për shkak të imazheve të papërdorura.

Në përfundim të pjesës praktike, do t'ju rikujtoja se listimet e plota nga artikulli janë të disponueshme në Git:

Rezultati

  1. Kemi marrë një strukturë logjike ndërtimi: një artefakt për një version.
  2. Ndërtimi është universale dhe nuk kërkon ndryshime manuale kur dalin versionet e reja të werf: dokumentacioni në faqe përditësohet automatikisht.
  3. Ndërtohen dy imazhe për konturet e ndryshme.
  4. Funksionon shpejt, pasi maksimalisht përdor caching - kur dalin versionet e reja të werf ose kur thirret GitHub webhook për review-commit - rinndërtohet vetëm artefakti përkatës me versionin e ndryshuar.
  5. Nuk është nevoja të mendoni për fshirjen e imazheve të papërdorura: pastrimi sipas politikave të werf do të mbajë rregull në Docker Registry.

Përfundimet

  • PĂ«rdorimi i werf lejon qĂ« ndĂ«rtimi tĂ« funksionojĂ« shpejt falĂ« caching-ut si tĂ« ndĂ«rtimit ashtu edhe caching-ut gjatĂ« punĂ«s me depo tĂ« jashtme.
  • Puna me depo tĂ« jashtme Git e shmang nevojĂ«n pĂ«r tĂ« klonuar sĂ«rish depozitĂ«n çdo herĂ« plotĂ«sisht ose pĂ«r tĂ« izmjĂ«kuar me logjikĂ«n e optimizimit. werf pĂ«rdor caching dhe bĂ«n klonimin vetĂ«m njĂ« herĂ«, pastaj pĂ«rdor fetch dhe vetĂ«m sipas nevojĂ«s.
  • MundĂ«sia e pĂ«rdorimit tĂ« шабlonĂ«ve Go nĂ« dosjen e konfigurimit tĂ« ndĂ«rtimit werf.yaml lejon tĂ« pĂ«rshkruhet ndĂ«rtimi, rezultati i tĂ« cilit varet nga tĂ« dhĂ«na tĂ« jashtme.
  • PĂ«rdorimi i montimit nĂ« werf pĂ«rshpejton ndĂ«rtimin e artefakteve nĂ« mĂ«nyrĂ« tĂ« konsiderueshme - falĂ« cache-it qĂ« Ă«shtĂ« i pĂ«rbashkĂ«t pĂ«r tĂ« gjitha pipeline-t.
  • werf lejon lehtĂ«sisht tĂ« konfigurohet pastrimi, qĂ« Ă«shtĂ« veçanĂ«risht i rĂ«ndĂ«sishĂ«m gjatĂ« ndĂ«rtimit dinamike.

P.S.

Lexoni gjithashtu në blogun tonë:

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster