Dynamische Assemblierung und Bereitstellung von Docker-Images mit werf am Beispiel einer versionierten Dokumentationsseite

Wir haben bereits mehr als einmal über unser GitOps-Tool gesprochen Hof, und dieses Mal möchten wir die Erfahrungen beim Zusammenstellen einer Website mit der Dokumentation des Projekts selbst teilen - werf.io (die russische Version ist ru.werf.io). Dies ist eine normale statische Site, aber ihr Aufbau ist interessant, da sie unter Verwendung einer dynamischen Anzahl von Artefakten erstellt wird.

Dynamische Assemblierung und Bereitstellung von Docker-Images mit werf am Beispiel einer versionierten Dokumentationsseite

Auf die Nuancen der Site-Struktur einzugehen: ein gemeinsames Menü für alle Versionen, eine Seite mit Informationen zu Veröffentlichungen usw. zu erstellen – das werden wir nicht tun. Stattdessen konzentrieren wir uns auf die Probleme und Besonderheiten dynamischer Builds und ein wenig auf die zugehörigen CI/CD-Prozesse.

Einführung: So funktioniert die Site

Beginnen wir mit der Tatsache, dass die Dokumentation für Werf zusammen mit seinem Code gespeichert wird. Dies stellt bestimmte Designanforderungen, die im Allgemeinen über den Rahmen dieses Artikels hinausgehen. Zumindest lässt sich jedoch Folgendes sagen:

  • Neue Werf-Funktionen sollten nicht ohne Aktualisierung der Dokumentation veröffentlicht werden, und umgekehrt implizieren alle Änderungen in der Dokumentation die Veröffentlichung einer neuen Version von Werf.
  • Das Projekt wird ziemlich intensiv entwickelt: Neue Versionen können mehrmals täglich veröffentlicht werden;
  • Alle manuellen Vorgänge zum Bereitstellen einer Site mit einer neuen Version der Dokumentation sind zumindest mühsam.
  • Das Projekt verfolgt einen semantischen Ansatz Versionierung, mit 5 Stabilitätskanälen. Der Veröffentlichungsprozess umfasst die sequenzielle Weitergabe von Versionen durch die Kanäle in der Reihenfolge zunehmender Stabilität: von Alpha bis absolut stabil;
  • Die Site verfügt über eine russischsprachige Version, die parallel zur Hauptversion (d. h. in englischer Sprache) „lebt und weiterentwickelt“ (d. h. deren Inhalt aktualisiert wird).

Um all diese „inneren Vorgänge“ vor dem Benutzer zu verbergen und ihm etwas anzubieten, das „einfach funktioniert“, haben wir separates Tool zum Installieren und Aktualisieren von werf - Das Multiwerf. Geben Sie einfach die Versionsnummer und den Stabilitätskanal an, den Sie verwenden möchten. Multiwerf prüft dann, ob auf dem Kanal eine neue Version vorhanden ist, und lädt sie gegebenenfalls herunter.

Die neuesten Versionen von werf sind in jedem Kanal im Versionsauswahlmenü auf der Site verfügbar. Standardmäßig an der Adresse werf.io/documentation Es wird die stabilste Kanalversion für die neueste Version geöffnet – sie wird auch von Suchmaschinen indiziert. Dokumentationen zum Kanal sind unter separaten Adressen verfügbar (z. B. werf.io/v1.0-beta/documentation für die Betaversion 1.0).

Insgesamt sind auf der Site folgende Versionen verfügbar:

  1. root (wird standardmäßig geöffnet),
  2. für jeden aktiven Update-Kanal jeder Version (z. B. werf.io/v1.0-beta).

Um eine bestimmte Version einer Site zu generieren, reicht es im Allgemeinen aus, sie mit Jekyll, ausgeführt im Verzeichnis /docs werf-Repository-entsprechender Befehl (jekyll build), nachdem zuvor auf das Git-Tag der erforderlichen Version gewechselt wurde.

Es bleibt nur noch hinzuzufügen:

  • das Dienstprogramm selbst (werf) wird für die Montage verwendet;
  • CI/CD-Prozesse werden auf Basis von GitLab CI aufgebaut;
  • und all dies läuft natürlich in Kubernetes.

Aufgaben

Lassen Sie uns nun die Aufgaben formulieren, die alle beschriebenen Besonderheiten berücksichtigen:

  1. Nach dem Ändern der Werf-Version auf einem beliebigen Update-Kanal Die Dokumentation auf der Site sollte automatisch aktualisiert werden.
  2. Um sich zu entwickeln, muss man manchmal in der Lage sein, Vorschauen der Site anzeigen.

Nach dem Ändern der Version auf einem beliebigen Kanal muss über die entsprechenden Git-Tags eine Neukompilierung der Site durchgeführt werden. Während des Zusammenstellungsprozesses des Images erhalten wir jedoch die folgenden Funktionen:

  • Da sich die Liste der Versionen auf den Kanälen ändert, ist es nur erforderlich, die Dokumentation für die Kanäle neu zu kompilieren, bei denen sich die Version geändert hat. Schließlich ist es nicht sehr schön, alles von Grund auf neu zusammenzusetzen.
  • Die Anzahl der Kanäle für Veröffentlichungen kann sich ändern. Es kann beispielsweise sein, dass irgendwann keine Version mehr auf den Kanälen verfügbar ist, die stabiler ist als die Early-Access-Version 1.1, aber mit der Zeit werden sie erscheinen – in diesem Fall können Sie den Build nicht manuell ändern?

Es stellt sich heraus, dass die Assembly ist von sich ändernden externen Daten abhängig.

Implementierung

Auswahl eines Ansatzes

Alternativ können Sie jede erforderliche Version als separaten Pod in Kubernetes ausführen. Diese Option impliziert eine größere Anzahl von Objekten im Cluster, die mit der Zunahme der Anzahl stabiler Werf-Versionen wächst. Und das wiederum bedeutet einen höheren Wartungsaufwand: Jede Version verfügt über einen eigenen HTTP-Server mit geringer Auslastung. Natürlich ist damit auch ein höherer Ressourcenaufwand verbunden.

Wir gingen den gleichen Weg Builds aller benötigten Versionen in einem Image. Die kompilierten Statistiken aller Versionen der Site befinden sich in einem Container mit NGINX, und der Datenverkehr zum entsprechenden Deployment erfolgt über NGINX Ingress. Die einfache Struktur – eine zustandslose Anwendung – ermöglicht Ihnen eine einfache Skalierung der Bereitstellung (je nach Auslastung) mithilfe von Kubernetes selbst.

Genauer gesagt erfassen wir zwei Bilder: eines für den Produktionsschaltkreis und das zweite zusätzlich für den Entwicklungsschaltkreis. Das zusätzliche Image wird nur zusammen mit dem Hauptimage auf dem Dev-Circuit verwendet (ausgeführt) und enthält die Version der Site aus dem Review-Commit. Das Routing zwischen ihnen erfolgt mithilfe von Ingress-Ressourcen.

Werf vs. Git-Klon und Artefakte

Wie bereits erwähnt, müssen Sie zum Generieren von Site-Statistiken für eine bestimmte Version der Dokumentation die Erstellung durchführen, indem Sie zum entsprechenden Repository-Tag wechseln. Dies könnte auch dadurch erreicht werden, dass bei jedem Build das Repository geklont wird und die entsprechenden Tags aus einer Liste ausgewählt werden. Dies ist jedoch ein ziemlich ressourcenintensiver Vorgang und erfordert darüber hinaus das Schreiben nicht trivialer Anweisungen... Ein weiterer schwerwiegender Nachteil besteht darin, dass bei diesem Ansatz während der Assemblierung keine Möglichkeit besteht, etwas zwischenzuspeichern.

Hier kommt uns das Dienstprogramm werf selbst zu Hilfe und implementiert intelligentes Caching und ermöglicht Ihnen die Nutzung externe Repositorien. Die Verwendung von Werf zum Hinzufügen von Code aus dem Repository beschleunigt den Build erheblich, da Werf das Repository im Wesentlichen einmal klont und dann ausführt nur fetch falls erforderlich. Darüber hinaus können wir beim Hinzufügen von Daten aus dem Repository nur die erforderlichen Verzeichnisse auswählen (in unserem Fall ist dies das Verzeichnis docs), wodurch die Menge der hinzugefügten Daten erheblich reduziert wird.

Da Jekyll ein Tool zum Kompilieren statischer Dateien ist und im endgültigen Bild nicht benötigt wird, wäre es logisch, in Artefaktwerferund im endgültigen Bild Importieren Sie nur das Kompilierungsergebnis.

Wir schreiben werf.yaml

Daher haben wir beschlossen, jede Version in einem separaten Werf-Artefakt zu kompilieren. Wir wir wissen nicht, wie viele dieser Artefakte es bei der Montage geben wird, daher können wir keine feste Build-Konfiguration schreiben (streng genommen können wir das, aber es wäre nicht sehr effizient).

werf ermöglicht Ihnen die Verwendung Go-Vorlagen in Ihrer Konfigurationsdatei (werf.yaml), und dies ermöglicht es Konfiguration „on the fly“ generieren abhängig von externen Daten (was benötigt wird!). In unserem Fall handelt es sich bei den externen Daten um Informationen zu Versionen und Releases, auf deren Grundlage wir die erforderliche Anzahl an Artefakten sammeln und als Ergebnis zwei Bilder erhalten: werf-doc и werf-dev zum Starten auf verschiedenen Rennstrecken.

Externe Daten werden über Umgebungsvariablen übergeben. Hier ist ihre Zusammensetzung:

  • RELEASES — eine Zeile mit einer Liste der Releases und der entsprechenden aktuellen Version von werf, als durch Leerzeichen getrennte Liste von Werten im Format <НОМЕР_РЕЛИЗА>%<НОМЕР_ВЕРСИИ>. Beispiel: 1.0%v1.0.4-beta.20
  • CHANNELS — eine Zeile mit einer Liste von Kanälen und der entsprechenden aktuellen Version von werf, in Form einer durch Leerzeichen getrennten Werteliste im Format <КАНАЛ>%<НОМЕР_ВЕРСИИ>. Beispiel: 1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22
  • ROOT_VERSION – werf-Release-Version, die standardmäßig auf der Site angezeigt werden soll (es ist nicht immer erforderlich, die Dokumentation für die höchste Release-Nummer anzuzeigen). Beispiel: v1.0.4-beta.20
  • REVIEW_SHA – der Hash des Review-Commits, aus dem die Version für den Testkreis erstellt werden soll.

Diese Variablen werden in der GitLab CI-Pipeline gefüllt. Wie das genau geschieht, wird unten beschrieben.

Definieren wir zunächst der Einfachheit halber werf.yaml Go-Vorlagenvariablen, indem Sie ihnen Werte aus Umgebungsvariablen zuweisen:

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

Die Beschreibung des Artefakts zum Kompilieren der statischen Version der Site ist im Allgemeinen für alle von uns benötigten Fälle gleich (einschließlich der Generierung der Root-Version sowie der Version für den Dev-Circuit). Daher werden wir es mit der Funktion in einen separaten Block auslagern define — zur späteren Wiederverwendung mit Hilfe von include. Wir übergeben der Vorlage die folgenden Argumente:

  • Version — die generierte Version (Tag-Name);
  • Channel — der Name des Aktualisierungskanals, für den das Artefakt generiert wird;
  • Commit – Commit-Hash, wenn das Artefakt für ein Review-Commit generiert wird;
  • Kontext.

Beschreibung der Artefaktvorlage

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

Der Artefaktname muss eindeutig sein. Dies erreichen wir beispielsweise durch das Hinzufügen des Kanalnamens (Variablenwert .Channel) als Suffix zum Artefaktnamen: artifact: doc-{{ .Channel }}. Sie müssen jedoch verstehen, dass Sie beim Importieren aus Artefakten auf dieselben Namen verweisen müssen.

Bei der Beschreibung eines Artefakts wird die folgende Werf-Funktion verwendet: Montage. Mounten mit einem angegebenen Serviceverzeichnis build_dir ermöglicht es Ihnen, den Jekyll-Cache zwischen Pipeline-Läufen beizubehalten, was beschleunigt den Zusammenbau erheblich.

Möglicherweise haben Sie auch die Verwendung der Datei bemerkt releases.yml — ist eine YAML-Datei mit Release-Daten, die angefordert wurden von Github.com (Artefakt, das beim Ausführen einer Pipeline erhalten wird). Es wird beim Kompilieren einer Site benötigt, aber im Kontext des Artikels ist es für uns interessant, weil sein Zustand davon abhängt Wiederzusammenbau nur eines Artefakts – Artefakt der Stammversion der Site (wird in anderen Artefakten nicht benötigt).

Dies wird mithilfe des Bedingungsoperators implementiert. if Go-Vorlagen und -Konstrukte {{ $Root.Files.Get "releases.yml" | sha256sum }} in der Bühne Stufen. Das funktioniert folgendermaßen: Beim Erstellen eines Artefakts für die Root-Version (Variable .Channel ist root) Datei-Hash releases.yml wirkt sich auf die Signatur der gesamten Phase aus, da es sich um eine Komponente des Ansible-Aufgabennamens handelt (Parameter name). Wenn Sie also Inhalt Datei releases.yml das entsprechende Artefakt wird wieder zusammengesetzt.

Bitte beachten Sie auch die Arbeit mit einem externen Repository. Im Bild eines Artefakts aus Werf-Repositorys, nur das Verzeichnis wird hinzugefügt /docs, und abhängig von den übergebenen Parametern werden die Daten des erforderlichen Tags oder Review-Commits sofort hinzugefügt.

Um die Artefaktvorlage zum Generieren einer Artefaktbeschreibung der übertragenen Kanalversionen und Releases zu verwenden, organisieren wir eine Schleife nach Variablen .WerfVersions в werf.yaml:

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

Da der Zyklus mehrere Artefakte erzeugt (wir hoffen es), ist es notwendig, das Trennzeichen zwischen ihnen zu berücksichtigen - die Sequenz --- (Weitere Informationen zur Syntax der Konfigurationsdatei finden Sie unter Dokumentation). Wie wir zuvor definiert haben, übergeben wir beim Aufrufen einer Vorlage in einer Schleife die Parameter Version, URL und Stammkontext.

Analog, aber ohne Zyklus, rufen wir die Artefaktvorlage für „Sonderfälle“ auf: für die Root-Version, sowie die Version aus dem Review-Commit:

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

Beachten Sie, dass das Artefakt für das Überprüfungs-Commit nur erstellt wird, wenn die Variable festgelegt ist. .WerfReviewCommit.

Die Artefakte sind fertig – Zeit, mit dem Importieren zu beginnen!

Das endgültige Image, das auf Kubernetes ausgeführt werden soll, ist ein reguläres NGINX mit einer hinzugefügten Serverkonfigurationsdatei. nginx.conf und statische Aufladung von Artefakten. Zusätzlich zum Root-Version-Artefakt der Site müssen wir den Zyklus mit Variablen wiederholen .WerfVersions um Kanalversionen und Release-Artefakte zu importieren + befolgen Sie die zuvor übernommene Artefakt-Benennungsregel. Da jedes Artefakt Versionen der Site für zwei Sprachen speichert, importieren wir sie an die von der Konfiguration bereitgestellten Stellen.

Beschreibung des endgültigen Bildes 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 -}}

Das zusätzliche Image, das zusammen mit dem Hauptimage im Dev Circuit gestartet wird, enthält nur zwei Versionen der Site: die Version aus dem Review-Commit und die Root-Version der Site (sie enthält gemeinsame Assets und, wenn Sie sich erinnern, Release-Daten). Somit unterscheidet sich das zusätzliche Bild vom Hauptbild nur im Importabschnitt (und natürlich im Namen):

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

Wie oben erwähnt, wird das Artefakt für das Review-Commit nur generiert, wenn die festgelegte Umgebungsvariable ausgeführt wird. REVIEW_SHA. Es wäre möglich, das Werf-Dev-Image überhaupt nicht zu generieren, wenn keine Umgebungsvariable vorhanden ist REVIEW_SHA, aber um Reinigung nach Richtlinien Docker-Images in Werf funktionierten für das Werf-Dev-Image. Wir werden es nur mit dem Artefakt der Root-Version erstellen lassen (es ist ohnehin bereits erstellt), um die Pipeline-Struktur zu vereinfachen.

Die Montage ist fertig! Kommen wir nun zu CI/CD und wichtigen Nuancen.

Pipeline in GitLab CI und Funktionen des dynamischen Builds

Beim Ausführen des Builds müssen wir die Umgebungsvariablen festlegen, die in werf.yaml. Dies gilt nicht für die Variable REVIEW_SHA, die wir beim Aufrufen der Pipeline vom GitHub-Hook aus festlegen.

Die Erstellung der notwendigen externen Daten verlagern wir in ein Bash-Skript generate_artifacts, wodurch zwei GitLab-Pipeline-Artefakte generiert werden:

  • Datei releases.yml mit Release-Daten,
  • Datei common_envs.sh, die Umgebungsvariablen für den Export enthält.

Dateiinhalte generate_artifacts finden Sie in unserem Repositorien mit Beispielen. Die eigentliche Datenerfassung ist nicht Gegenstand des Artikels, sondern die Datei common_envs.sh ist uns wichtig, da der Betrieb von werf davon abhängt. Ein Beispiel für den Inhalt:

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'

Die Ausgabe eines solchen Skripts kann beispielsweise mit der Bash-Funktion source.

Und jetzt der interessanteste Teil. Damit sowohl der Build als auch die Bereitstellung der Anwendung korrekt funktionieren, muss sichergestellt werden, dass werf.yaml war das gleiche zumindest innerhalb einer Pipeline. Wenn diese Bedingung nicht erfüllt ist, unterscheiden sich die Stufensignaturen, die werf während der Assemblierung und beispielsweise der Bereitstellung berechnet. Dies führt zu einem Bereitstellungsfehler, da das für die Bereitstellung erforderliche Image fehlt.

Mit anderen Worten: Wenn während der Zusammenstellung des Site-Images die Informationen zu Releases und Versionen gleich sind und zum Zeitpunkt der Bereitstellung eine neue Version veröffentlicht wird und die Umgebungsvariablen unterschiedliche Werte haben, endet die Bereitstellung mit einem Fehler: Schließlich wurde das Artefakt der neuen Version noch nicht zusammengestellt.

Wenn Generation werf.yaml von externen Daten abhängt (zum Beispiel einer Liste aktueller Versionen, wie in unserem Fall), dann sollten die Zusammensetzung und Werte dieser Daten innerhalb der Pipeline aufgezeichnet werden. Dies ist insbesondere dann wichtig, wenn sich die äußeren Parameter häufig ändern.

Wir werden Empfangen und Aufzeichnen externer Daten in der ersten Phase der Pipeline in GitLab (Vorgefertigt) und übermitteln Sie diese weiter in der Form GitLab CI-Artefakte. Dadurch können Sie Pipeline-Jobs (Build, Deploy, Clean) mit der gleichen Konfiguration ausführen und neu starten in werf.yaml.

Inhalte der Etappe Vorgefertigt Datei .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

Sobald Sie externe Daten einem Artefakt zugewiesen haben, können Sie es mithilfe der standardmäßigen GitLab CI-Pipeline-Phasen erstellen und bereitstellen: Erstellen und Bereitstellen. Wir starten die Pipeline selbst mithilfe von Hooks aus dem Werf-GitHub-Repository (d. h. wenn es Änderungen im Repository auf GitHub gibt). Die Daten hierzu können den GitLab-Projekteigenschaften im Abschnitt CI/CD-Einstellungen -> Pipeline-Trigger, und erstellen Sie dann den entsprechenden Webhook in GitHub (Einstellungen -> Webhooks).

Die Build-Phase sieht folgendermaßen aus:

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 fügt zwei Artefakte aus der Phase zur Build-Phase hinzu Vorgefertigt, daher exportieren wir Variablen mit vorbereiteten Eingabedaten mithilfe des Konstrukts source common_envs.sh. Wir starten die Build-Phase in allen Fällen, außer wenn die Pipeline planmäßig gestartet wird. Wir werden einen geplanten Pipeline-Lauf zur Bereinigung durchführen – in diesem Fall ist es nicht erforderlich, einen Build durchzuführen.

In der Bereitstellungsphase beschreiben wir zwei Aufgaben – getrennt für die Bereitstellung in Produktions- und Entwicklungsschaltungen unter Verwendung einer YAML-Vorlage:

.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

Die Tasks unterscheiden sich im Wesentlichen nur in der Angabe des Clusterkontextes, in den werf das Deployment durchführen soll (WERF_KUBE_CONTEXT) und Festlegen der Konturumgebungsvariablen (environment.name и environment.url), die dann in Helm-Chart-Vorlagen verwendet werden. Wir werden den Inhalt der Vorlagen nicht zur Verfügung stellen, weil... es nichts Interessantes für das betreffende Thema gibt, aber Sie können sie finden in Repositorien zum Artikel.

Letzte Berührung

Da Werf-Versionen recht häufig veröffentlicht werden, werden häufig neue Images erstellt und das Docker-Register wächst ständig. Daher ist es zwingend erforderlich, die automatische Bildbereinigung gemäß den Richtlinien zu konfigurieren. Das geht ganz einfach.

Zur Umsetzung benötigen Sie:

  • Fügen Sie einen Reinigungsschritt hinzu zu .gitlab-ci.yml;
  • Fügen Sie die regelmäßige Ausführung der Reinigungsaufgabe hinzu;
  • Richten Sie eine Umgebungsvariable mit einem Schreibzugriffstoken ein.

Fügen Sie eine Reinigungsphase hinzu, um .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

Fast alles davon haben wir oben bereits gesehen – nur zum Bereinigen müssen Sie sich zunächst mit einem Token bei der Docker-Registrierung anmelden, der über die Berechtigung zum Löschen von Bildern in der Docker-Registrierung verfügt (der automatisch ausgestellte Token für die GitLab CI-Aufgabe verfügt nicht über solche Rechte). Das Token muss vorab in GitLab erstellt und sein Wert in der Umgebungsvariable angegeben werden WERF_IMAGES_CLEANUP_PASSWORD Projekt (CI/CD-Einstellungen -> Variablen).

Das Hinzufügen einer Reinigungsaufgabe mit dem erforderlichen Zeitplan erfolgt in CI/CD ->
Zeitpläne
.

Das war's: Ihr Docker Registry-Projekt wird nicht mehr ständig aus ungenutzten Images wachsen.

Zum Abschluss des praktischen Teils möchte ich Sie daran erinnern, dass die vollständigen Auflistungen aus dem Artikel verfügbar sind in Git:

Erlebe die Kraft effektiver Ergebnisse

  1. Wir haben eine logische Assemblystruktur: ein Artefakt pro Version.
  2. Die Assembly ist universell und erfordert keine manuellen Änderungen, wenn neue Versionen von Werf veröffentlicht werden: Die Dokumentation auf der Site wird automatisch aktualisiert.
  3. Für unterschiedliche Konturen werden zwei Bilder aufgenommen.
  4. Funktioniert schnell, da das Caching maximal genutzt wird. Wenn eine neue Version von Werf veröffentlicht wird oder ein GitHub-Hook für ein Review-Commit aufgerufen wird, wird nur das entsprechende Artefakt mit der geänderten Version neu erstellt.
  5. Sie müssen sich keine Gedanken über das Löschen nicht verwendeter Bilder machen: Die richtlinienbasierte Bereinigung von werf sorgt für Ordnung in Ihrer Docker-Registrierung.

Befund

  • Durch die Verwendung von werf kann der Build schnell ausgeführt werden, da sowohl der Build selbst als auch das Caching beim Arbeiten mit externen Repositories zwischengespeichert werden.
  • Durch die Arbeit mit externen Git-Repositorys entfällt die Notwendigkeit, jedes Mal das gesamte Repository zu klonen oder das Rad mit kniffliger Optimierungslogik neu zu erfinden. werf verwendet Cache und Klone nur einmal und verwendet dann fetch und nur wenn nötig.
  • Möglichkeit zur Verwendung von Go-Vorlagen in der Build-Konfigurationsdatei werf.yaml ermöglicht die Beschreibung einer Baugruppe, deren Ergebnis von externen Daten abhängt.
  • Die Verwendung der Einbindung in Werf beschleunigt die Sammlung von Artefakten erheblich – aufgrund des Caches, der allen Pipelines gemeinsam ist.
  • werf erleichtert die Konfiguration der Bereinigung, was besonders für dynamische Builds wichtig ist.

PS

Lesen Sie auch auf unserem Blog:

Source: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz und VPS-VDS-Servern 🔥 Kaufen Sie zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server | ProHoster