Dynamiczny montaż i wdrażanie obrazów Dockera za pomocą werf na przykładzie wersjonowanej witryny z dokumentacją

O naszym narzędziu GitOps wspominaliśmy już nie raz. werf, i tym razem chcielibyśmy podzielić się naszym doświadczeniem w montażu strony z dokumentacją samego projektu - werf.io (jego rosyjska wersja to pl.werf.io). Jest to zwykła strona statyczna, ale jej montaż jest interesujący, ponieważ jest zbudowany przy użyciu dynamicznej liczby artefaktów.

Dynamiczny montaż i wdrażanie obrazów Dockera za pomocą werf na przykładzie wersjonowanej witryny z dokumentacją

Wejdź w niuanse struktury witryny: wygeneruj wspólne menu dla wszystkich wersji, strony z informacjami o wydaniach itp. - Nie będziemy. Zamiast tego skupmy się na zagadnieniach i cechach montażu dynamicznego, a trochę na towarzyszących mu procesach CI/CD.

Wprowadzenie: jak działa strona

Na początek dokumentacja werf jest przechowywana wraz z jej kodem. Nakłada to pewne wymagania rozwojowe, które generalnie wykraczają poza zakres tego artykułu, ale można co najmniej stwierdzić, że:

  • Nie należy wypuszczać nowych funkcji werf bez aktualizacji dokumentacji i odwrotnie, wszelkie zmiany w dokumentacji pociągają za sobą wydanie nowej wersji werf;
  • Projekt ma dość intensywny rozwój: nowe wersje mogą być wydawane kilka razy dziennie;
  • Wszelkie ręczne operacje mające na celu wdrożenie witryny z nową wersją dokumentacji są co najmniej żmudne;
  • W projekcie przyjęto podejście semantyczne wersjonowanie, z 5 kanałami stabilności. Proces wydawania obejmuje sekwencyjne przechodzenie wersji przez kanały w kolejności rosnącej stabilności: od alfa do solidnego;
  • Witryna ma wersję w języku rosyjskim, która „żyje i rozwija się” (tj. której treść jest aktualizowana) równolegle z wersją główną (tj. anglojęzyczną).

Aby ukryć całą tę „wewnętrzną kuchnię” przed użytkownikiem, oferując mu coś, co „po prostu działa”, zrobiliśmy to oddzielne narzędzie do instalacji i aktualizacji werf - jest multiwerf. Musisz tylko podać numer wydania i kanał stabilności, którego chcesz użyć, a multiwerf sprawdzi, czy na kanale jest nowa wersja i w razie potrzeby ją pobierze.

W menu wyboru wersji na stronie, w każdym kanale dostępne są najnowsze wersje werf. Domyślnie według adresu werf.io/documentation otwiera się wersja najbardziej stabilnego kanału dla najnowszego wydania - jest on również indeksowany przez wyszukiwarki. Dokumentacja kanału dostępna jest pod osobnymi adresami (np. werf.io/v1.0-beta/documentation dla wersji beta 1.0).

Łącznie na stronie dostępne są następujące wersje:

  1. root (domyślnie otwiera się),
  2. dla każdego aktywnego kanału aktualizacji każdego wydania (na przykład werf.io/v1.0-beta).

Aby wygenerować konkretną wersję witryny, wystarczy ją skompilować za pomocą Jekylluruchamiając w katalogu /docs odpowiednie polecenie repozytorium werf (jekyll build), po przejściu na tag Git wymaganej wersji.

Pozostaje tylko dodać, że:

  • samo narzędzie (werf) służy do montażu;
  • Procesy CI/CD budowane są w oparciu o GitLab CI;
  • a wszystko to oczywiście działa w Kubernetesie.

zadania

Sformułujmy teraz zadania uwzględniające wszystkie opisane specyfiki:

  1. Po zmianie wersji werf na dowolnym kanale aktualizacji dokumentacja na stronie powinna być automatycznie aktualizowana.
  2. Aby się rozwijać, czasami trzeba to umieć przeglądaj wersje poglądowe witryny.

Witryna musi zostać przekompilowana po zmianie wersji na dowolnym kanale z odpowiednich tagów Git, ale w procesie budowania obrazu uzyskamy następujące funkcje:

  • Ponieważ zmienia się lista wersji na kanałach, konieczne jest jedynie odbudowanie dokumentacji dla kanałów, w których wersja się zmieniła. Przecież odbudowanie wszystkiego od nowa nie jest zbyt miłe.
  • Zestaw kanałów wydań może ulec zmianie. W pewnym momencie może nie być na kanałach wersji stabilniejszej niż wersja 1.1 wczesnego dostępu, ale z czasem się one pojawią – czy w takim przypadku nie należy ręcznie zmieniać montażu?

Okazuje się, że montaż zależy od zmiany danych zewnętrznych.

realizacja

Wybór podejścia

Alternatywnie możesz uruchomić każdą wymaganą wersję jako oddzielny pod w Kubernetes. Opcja ta oznacza większą liczbę obiektów w klastrze, która będzie rosła wraz ze wzrostem liczby stabilnych wydań werf. A to z kolei oznacza bardziej złożoną konserwację: każda wersja ma własny serwer HTTP i przy niewielkim obciążeniu. Oczywiście wiąże się to również z większymi kosztami zasobów.

Poszliśmy tą samą drogą montaż wszystkich niezbędnych wersji na jednym obrazie. Skompilowane statystyki wszystkich wersji witryny znajdują się w kontenerze z NGINX, a ruch do odpowiedniego wdrożenia przechodzi przez NGINX Ingress. Prosta struktura – aplikacja bezstanowa – pozwala na łatwe skalowanie Deploymentu (w zależności od obciążenia) przy użyciu samego Kubernetesa.

Mówiąc dokładniej, zbieramy dwa obrazy: jeden dla obwodu produkcyjnego, drugi dodatkowy dla obwodu deweloperskiego. Dodatkowy obraz jest używany (uruchamiany) tylko na obwodzie deweloperskim razem z głównym i zawiera wersję witryny z zatwierdzenia przeglądu, a routing pomiędzy nimi odbywa się przy użyciu zasobów Ingress.

werf vs klon git i artefakty

Jak już wspomniano, aby wygenerować statykę witryny dla konkretnej wersji dokumentacji, należy ją zbudować, przełączając się na odpowiedni tag repozytorium. Można to również zrobić klonując repozytorium przy każdej kompilacji, wybierając odpowiednie znaczniki z listy. Jest to jednak operacja wymagająca dużych zasobów, a ponadto wymaga napisania nietrywialnych instrukcji... Kolejną poważną wadą jest to, że przy takim podejściu nie ma możliwości buforowania czegoś podczas montażu.

Tutaj z pomocą przychodzi nam samo narzędzie werf, wdrażając inteligentne buforowanie i umożliwienie korzystania repozytoria zewnętrzne. Użycie werf do dodania kodu z repozytorium znacznie przyspieszy kompilację, ponieważ werf zasadniczo klonuje repozytorium raz, a następnie wykonuje tylko fetch Jeśli to konieczne. Dodatkowo dodając dane z repozytorium możemy wybrać jedynie niezbędne katalogi (w naszym przypadku jest to katalog docs), co znacznie zmniejszy ilość dodawanych danych.

Ponieważ Jekyll jest narzędziem przeznaczonym do kompilowania danych statycznych i nie jest potrzebny w ostatecznym obrazie, logiczne byłoby skompilowanie artefakt Werfai do końcowego obrazu zaimportuj tylko wynik kompilacji.

Piszemy werf.yaml

Zdecydowaliśmy więc, że skompilujemy każdą wersję w osobnym artefakcie werf. Jednakże, my nie wiemy, ile z tych artefaktów będzie podczas montażu, więc nie możemy napisać stałej konfiguracji kompilacji (ściśle mówiąc, nadal możemy, ale nie będzie to w pełni skuteczne).

werf pozwala na użycie Przejdź do szablonów w pliku konfiguracyjnym (werf.yaml) i dzięki temu jest to możliwe generuj konfigurację w locie w zależności od danych zewnętrznych (czego potrzebujesz!). Dane zewnętrzne w naszym przypadku to informacje o wersjach i wydaniach, na podstawie których zbieramy wymaganą liczbę artefaktów i w efekcie uzyskujemy dwa obrazy: werf-doc и werf-dev pracować w różnych obwodach.

Dane zewnętrzne są przekazywane poprzez zmienne środowiskowe. Oto ich skład:

  • RELEASES — linia z listą wydań i odpowiadającą im aktualną wersją werf, w postaci oddzielonej spacjami listy wartości w formacie <НОМЕР_РЕЛИЗА>%<НОМЕР_ВЕРСИИ>. Przykład: 1.0%v1.0.4-beta.20
  • CHANNELS — linia z listą kanałów i odpowiadającą im aktualną wersją werf, w postaci oddzielonej spacjami listy wartości w formacie <КАНАЛ>%<НОМЕР_ВЕРСИИ>. Przykład: 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • ROOT_VERSION — wersja wydania werf, która ma być domyślnie wyświetlana na stronie (nie zawsze jest konieczne wyświetlanie dokumentacji według najwyższego numeru wydania). Przykład: v1.0.4-beta.20
  • REVIEW_SHA — skrót zatwierdzenia przeglądu, z którego należy zbudować wersję dla pętli testowej.

Zmienne te zostaną wypełnione w potoku GitLab CI, a jak dokładnie opisano poniżej.

Przede wszystkim dla wygody definiujemy w werf.yaml Przejdź do zmiennych szablonowych, przypisując im wartości ze zmiennych środowiskowych:

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

Opis artefaktu służącego do kompilacji statycznej wersji witryny jest generalnie taki sam dla wszystkich potrzebnych nam przypadków (w tym generowania wersji root, a także wersji dla obwodu deweloperskiego). Dlatego przeniesiemy go do osobnego bloku za pomocą funkcji define - do późniejszego ponownego użycia include. Do szablonu przekażemy następujące argumenty:

  • Version — wygenerowana wersja (nazwa tagu);
  • Channel — nazwa kanału aktualizacji, dla którego generowany jest artefakt;
  • Commit — skrót zatwierdzenia, jeśli artefakt jest generowany na potrzeby zatwierdzenia przeglądu;
  • kontekst.

Opis szablonu artefaktu

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

Nazwa artefaktu musi być unikalna. Możemy to osiągnąć np. dodając nazwę kanału (wartość zmiennej .Channel) jako przyrostek do nazwy artefaktu: artifact: doc-{{ .Channel }}. Musisz jednak zrozumieć, że importując z artefaktów, będziesz musiał odwoływać się do tych samych nazw.

Podczas opisywania artefaktu używana jest następująca funkcja werf: montowanie. Montaż wskazujący katalog usług build_dir umożliwia zapisanie pamięci podręcznej Jekyll między uruchomieniami potoku, co znacznie przyspiesza ponowny montaż.

Być może zauważyłeś również użycie pliku releases.yml to plik YAML, z którego zażądano danych o wydaniu Github.com (artefakt uzyskany podczas wykonywania potoku). Jest potrzebny podczas kompilacji strony, ale w kontekście artykułu jest dla nas interesujący, ponieważ zależy od jej stanu ponowne złożenie tylko jednego artefaktu — artefakt wersji root witryny (nie jest potrzebny w innych artefaktach).

Jest to realizowane za pomocą instrukcji warunkowej if Przejdź do szablonów i projektów {{ $Root.Files.Get "releases.yml" | sha256sum }} w scenie gradacja. Działa to w następujący sposób: podczas budowania artefaktu dla wersji root (zmienna .Channel równa się root) skrót pliku releases.yml wpływa na sygnaturę całego etapu, ponieważ jest częścią nazwy zadania Ansible (parametr name). Zatem przy zmianie treść plik releases.yml odpowiedni artefakt zostanie ponownie złożony.

Proszę zwrócić także uwagę na pracę z zewnętrznym repozytorium. Na obrazie artefaktu z repozytorium werf, dodawany jest tylko katalog /docsi w zależności od przekazanych parametrów, dane wymaganego tagu lub zatwierdzenia przeglądu są natychmiast dodawane.

Aby użyć szablonu artefaktu do wygenerowania opisu artefaktu przesłanych wersji kanałów i wydań, organizujemy pętlę na zmiennej .WerfVersions в werf.yaml:

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

Ponieważ pętla wygeneruje kilka artefaktów (mamy taką nadzieję), należy uwzględnić oddzielający je separator - sekwencję --- (Aby uzyskać więcej informacji na temat składni pliku konfiguracyjnego, zobacz dokumentacja). Jak zdefiniowano wcześniej, wywołując szablon w pętli, przekazujemy parametry wersji, adres URL i kontekst główny.

Podobnie, ale bez pętli, szablon artefaktu nazywamy dla „przypadków specjalnych”: dla wersji root, a także wersji z zatwierdzenia przeglądu:

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

Należy pamiętać, że artefakt zatwierdzenia przeglądu zostanie zbudowany tylko wtedy, gdy zmienna jest ustawiona .WerfReviewCommit.

Artefakty są gotowe – czas rozpocząć import!

Ostateczny obraz, zaprojektowany do działania na Kubernetesie, to zwykły NGINX z dodanym plikiem konfiguracyjnym serwera nginx.conf i statyczne spowodowane artefaktami. Oprócz artefaktu wersji root witryny musimy powtórzyć pętlę na zmiennej .WerfVersions aby zaimportować artefakty wersji kanału i wydania + postępuj zgodnie z przyjętą wcześniej zasadą nazewnictwa artefaktów. Ponieważ każdy artefakt przechowuje wersje serwisu w dwóch językach, importujemy je w miejsca wskazane w konfiguracji.

Opis końcowego obrazu 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 -}}

Dodatkowy obraz, który wraz z głównym jest uruchamiany w obwodzie deweloperskim, zawiera tylko dwie wersje witryny: wersję z zatwierdzenia recenzji i wersję główną witryny (istnieją zasoby ogólne i, jeśli pamiętasz , dane wydania). Zatem dodatkowy obraz będzie różnił się od głównego tylko w sekcji importu (i oczywiście w nazwie):

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

Jak zauważono powyżej, artefakt zatwierdzenia przeglądu zostanie wygenerowany tylko po uruchomieniu ustawionej zmiennej środowiskowej REVIEW_SHA. Gdyby nie było zmiennej środowiskowej, możliwe byłoby w ogóle nie generowanie obrazu werf-dev REVIEW_SHA, ale żeby sprzątanie według zasad Obrazy Dockera w werf działały dla obrazu werf-dev, pozostawimy go do zbudowania tylko z artefaktem wersji root (i tak jest już zbudowany), aby uprościć strukturę potoku.

Montaż jest gotowy! Przejdźmy do CI/CD i ważnych niuansów.

Pipeline w GitLab CI i funkcje dynamicznej kompilacji

Podczas uruchamiania kompilacji musimy ustawić zmienne środowiskowe używane w werf.yaml. Nie dotyczy to zmiennej REVIEW_SHA, którą ustawimy wywołując potok z hooka GitHuba.

Niezbędne dane zewnętrzne wygenerujemy w skrypcie Bash generate_artifacts, który wygeneruje dwa artefakty potoku GitLab:

  • файл releases.yml z danymi wydania,
  • файл common_envs.sh, zawierający zmienne środowiskowe, które mają zostać wyeksportowane.

Zawartość pliku generate_artifacts znajdziesz w naszym repozytoria z przykładami. Samo otrzymanie danych nie jest tematem artykułu, ale pliku common_envs.sh jest dla nas ważne, ponieważ od tego zależy praca werfa. Przykładowa jego zawartość:

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'

Możesz wykorzystać wynik takiego skryptu, na przykład za pomocą funkcji Bash source.

Teraz zaczyna się zabawa. Aby zarówno kompilacja, jak i wdrożenie aplikacji działały poprawnie, należy o to zadbać werf.yaml było ten sam przynajmniej w ramach jednego rurociągu. Jeśli ten warunek nie zostanie spełniony, wówczas sygnatury etapów, które werf oblicza podczas montażu i np. wdrażania, będą inne. Doprowadzi to do błędu wdrażania, ponieważ... brakuje obrazu wymaganego do wdrożenia.

Innymi słowy, jeśli podczas montażu obrazu witryny informacje o wydaniach i wersjach są takie same, a w momencie wdrożenia zostanie wydana nowa wersja, a zmienne środowiskowe będą miały różne wartości, wówczas wdrożenie zakończy się błędem: w końcu artefakt nowej wersji nie został jeszcze zbudowany.

Jeśli pokolenie werf.yaml zależy od danych zewnętrznych (na przykład listy aktualnych wersji, jak w naszym przypadku), wówczas skład i wartości takich danych powinny zostać zarejestrowane w ramach rurociągu. Jest to szczególnie ważne, jeśli parametry zewnętrzne zmieniają się dość często.

Będziemy odbierać i rejestrować dane zewnętrzne na pierwszym etapie rurociągu w GitLabie (Prekompilacja) i przekazać je dalej w formularzu Artefakt GitLab CI. Umożliwi to uruchamianie i ponowne uruchamianie zadań potokowych (kompilacja, wdrażanie, czyszczenie) z tą samą konfiguracją w werf.yaml.

Zawartość sceny Prekompilacja plik .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

Po przechwyceniu danych zewnętrznych w artefakcie możesz kompilować i wdrażać przy użyciu standardowych etapów potoku GitLab CI: Kompiluj i wdrażaj. Sam potok uruchamiamy za pomocą hooków z repozytorium werf GitHub (czyli wtedy, gdy w repozytorium na GitHubie zajdą zmiany). Dane do nich znajdziesz we właściwościach projektu GitLab w sekcji Ustawienia CI/CD -> Wyzwalacze potoku, a następnie utwórz odpowiedni webhook w GitHub (Ustawienia -> Webhooki).

Etap kompilacji będzie wyglądał następująco:

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 doda dwa artefakty ze sceny do etapu kompilacji Prekompilacja, więc eksportujemy zmienne z przygotowanymi danymi wejściowymi za pomocą konstrukcji source common_envs.sh. Etap budowy rozpoczynamy we wszystkich przypadkach, za wyjątkiem uruchomienia gazociągu zgodnie z harmonogramem. Zgodnie z harmonogramem poprowadzimy rurociąg do oczyszczenia – w tym przypadku nie ma konieczności wykonywania montażu.

Na etapie wdrożenia opiszemy dwa zadania – osobno dla wdrożenia do obwodów produkcyjnych i deweloperskich, z wykorzystaniem szablonu 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

Zadania różnią się zasadniczo jedynie wskazaniem kontekstu klastra, w którym werf powinien przeprowadzić wdrożenie (WERF_KUBE_CONTEXT) i ustawienie zmiennych środowiskowych pętli (environment.name и environment.url), które są następnie używane w szablonach wykresów Helma. Nie będziemy udostępniać zawartości szablonów, ponieważ... nie ma tam nic interesującego na dany temat, ale można je znaleźć repozytorium artykułu.

Ostatni dotyk

Ponieważ wersje werf są wydawane dość często, nowe obrazy będą często tworzone, a rejestr Docker będzie stale rósł. Dlatego konieczne jest skonfigurowanie automatycznego czyszczenia obrazu w oparciu o zasady. Jest to bardzo łatwe do zrobienia.

Do wdrożenia będziesz potrzebować:

  • Dodaj etap czyszczenia do .gitlab-ci.yml;
  • Dodaj okresowe wykonywanie zadania czyszczenia;
  • Skonfiguruj zmienną środowiskową z tokenem dostępu do zapisu.

Dodanie etapu czyszczenia do .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

Prawie to wszystko widzieliśmy już trochę wyżej - tylko żeby to wyczyścić należy najpierw zalogować się do Rejestru Dockera przy użyciu tokena mającego uprawnienia do usuwania obrazów w Rejestrze Dockera (automatycznie wystawiony token zadania GitLab CI nie mają takie prawa). Token należy wcześniej utworzyć w GitLabie i podać jego wartość w zmiennej środowiskowej WERF_IMAGES_CLEANUP_PASSWORD projekt (Ustawienia CI/CD -> Zmienne).

Dodawanie zadania czyszczenia z wymaganym harmonogramem odbywa się w CI/CD ->
Harmonogramy
.

To wszystko: projekt w rejestrze Dockera nie będzie już stale powiększany z nieużywanych obrazów.

Na koniec części praktycznej przypomnę, że pełne zestawienia z artykułu dostępne są w git:

Doświadcz mocnych i skutecznych rezultatów

  1. Otrzymaliśmy logiczną strukturę złożenia: jeden artefakt na wersję.
  2. Montaż jest uniwersalny i nie wymaga ręcznych zmian po wydaniu nowych wersji werf: dokumentacja na stronie jest aktualizowana automatycznie.
  3. Dwa obrazy są złożone dla różnych konturów.
  4. Działa szybko, bo Buforowanie jest wykorzystywane tak często, jak to możliwe — gdy wydawana jest nowa wersja werf lub wywoływany jest hak GitHub w celu zatwierdzenia recenzji, odbudowywany jest tylko odpowiedni artefakt ze zmienioną wersją.
  5. Nie musisz myśleć o usuwaniu nieużywanych obrazów: czyszczenie zgodnie z polityką werf zapewni porządek w rejestrze Docker.

odkrycia

  • Użycie werf umożliwia szybką pracę zestawu dzięki buforowaniu zarówno samego zestawu, jak i buforowaniu podczas pracy z zewnętrznymi repozytoriami.
  • Praca z zewnętrznymi repozytoriami Git eliminuje potrzebę każdorazowego klonowania całego repozytorium lub wymyślania koła na nowo dzięki skomplikowanej logice optymalizacji. werf używa pamięci podręcznej i klonuje tylko raz, a następnie używa fetch i tylko wtedy, gdy jest to konieczne.
  • Możliwość korzystania z szablonów Go w pliku konfiguracyjnym kompilacji werf.yaml pozwala opisać złożenie, którego wynik zależy od danych zewnętrznych.
  • Użycie mount w werf znacznie przyspiesza zbieranie artefaktów - ze względu na pamięć podręczną, która jest wspólna dla wszystkich potoków.
  • werf ułatwia konfigurację czyszczenia, co jest szczególnie ważne przy dynamicznym budowaniu.

PS

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz