
Vor zwei Jahren haben wir den Artikel "
Erstellen von Projekten mit GitLab CI: eine .gitlab-ci.yml für Hunderte von Anwendungen " veröffentlicht. Jetzt werden wir über die Lösung eines ähnlichen Problems sprechen. In
.gitlab-ci.yml
Material geht es darum, wie Sie CI / CD-Prozesse für eine große Anzahl ähnlicher Anwendungen mit dem Aufkommen von
include
in
.gitlab-ci.yml
und dem Aufkommen von werf als Ersatz für dapp erstellen können.
Einführung
In den weiteren Anweisungen im Artikel wird die folgende Situation berücksichtigt:
- Es gibt eine große Client-Anwendung, die in viele Repositorys unterteilt ist.
- Jedes Repository ist eine separate Anwendung, die in einem Kubernetes-Cluster ausgeführt werden muss.
- Als CI-System wird GitLab CI verwendet.
- Die Bereitstellung (die Infrastruktur, in der der Code bereitgestellt wird) wird in Helmdiagrammen beschrieben.
- Erstellen Sie Images und stellen Sie sie mit werf in Kubernetes bereit .
Der Einfachheit und Bequemlichkeit halber (und als Hommage an die Mode) werden wir diese Anwendungen weiterhin als Microservices bezeichnen.
Alle diese Mikrodienste werden auf dieselbe Weise zusammengestellt, bereitgestellt und gestartet , und bestimmte Einstellungen werden mithilfe von Umgebungsvariablen konfiguriert.
Das Kopieren von
.gitlab-ci.yml
,
werf.yaml
und
.helm
bringt
.helm
viele Probleme mit sich. Schließlich sollte jede Bearbeitung in CI, der Assemblierungsprozess oder die Beschreibung des Helm-Diagramms zu anderen Repositorys hinzugefügt werden ...
Vorlagen in .gitlab-ci.yml verbinden
Mit dem Aufkommen der
include:file
Direktive in GitLab CE (
seit Version 11.7 ) wurde es möglich, ein gemeinsames CI zu erstellen.
include
selbst erschien etwas früher (in 11.4), erlaubte jedoch das Verbinden von Vorlagen nur über
öffentliche URLs, was seine Funktionalität etwas einschränkte. Die GitLab-Dokumentation
beschreibt alle Funktionen und Verwendungsbeispiele
perfekt .
Somit war es möglich, das Kopieren von
.gitlab-ci.yml
zwischen Repositories zu verweigern und dessen Relevanz zu unterstützen. Hier ist ein Beispiel
.gitlab-ci.yml
mit
include
:
include: - project: 'infra/gitlab-ci' ref: 1.0.0 file: base-gitlab-ci.yaml - project: 'infra/gitlab-ci' ref: 1.0.0 file: cleanup.yaml
Wir empfehlen dringend, Zweigstellennamen in
ref
mit Vorsicht zu verwenden . Einschlüsse werden zum Zeitpunkt der Erstellung der Pipeline berechnet, sodass Ihre CI-Änderungen möglicherweise zum ungünstigsten Zeitpunkt automatisch in die Produktionspipeline fallen. Die
Verwendung von Tags in ref
erleichtert jedoch die Versionierung der Beschreibung von CI / CD-Prozessen. Beim Aktualisieren sieht alles so transparent wie möglich aus und Sie können den Verlauf von Änderungen in Pipeline-Versionen leicht verfolgen, wenn Sie die semantische Versionierung für Tags verwenden.
Verbinden Sie .helm von einem separaten Repository aus
Da diese Microservices auf dieselbe Weise bereitgestellt und ausgeführt werden, ist derselbe Satz von Helmvorlagen erforderlich. Um zu vermeiden, dass das
.helm
Verzeichnis zwischen Repositorys kopiert wird, haben wir das Repository
.helm
, in dem Helm-Vorlagen gespeichert und auf dem gewünschten Tag überprüft wurden. Es sah ungefähr so aus:
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.example.com/infra/helm.git .helm - cd .helm && git checkout tags/1.0.0 - type multiwerf && source <(multiwerf use 1.0 beta) - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose) - werf deploy --stages-storage :local
Es gab auch Variationen mit Git-Submodulen, aber alles scheint eher eine Problemumgehung zu sein ...
Und jetzt, mit der jüngsten werf-Veröffentlichung,
hat er die Möglichkeit, Diagramme aus externen Repositories
zu verbinden. Die vollständige Unterstützung der Funktionen des Paketmanagers ermöglichte es wiederum, die Abhängigkeiten für die Bereitstellung der Anwendung
transparent zu beschreiben.
Reihenfolge der Aktionen
Kommen wir zurück zur Lösung unseres Problems mit Microservices. Lassen Sie uns unser Repository zum Speichern von Helm-Diagrammen erweitern - zum Beispiel
ChartMuseum . Es kann problemlos in einem Kubernetes-Cluster bereitgestellt werden:
helm repo add stable https://kubernetes-charts.storage.googleapis.com helm install stable/chartmuseum --name flant-chartmuseum
Ingress hinzufügen:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/force-ssl-redirect: "false" nginx.ingress.kubernetes.io/proxy-body-size: 10m nginx.ingress.kubernetes.io/ssl-redirect: "false" name: chart-museum spec: rules: - host: flant-chartmuseum.example.net http: paths: - backend: serviceName: flant-chartmuseum servicePort: 8080 path: / status: loadBalancer: {}
Das
flant-chartmuseum
für die
flant-chartmuseum
muss die Umgebungsvariable
DISABLE_API
in
false
ändern. Andernfalls (standardmäßig) funktionieren die ChartMuseum-API-Anforderungen nicht und es ist nicht möglich, neue Diagramme zu erstellen.
Nun beschreiben wir das Repository, in dem die gemeinsam genutzten Helmdiagramme gespeichert werden. Die Struktur der Verzeichnisse ist wie folgt:
. ├── charts │ └── yii2-microservice │ ├── Chart.yaml │ └── templates │ ├── app.yaml └── README.md
Chart.yaml
könnte folgendermaßen aussehen:
name: yii2-microservice version: 1.0.4
Das
templates
sollte alle erforderlichen Kubernetes-Grundelemente enthalten, die zum Bereitstellen der Anwendung im Cluster erforderlich sind. Wie Sie vielleicht bereits vermutet haben, ist der Microservice in diesem Fall eine PHP-Anwendung, die auf dem yii2-Framework basiert. Beschreiben wir die minimale Bereitstellung mit zwei nginx- und php-fpm-Containern, die mit werf erstellt wurden:
--- apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Values.global.werf.name }} spec: replicas: 1 revisionHistoryLimit: 3 template: metadata: labels: service: {{ .Values.global.werf.name }} spec: imagePullSecrets: - name: registrysecret containers: - name: backend {{ tuple "backend" . | include "werf_container_image" | indent 8 }} command: [ '/usr/sbin/php-fpm7', "-F" ] ports: - containerPort: 9000 protocol: TCP name: http env: {{ tuple "backend" . | include "werf_container_env" | indent 8 }} - name: frontend command: ['/usr/sbin/nginx'] {{ tuple "frontend" . | include "werf_container_image" | indent 8 }} ports: - containerPort: 80 name: http lifecycle: preStop: exec: command: ["/usr/sbin/nginx", "-s", "quit"] env: {{ tuple "frontend" . | include "werf_container_env" | indent 8 }} --- apiVersion: v1 kind: Service metadata: name: {{ .Values.global.werf.name }} spec: selector: service: {{ .Values.global.werf.name }} ports: - name: http port: 80 protocol: TCP
Die Variable
.Values.global.werf.name
enthält den Namen des Projekts aus der
werf.yaml
Datei, mit der Sie die erforderlichen Namen von Diensten und Bereitstellungen
werf.yaml
können.
Lassen Sie uns die einfachste Automatisierung für Push im ChartMuseum unserer Diagramme vornehmen, wenn Sie sich für den Hauptzweig festlegen. Dazu beschreiben wir
.gitlab-ci.yml
:
Build and push to chartmuseum: script: - for i in $(ls charts); do helm package "charts/$i"; done; - for i in $(find . -type f -name "*.tgz" -printf "%f\n"); do curl --data-binary "@$i" http://flant-chartmuseum.example.net/api/charts; done; stage: build environment: name: infra only: - master tags: - my-shell-runner-tag
Chart.yaml
werden versioniert, indem die
version
in
Chart.yaml
. Alle neuen Diagramme werden automatisch zum ChartMuseum hinzugefügt.
Wir gehen bis zur Ziellinie! Im Projekt-Repository in
.helm/requirements.yaml
schreiben
.helm/requirements.yaml
die Abhängigkeiten für das Diagramm:
dependencies: - name: yii2-microservice version: "1.0.4" repository: "@flant"
... und im Verzeichnis mit dem Repository ausführen:
werf helm repo init werf helm repo add flant http://flant-chartmuseum.example.net werf helm dependency update
Wir
.helm/requirements.lock
drin
.helm/requirements.lock
.
werf helm dependency build
die Anwendung im Cluster
werf helm dependency build
, reicht es aus, den
werf helm dependency build
bevor
werf deploy
.
Um die Beschreibung der Bereitstellung der Anwendung zu aktualisieren, müssen Sie jetzt die Repositorys mit Microservices durchsuchen und kleine Patches mit Änderungen an den Hashes und Tags in den
requirements.yaml
und den
requirements.lock
anwenden. Auf Wunsch kann dieser Vorgang auch über CI automatisiert werden: Wie dies beschrieben wird, haben wir bereits im
genannten Artikel beschrieben .
Fazit
Ich hoffe, dass die beschriebene Abfolge von Maßnahmen zur Wartung ähnlicher Anwendungen für Ingenieure mit ähnlichen Problemen hilfreich ist.
Gerne teilen wir Ihnen auch weitere praktische Rezepte für die Verwendung von
werf mit . Wenn Sie Schwierigkeiten haben, deren Implementierung unüberwindbar oder einfach unverständlich erscheint, können Sie sich daher gerne an
Telegram wenden oder hier in den Kommentaren Anfragen für zukünftige Materialien hinterlassen.
PS
Lesen Sie auch in unserem Blog: