Дынамічная зборка і дэплой Docker-вобразаў з werf на прыкладзе сайта версіяванай дакументацыі.

Мы ўжо не раз расказвалі пра свой GitOps-інструмент werf, а ў гэты раз хацелі б падзяліцца досведам зборкі сайта з дакументацыяй самога праекту. werf.io (яго рускамоўная версія - by.werf.io). Гэта звычайны статычны сайт, аднак яго зборка цікавая тым, што пабудавана з выкарыстаннем дынамічнай колькасці артэфактаў.

Дынамічная зборка і дэплой Docker-вобразаў з werf на прыкладзе сайта версіяванай дакументацыі.

Удавацца ў нюансы структуры сайта: генерацыю агульнага меню для ўсіх версій, старонкі з інфармацыяй аб рэлізах і да т.п. - не будзем. Замест гэтага, сфакусуемся на пытаннях і асаблівасцях дынамічнай зборкі і крыху на спадарожных працэсах CI/CD.

Увядзенне: як уладкованы сайт

Пачнём з таго, што дакументацыя па werf захоўваецца разам з яго кодам. Гэта прад'яўляе пэўныя патрабаванні да распрацоўкі, якія ў цэлым выходзяць за рамкі гэтага артыкула, але як мінімум можна сказаць, што:

  • Новыя функцыі werf не павінны выходзіць без абнаўлення дакументацыі і, наадварот, якія-небудзь змены ў дакументацыі маюць на ўвазе выйсце новай версіі werf;
  • У праекту даволі інтэнсіўная распрацоўка: новыя версіі могуць выходзіць некалькі разоў у дзень;
  • Якія-небудзь ручныя аперацыі па дэплоі сайта з новай версіяй дакументацыі як мінімум стомныя;
  • У праекце прыняты падыход семантычнага версіявання, з 5-ю каналамі стабільнасці. Рэлізны працэс мае на ўвазе паслядоўнае праходжанне версій па каналах у парадку павышэння стабільнасці: ад alpha да rock-solid;
  • У сайта ёсць рускамоўная версія, якая "жыве і развіваецца" (г.зн. кантэнт якой абнаўляецца) паралельна з асноўнай (г.зн. англамоўнай) версіяй.

Каб схаваць ад карыстальніка ўсю гэтую "ўнутраную кухню", прапанаваўшы яму тое, што "проста працуе", мы зрабілі асобны інструмент ўстаноўкі і абнаўлення werf - Гэта multiwerf. Дастаткова паказаць нумар рэлізу і канал стабільнасці, які вы гатовы выкарыстоўваць, а multiwerf праверыць, ці ёсць новая версія на канале, і запампуе яе пры неабходнасці.

У меню выбару версій на сайце даступны апошнія версіі werf у кожным канале. Па змаўчанні, па адрасе werf.io/documentation адкрываецца версія найбольш стабільнага канала для апошняга рэлізу - яна ж індэксуецца пошукавікамі. Дакументацыя для канала даступна па асобных адрасах (напрыклад, werf.io/v1.0-beta/documentation для beta-рэлізу 1.0).

Разам у сайта даступныя наступныя версіі:

  1. каранёвая (адкрываецца па змаўчанні),
  2. для кожнага актыўнага канала абнаўленняў кожнага рэлізу (напрыклад, werf.io/v1.0-beta).

Для генерацыі канкрэтнай версіі сайта ў агульным выпадку дастаткова выканаць яго кампіляцыю сродкамі Джэкіл, запусціўшы ў каталогу /docs рэпазітара werf адпаведную каманду (jekyll build), папярэдне пераключыўшыся на Git-тэг неабходнай версіі.

Застаецца толькі дадаць, што:

  • для зборкі выкарыстоўваецца сама ўтыліта (werf);
  • CI/CD-працэсы пабудаваны на базе GitLab CI;
  • і ўсё гэта, вядома, працуе ў Kubernetes.

задачы

Цяпер сфармулюем задачы, якія ўлічваюць усю апісаную спецыфіку:

  1. Пасля змены версіі werf на любым канале абнаўленняў дакументацыя на сайце павінна аўтаматычна абнаўляцца.
  2. Для распрацоўкі трэба мець магчымасць часам праглядаць папярэднія версіі сайта.

Перакампіляванне сайта неабходна выконваць пасля змены версіі на любым канале з адпаведных Git-тэгаў, але падчас зборкі выявы мы атрымаем наступныя асаблівасці:

  • Паколькі спіс версій на каналах мяняецца, то перазбіраць неабходна толькі дакументацыю для каналаў, дзе змянілася версія. Бо перазбіраць усё нанова не вельмі хораша.
  • Сам набор каналаў для рэлізаў можа мяняцца. У нейкі момант часу, напрыклад, можа не быць версіі на каналах стабільней рэлізу early-access 1.1, але з часам яны з'явяцца - не змяняць жа ў гэтым выпадку зборку рукамі?

Атрымліваецца, што зборка залежыць ад якія змяняюцца вонкавых дадзеных.

Рэалізацыя

Выбар падыходу

Як варыянт, можна запускаць кожную неабходную версію асобным pod'ом у Kubernetes. Такі варыянт мае на ўвазе большую колькасць аб'ектаў у кластары, якое будзе расці з павелічэннем колькасці стабільных рэлізаў werf. А гэта ў сваю чаргу мае на ўвазе больш складанае абслугоўванне: на кожную версію з'яўляецца свой HTTP-сервер, прычым з невялікай нагрузкай. Вядома, гэта цягне і вялікія выдаткі па рэсурсах.

Мы ж пайшлі па шляху зборкі ўсіх неабходных версій у адной выяве. Скампіляваная статыка ўсіх версій сайта знаходзіцца ў кантэйнеры з NGINX, а трафік на адпаведны Deployment прыходзіць праз NGINX Ingress. Простая структура – ​​stateless-дадатак – дазваляе лёгка маштабаваць Deployment (у залежнасці ад нагрузкі) сродкамі самога Kubernetes.

Калі быць дакладней, то мы збіраем дзве выявы: адзін - для production-контуру, другі - дадатковы, для dev-контуру. Дадатковая выява выкарыстоўваецца (запускаецца) толькі на dev-контуры сумесна з асноўным і ўтрымоўвае версію сайта з review-каміту, а маршрутызацыя паміж імі выконваецца з дапамогай Ingress-рэсурсаў.

werf vs git clone і артэфакты

Як ужо згадвалася, каб згенераваць статыку сайта для канкрэтнай версіі дакументацыі, трэба выканаць зборку, пераключыўшыся ў адпаведны тэг рэпазітара. Можна было б рабіць гэта і шляхам кланавання рэпазітара кожны раз пры зборцы, выбіраючы адпаведныя тэгі па спісе. Аднак гэта даволі рэсурсаёмістая аперацыя і, да таго ж, якая патрабуе напісання нетрывіяльных інструкцый… Іншы сур'ёзны мінус - пры такім падыходзе няма магчымасці нешта кэшаваць падчас зборкі.

Тут нам на дапамогу прыходзіць сама ўтыліта werf, якая рэалізуе разумнае кэшаванне і якая дазваляе выкарыстоўваць знешнія рэпазітары. Выкарыстанне werf для дадання кода з рэпазітара значна паскорыць зборку, т.к. werf па сутнасці адзін раз робіць кланаванне рэпазітара, а затым выконвае толькі fetch пры неабходнасці. Акрамя таго, пры даданні дадзеных з рэпазітара мы можам абраць толькі неабходныя дырэкторыі (у нашым выпадку гэта каталог docs), што значна знізіць аб'ём дададзеных дадзеных.

Паколькі Jekyll - прылада, прызначаны для кампіляцыі статыкі і ён не патрэбен у канчатковым вобразе, лагічна было б выканаць кампіляцыю ў артэфакце werf, а ў канчатковы вобраз імпартаваць толькі вынік кампіляцыі.

Пішам werf.yaml

Такім чынам мы вызначыліся, што будзем кампіляваць кожную версію ў асобным артэфакце werf. Аднак мы не ведаем, колькі гэтых артэфактаў будзе пры зборцы, таму не можам напісаць фіксаваную канфігурацыю зборкі (строга кажучы, усё ж такі можам, але гэта будзе не зусім эфектыўна).

werf дазваляе выкарыстоўваць Go-шаблоны у сваім файле канфігурацыі (werf.yaml), а гэта дае магчымасць генераваць канфіг "на лета" у залежнасці ад вонкавых дадзеных (тое, што трэба!). Вонкавымі дадзенымі ў нашым выпадку выступае інфармацыя аб версіях і рэлізах, на падставе якой мы збіраем неабходную колькасць артэфактаў і атрымліваем у выніку два выявы: werf-doc и werf-dev для запуску на розных контурах.

Вонкавыя дадзеныя перадаюцца праз зменныя асяроддзі. Вось іх склад:

  • RELEASES - Радок са спісам рэлізаў і адпаведнай ім актуальнай версіі werf, у выглядзе спісу праз прабел значэнняў у фармаце <НОМЕР_РЕЛИЗА>%<НОМЕР_ВЕРСИИ>. прыклад: 1.0%v1.0.4-beta.20
  • CHANNELS - радок са спісам каналаў і адпаведнай ім актуальнай версіі werf, у выглядзе спісу праз прабел значэнняў у фармаце <КАНАЛ>%<НОМЕР_ВЕРСИИ>. прыклад: 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • ROOT_VERSION - версія рэлізу werf для адлюстравання па змаўчанні на сайце (не заўсёды трэба выводзіць дакументацыю па найвышэйшым нумары рэлізу). Прыклад: v1.0.4-beta.20
  • REVIEW_SHA - хэш review-каміту, з якога трэба сабраць версію для тэставага контуру.

Гэтыя зменныя будуць напаўняцца ў pipeline GitLab CI, а як менавіта - напісана ніжэй.

Перш за ўсё, для зручнасці, вызначым у werf.yaml зменныя Go-шаблонаў, прысвоіўшы ім значэння з зменных асяроддзі:

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

Апісанне артэфакта для кампіляцыі статыкі версіі сайта ў цэлым аднолькавае для ўсіх неабходных нам выпадкаў (у тым ліку, генерацыя каранёвай версіі, а таксама версіі для dev-контуру). Таму вынесем яго ў асобны блок з дапамогай функцыі define - Для наступнага перавыкарыстання з дапамогай include. Шаблону будзем перадаваць наступныя аргументы:

  • Version - генерыруемую версію (назва тэга);
  • Channel - назва канала абнаўленняў, для якога генеруецца артэфакт;
  • Commit - хэш комміта, калі артэфакт генеруецца для review-каміту;
  • кантэкст.

Апісанне шаблона артэфакта

{{- 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: "Install Dependencies"
    shell: bundle install
    args:
      executable: /bin/bash
      chdir: /app/docs
  beforeSetup:
{{- if .Commit }}
  - shell: echo "Review 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/ru_site/
  - file:
      dest: /app/docs/pages_ru/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/_ru_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/app/docs/_config_ru.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 }}

Назва артэфакта павінна быць унікальнай. Мы можам гэтага дасягнуць, напрыклад, дадаўшы назву канала (значэнне зменнай .Channel) у якасці суфікса назвы артэфакта: artifact: doc-{{ .Channel }}. Але трэба разумець, што пры імпарце з артэфактаў неабходна будзе спасылацца на такія ж імёны.

Пры апісанні артэфакта выкарыстоўваецца такая магчымасць werf, як мантаванне. Мантаванне з указаннем службовай дырэкторыі build_dir дазваляе захоўваць кэш Jekyll паміж запускамі pipeline, што істотна паскарае перазборку.

Таксама вы маглі заўважыць выкарыстанне файла releases.yml - гэта YAML-файл з дадзенымі аб рэлізах, запытаны з github.com (Артэфакт, які атрымліваецца пры выкананні pipeline). Ён патрэбен пры кампіляцыі сайта, але ў кантэксце артыкула нам ён цікавы тым, што ад яго стану залежыць перазборка толькі аднаго артэфакта - Артэфакта сайта каранёвай версіі (у іншых артэфактах ён не патрэбен).

Гэта рэалізавана з дапамогай умоўнага аператара if Go-шаблонаў і канструкцыі {{ $Root.Files.Get "releases.yml" | sha256sum }} у этапе стадыі. Працуе гэта наступным чынам: пры зборцы артэфакта для каранёвай версіі. .Channel роўна root) хэш файла releases.yml уплывае на сігнатуру ўсёй стадыі, бо ён з'яўляецца складнікам імя Ansible-заданні (параметр name). Такім чынам, пры змене змесціва файл releases.yml адпаведны артэфакт будзе перасабраны.

Звярніце ўвагу таксама на працу з вонкавым рэпазітаром. У вобраз артэфакта з рэпазітара werf, дадаецца толькі каталог /docs, прычым у залежнасці ад перададзеных параметраў дадаюцца дадзеныя адразу неабходнага тэга ці review-каміту.

Каб выкарыстоўваць шаблон артэфакта для генерацыі апісання артэфакта перададзеных версій каналаў і рэлізаў, які арганізуецца цыкл па зменнай .WerfVersions в werf.yaml:

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

Т.к. цыкл згенеруе некалькі артэфактаў (мы спадзяемся на гэта), неабходна ўлічыць падзельнік паміж імі - паслядоўнасць --- (падрабязней аб сінтаксісе файла канфігурацыі гл. у дакументацыі). Як вызначыліся раней, пры выкліку шаблона ў цыкле мы перадаем параметры версіі, URL і каранёвы кантэкст.

Аналагічна, але ўжо без цыклу, выклікаем шаблон артэфакта для "асаблівых выпадкаў": для каранёвай версіі, а таксама версіі з review-каміту:

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

Звярніце ўвагу, што артэфакт для review-каміту будзе збірацца толькі ў тым выпадку, калі ўстаноўлена пераменная. .WerfReviewCommit.

Артэфакты гатовыя - пара заняцца імпартам!

Канчатковая выява, прызначаны для запуску ў Kubernetes, уяўляе сабою звычайны NGINX, у які дададзены файл канфігурацыі сервера nginx.conf і статыка з артэфактаў. Акрамя артэфакта каранёвай версіі сайта нам трэба паўтарыць цыкл па зменнай .WerfVersions для імпарту артэфактаў версій каналаў і рэлізаў + выканаць правіла наймення артэфактаў, якое мы прынялі раней. Паколькі кожны артэфакт захоўвае версіі сайта для дзвюх моў, імпартуем іх у месцы, прадугледжаныя канфігурацыяй.

Апісанне канчатковай выявы 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 -}}

Дадатковая выява, які разам з асноўным запускаецца на dev-контуры, утрымоўвае толькі дзве версіі сайта: версію з review-каміта і каранёвую версію сайта (там агульныя асеты і, калі падушыце, дадзеныя па рэлізах). Такім чынам, дадатковая выява ад асноўнага будзе адрознівацца толькі секцыяй імпарту (ну і, вядома, імем):

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

Як ужо заўважалі вышэй, артэфакт для review-комміта будзе генеравацца толькі пры запуску ўсталяванай зменнай асяроддзя REVIEW_SHA. Можна было б наогул не генераваць выяву werf-dev, калі няма зменнай асяроддзя REVIEW_SHA, Але для таго, каб ачыстка па палітыках Docker-вобразаў у werf працавала для выявы werf-dev, мы пакінем яго збірацца толькі з артэфактам каранёвай версіі (усё роўна ён ужо сабраны), для спрашчэння структуры pipeline.

Зборка гатова! Пераходзім да CI/CD і важным нюансам.

Пайплайн у GitLab CI і асаблівасці дынамічнай зборкі

Пры запуску зборкі нам неабходна ўсталяваць зменныя асяроддзі, выкарыстоўваныя ў werf.yaml. Гэта не дакранаецца зменнай REVIEW_SHA, якую будзем усталёўваць пры выкліку pipeline ад хука GitHub.

Фарміраванне неабходных знешніх дадзеных вынесем у Bash-скрыпт generate_artifacts, які будзе генераваць два артэфакты pipeline GitLab:

  • файл releases.yml з дадзенымі аб рэлізах,
  • файл common_envs.sh, які змяшчае зменныя асяроддзі для экспарту.

Змесціва файла generate_artifacts вы знойдзеце ў нашым рэпазітары з прыкладамі. Само атрыманне даных не з'яўляецца прадметам артыкула, а вось файл common_envs.sh нам важны, т.я. ад яго залежыць праца werf. Прыклад яго змесціва:

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'

Выкарыстоўваць выснову такога скрыпту можна, напрыклад, з дапамогай Bash-функцыі source.

А зараз самае цікавае. Каб і зборка, і дэплой прыкладанні працавалі правільна, неабходна зрабіць так, каб werf.yaml быў аднолькавым як мінімум у рамках аднаго pipeline. Калі гэта ўмова не выканаць, то сігнатуры стадый, якія разлічвае werf пры зборцы і, напрыклад, дэплой, будуць рознымі. Гэта прывядзе да памылкі дэплою, т.я. неабходны для дэплою выява будзе адсутнічаць.

Іншымі словамі, калі падчас зборкі выявы сайта інфармацыя аб рэлізах і версіях будзе адна, а ў момант дэплою выйдзе новая версія і зменныя асяроддзі будуць мець іншыя значэнні, то дэплой завершыцца з памылкай: бо артэфакт новай версіі яшчэ не сабраны.

Калі генерацыя werf.yaml залежыць ад вонкавых дадзеных (напрыклад, спісу актуальных версій, як у нашым выпадку), то склад і значэнні такіх дадзеных павінны фіксавацца ў рамках pipeline. Гэта асабліва важна, калі вонкавыя параметры змяняюцца даволі часта.

Мы будзем атрымліваць і фіксаваць знешнія дадзеныя на першай стадыі пайплайна ў GitLab (Prebuild) і перадаваць іх далей у выглядзе артэфакта GitLab CI. Гэта дазволіць запускаць і перазапускаць заданні pipelinе'а (зборка, дэплой, ачыстка) з аднолькавай канфігурацыяй у werf.yaml.

Змест стадыі Prebuild файл .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 week

Зафіксаваўшы вонкавыя дадзеныя ў артэфакце, можна выконваць зборку і дэплой, выкарыстаючы стандартныя стадыі пайплайна GitLab CI: Build і Deploy. Сам пайплайн мы запускаем па хуках з GitHub-рэпазітара werf (г.зн. пры зменах у рэпазітары на GitHub). Дадзеныя для іх можна ўзяць ва ўласцівасцях праекту GitLab у падзеле. CI / CD Settings -> Pipeline triggers, а затым створым у GitHub адпаведны Webhook (Settings -> Webhooks).

Стадыя зборкі будзе выглядаць наступным чынам:

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 дадасць у стадыю зборкі два артэфакты са стадыі. Prebuild, так што мы экспартуем зменныя з падрыхтаванымі ўваходнымі дадзенымі з дапамогай канструкцыі source common_envs.sh. Запускаем стадыю зборкі ва ўсіх выпадках, акрамя запуску пайплайна па раскладзе. Па раскладзе ў нас будзе запускацца пайплайн для ачысткі - выконваць зборку ў гэтым выпадку не трэба.

На стадыі дэплою апішам два заданні - асобна для дэплою на production- і dev-контуры, з выкарыстаннем 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

Заданні па сутнасці адрозніваюцца толькі ўказаннем кантэксту кластара, у які werf павінен выконваць дэплой (WERF_KUBE_CONTEXT), і ўстаноўкай зменных асяроддзя контуру (environment.name и environment.url), якія выкарыстоўваюцца затым у шаблонах Helm-чарта. Змест шаблонаў прыводзіць не будзем, т.я. там няма нічога цікавага для разгляданай тэмы, але вы можаце іх знайсці ў рэпазітары да артыкула.

Фінальная рыска

Паколькі версіі werf выходзяць даволі часта, часта будуць і збірацца новыя выявы, а Docker Registry - увесь час расці. Таму абавязкова трэба наладзіць аўтаматычную ачыстку вобразаў па палітыках. Зрабіць гэта вельмі проста.

Для рэалізацыі спатрэбіцца:

  • Дадаць стадыю ачысткі ў .gitlab-ci.yml;
  • Дадаць перыядычнае выкананне задання ачысткі;
  • Наладзіць зменную асяроддзі з токенам доступу на запіс.

Дадаем стадыю ачысткі ў .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

Амаль усе мы гэта ўжо бачылі крыху вышэй - толькі для ачысткі трэба папярэдне аўтарызавацца ў Docker Registry з токенам, мелым правы на выдаленне выяў у Docker Registry (у выдаванага аўтаматычна токена задання GitLab CI няма такіх мае рацыю). Токенаў трэба завесці ў GitLab загадзя і паказаць яго значэнне ў зменнай асяроддзі WERF_IMAGES_CLEANUP_PASSWORD праекта (CI/CD Settings -> Variables).

Даданне задання ачысткі з неабходным раскладам вырабляецца ў CI/CD ->
расклад
.

Усё: праект у Docker Registry больш не будзе ўвесь час расці ад невыкарыстоўваных выяў.

У завяршэнні практычнай часткі нагадаю, што поўныя лістынгі з артыкула даступныя ў ісці:

Вынік

  1. Мы атрымалі лагічную структуру зборкі: адзін артэфакт на адну версію.
  2. Зборка ўніверсальная і не патрабуе ручных змен пры выхадзе новых версій werf: дакументацыя на сайце аўтаматычна абнаўляецца.
  3. Збіраецца дзве выявы для розных контураў.
  4. Працуе хутка, т.я. максімальна выкарыстоўваецца кэшаванне - пры выхадзе новай версіі werf або выкліку GitHub-хуку для review-комміта - ажыццяўляецца перазборка толькі адпаведнага артэфакта са змененай версіяй.
  5. Не трэба думаць аб выдаленні невыкарыстоўваных выяў: ачыстка па палітыках werf будзе падтрымліваць парадак у Docker Registry.

Высновы

  • Выкарыстанне werf дазваляе зборцы працаваць хутка дзякуючы кэшаванню як самой зборкі, так і кэшаванню пры працы з вонкавымі рэпазітарамі.
  • Праца з вонкавымі Git-рэпазітарамі пазбаўляе ад неабходнасці кланаваць рэпазітар кожны раз цалкам або вынаходзіць ровар з хітрай логікай аптымізацыі. werf выкарыстоўвае кэш і робіць кланаванне толькі адзін раз, а далей выкарыстоўвае fetch і толькі па неабходнасці.
  • Магчымасць выкарыстання Go-шаблонаў у файле канфігурацыі зборкі werf.yaml дазваляе апісаць зборку, вынік якой залежыць ад вонкавых дадзеных.
  • Выкарыстанне мантавання ў werf значна паскарае збору артэфактаў - за кошт кэша, які з'яўляецца агульным для ўсіх pipeline.
  • werf дазваляе лёгка наладзіць ачыстку, што асабліва актуальна пры дынамічнай зборцы.

PS

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар