Erprobung neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes


Hallo! In letzter Zeit wurden viele coole Automatisierungstools zum Erstellen von Docker-Images und für die Bereitstellung auf Kubernetes veröffentlicht. In diesem Zusammenhang habe ich mich entschlossen, mit dem Gitlab zu spielen, seine Fähigkeiten zu untersuchen und natürlich die Pipeline zu konfigurieren.


Die Inspiration fĂĽr diese Arbeit war die Site kubernetes.io , die automatisch aus Quellcodes generiert wird. FĂĽr jeden gesendeten Pool generiert der Roboter automatisch eine Vorschau-Version der Site mit Ihren Ă„nderungen und bietet einen Link zum Anzeigen.


Ich habe versucht, einen ähnlichen Prozess von Grund auf neu zu erstellen, der jedoch vollständig auf Gitlab CI und kostenlosen Tools basiert, mit denen ich Anwendungen in Kubernetes bereitgestellt habe. Heute werde ich Ihnen endlich mehr darüber erzählen.


In dem Artikel werden Tools wie die folgenden behandelt:
Hugo , qbec , kaniko , git-crypt und GitLab CI bei der Erstellung dynamischer Umgebungen.




Inhalt


  1. Wir stellen Hugo vor
  2. Dockerfile vorbereiten
  3. Bekanntschaft mit Kaniko
  4. Wir stellen vor: qbec
  5. Versucht Gitlab-Runner mit Kubernetes-Executor
  6. Einsatz von Helmkarten mit qbec
  7. EinfĂĽhrung in die Git-Krypta
  8. Erstellen Sie ein Toolbox-Image
  9. Unsere erste Pipeline und Assemblierung von Bildern nach Tags
  10. Bereitstellungsautomatisierung
  11. Artefakte und Push-Build-in-Master
  12. Dynamische Umgebungen
  13. Apps ĂĽberprĂĽfen



1. Hugo vorstellen


Als Beispiel für unser Projekt werden wir versuchen, eine Website für die Veröffentlichung von Dokumentationen zu erstellen, die auf Hugo basieren. Hugo ist ein statischer Inhaltsgenerator.


Für diejenigen, die mit statischen Generatoren nicht vertraut sind, werde ich Ihnen etwas mehr über sie erzählen. Im Gegensatz zu regulären Datenbank-Site-Engines und einigen PHP-Engines, die nach Aufforderung durch einen Benutzer Seiten im laufenden Betrieb generieren, sind statische Generatoren etwas anders angeordnet. Sie ermöglichen es Ihnen, die Quelle, in der Regel eine Reihe von Dateien in den Markup- und Designvorlagen von Markdown, zu nehmen und sie dann zu einer vollständig fertigen Site zu kompilieren.


Das heißt, am Ausgang erhalten Sie eine Verzeichnisstruktur und eine Reihe von generierten HTML-Dateien, die einfach auf jedes billige Hosting hochgeladen werden können und eine funktionierende Site erhalten.


Hugo kann lokal installiert und ausprobiert werden:


Wir initialisieren die neue Site:


hugo new site docs.example.org 

Und zur gleichen Zeit das Git-Repository:


 cd docs.example.org git init 

Bisher ist unsere Website makellos und damit etwas darauf erscheint, mĂĽssen wir zuerst ein Thema, ein Thema, verknĂĽpfen. Es handelt sich lediglich um eine Reihe von Vorlagen und voreingestellten Regeln, mit denen unsere Website generiert wird.


Als Thema verwenden wir Learn , das meiner Meinung nach am besten fĂĽr eine Site mit Dokumentation geeignet ist.


Ich möchte besonders darauf achten, dass wir keine Themendateien im Repository unseres Projekts speichern müssen, sondern sie einfach mit dem Git-Submodul verbinden können :


 git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn 

Daher befinden sich in unserem Repository nur Dateien, die in direktem Zusammenhang mit unserem Projekt stehen, und das verknĂĽpfte Thema bleibt in Form eines Links zu einem bestimmten Repository und wird darin festgeschrieben. Das heiĂźt, es kann immer aus der Originalquelle abgerufen werden und hat keine Angst vor inkompatiblen Ă„nderungen.


Fix config.toml config:


 baseURL = "http://docs.example.org/" languageCode = "en-us" title = "My Docs Site" theme = "learn" 

Bereits in dieser Phase können Sie Folgendes ausführen:


 hugo server 

Und unter http: // localhost: 1313 / überprüfen Sie unsere neu erstellte Site. Alle am Verzeichnis vorgenommenen Änderungen werden automatisch aktualisiert, und die geöffnete Seite im Browser ist sehr praktisch.


Versuchen wir, ein Deckblatt in content / _index.md zu erstellen:


 # My docs site ## Welcome to the docs! You will be very smart :-) 

Screenshot der neu erstellten Seite


Um eine Site zu generieren, fĂĽhren Sie einfach Folgendes aus:


 hugo 

Der Inhalt des public / -Verzeichnisses ist Ihre Site.
Ja, ĂĽbrigens, fĂĽgen wir es sofort zu .gitignore hinzu :


 echo /public > .gitignore 

Vergessen Sie nicht, unsere Ă„nderungen zu ĂĽbernehmen:


 git add . git commit -m "New site created" 



2. Vorbereiten der Docker-Datei


Es ist Zeit, die Struktur unseres Repository zu bestimmen. Normalerweise benutze ich etwas wie:


 . ├── deploy │ ├── app1 │ └── app2 └── dockerfiles ├── image1 └── image2 

  • dockerfiles / - enthält Verzeichnisse mit Dockerfiles und allem, was Sie zum Erstellen unserer Docker-Images benötigen.
  • deploy / - enthält Verzeichnisse fĂĽr die Bereitstellung unserer Anwendungen auf Kubernetes

Daher erstellen wir unser erstes Dockerfile unter dem Pfad dockerfiles / website / Dockerfile


 FROM alpine:3.11 as builder ARG HUGO_VERSION=0.62.0 RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-64bit.tar.gz | tar -xz -C /usr/local/bin ADD . /src RUN hugo -s /src FROM alpine:3.11 RUN apk add --no-cache darkhttpd COPY --from=builder /src/public /var/www ENTRYPOINT [ "/usr/bin/darkhttpd" ] CMD [ "/var/www" ] 

Wie Sie sehen, enthält die Docker-Datei zwei FROMs. Diese Funktion wird als mehrstufige Erstellung bezeichnet und ermöglicht es Ihnen, alles Unnötige vom endgültigen Docker-Image auszuschließen.
Daher enthält das endgültige Image nur darkhttpd (einen einfachen HTTP-Server) und public / - den Inhalt unserer statisch generierten Site.


Vergessen Sie nicht, unsere Ă„nderungen zu ĂĽbernehmen:


 git add dockerfiles/website git commit -m "Add Dockerfile for website" 



3. Bekanntschaft mit Kaniko


Als Sammler von Docker-Images habe ich mich fĂĽr Kaniko entschieden , da fĂĽr die Arbeit kein Docker-Daemon erforderlich ist. Die Assembly selbst kann auf jedem Computer ausgefĂĽhrt und der Cache direkt in der Registrierung gespeichert werden, sodass kein dauerhafter Speicher mehr erforderlich ist .


Um das Image zu erstellen, starten Sie einfach den Container mit Kaniko Executor und übergeben Sie den aktuellen Build-Kontext an ihn. Dies können Sie lokal über Docker tun:


 docker run -ti --rm \ -v $PWD:/workspace \ -v ~/.docker/config.json:/kaniko/.docker/config.json:ro \ gcr.io/kaniko-project/executor:v0.15.0 \ --cache \ --dockerfile=dockerfiles/website/Dockerfile \ --destination=registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1 

Dabei ist registry.gitlab.com/kvaps/docs.example.org/website der Name Ihres Docker-Image. Nach dem Zusammenbau wird es automatisch in das Docker-Register verschoben.


Mit dem Parameter --cache können Sie Layer in der Docker-Registrierung zwischenspeichern. Für das angegebene Beispiel werden sie in registry.gitlab.com/kvaps/docs.example.org/website/cache gespeichert. Mit dem Parameter --cache können Sie jedoch einen anderen Pfad angeben repo .


Screenshot Docker-Registry




4. Bekanntschaft mit qbec


Qbec ist ein Bereitstellungstool, mit dem Sie das Manifest Ihrer Anwendung deklarativ beschreiben und für Kubernetes bereitstellen können. Durch die Verwendung von Jsonnet als Hauptsyntax können Sie die Beschreibung von Unterschieden für mehrere Umgebungen erheblich vereinfachen und die Code-Wiederholbarkeit nahezu vollständig eliminieren.


Dies kann insbesondere dann der Fall sein, wenn Sie eine Anwendung in mehreren Clustern mit unterschiedlichen Parametern bereitstellen müssen und diese deklarativ in Git beschreiben möchten.


Mit Qbec können Sie Helm-Diagramme auch rendern, indem Sie ihnen die erforderlichen Parameter übergeben und sie anschließend ausführen. Außerdem können Sie reguläre Manifeste mit verschiedenen Mutationen erstellen, die auf sie angewendet werden können. Auf diese Weise muss das ChartMuseum nicht mehr verwendet werden. Das heißt, Sie können Diagramme direkt in git speichern und rendern, wo sie sich befinden.


Wie ich bereits sagte, speichern wir alle Bereitstellungen im Verzeichnis deploy / :


 mkdir deploy cd deploy 

Initialisieren wir unsere erste Anwendung:


 qbec init website cd website 

Nun sieht die Struktur unserer Anwendung so aus:


 . ├── components ├── environments │  ├── base.libsonnet │  └── default.libsonnet ├── params.libsonnet └── qbec.yaml 

Schauen Sie sich die Datei qbec.yaml an :


 apiVersion: qbec.io/v1alpha1 kind: App metadata: name: website spec: environments: default: defaultNamespace: docs server: https://kubernetes.example.org:8443 vars: {} 

Hier interessieren uns vor allem spec.environments , qbec hat bereits eine Standardumgebung fĂĽr uns angelegt und die Serveradresse sowie den Namespace aus unserer aktuellen kubeconfig ĂĽbernommen.
Jetzt wird qbec bei der Bereitstellung in der Standardumgebung immer nur im angegebenen Kubernetes-Cluster und im angegebenen Namespace bereitgestellt. Sie mĂĽssen also nicht mehr zwischen Kontexten und Namespaces wechseln, um eine Bereitstellung durchzufĂĽhren.
Bei Bedarf können Sie die Einstellungen in dieser Datei jederzeit aktualisieren.


Alle Ihre Umgebungen sind in qbec.yaml und in der Datei params.libsonnet beschrieben , in der angegeben ist, wo Sie die Parameter fĂĽr diese Umgebungen festlegen mĂĽssen.


Als nächstes sehen wir zwei Verzeichnisse:


  • komponenten / - alle manifeste fĂĽr unsere anwendung werden hier gespeichert, sie können sowohl in jsonnet als auch in normalen yaml-dateien beschrieben werden
  • Umgebungen / - Hier werden alle Variablen (Parameter) fĂĽr unsere Umgebungen beschrieben.

Standardmäßig haben wir zwei Dateien:


  • environ / base.libsonnet - enthält allgemeine Parameter fĂĽr alle Umgebungen
  • environ / default.libsonnet - Enthält Parameter, die fĂĽr die Standardumgebung ĂĽberschrieben werden

Ă–ffnen wir die Datei surroundings / base.libsonnet und fĂĽgen dort die Parameter fĂĽr unsere erste Komponente hinzu:


 { components: { website: { name: 'example-docs', image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1', replicas: 1, containerPort: 80, servicePort: 80, nodeSelector: {}, tolerations: [], ingressClass: 'nginx', domain: 'docs.example.org', }, }, } 

Wir werden auch unsere erste Komponente components / website.jsonnet erstellen:


 local env = { name: std.extVar('qbec.io/env'), namespace: std.extVar('qbec.io/defaultNs'), }; local p = import '../params.libsonnet'; local params = p.components.website; [ { apiVersion: 'apps/v1', kind: 'Deployment', metadata: { labels: { app: params.name }, name: params.name, }, spec: { replicas: params.replicas, selector: { matchLabels: { app: params.name, }, }, template: { metadata: { labels: { app: params.name }, }, spec: { containers: [ { name: 'darkhttpd', image: params.image, ports: [ { containerPort: params.containerPort, }, ], }, ], nodeSelector: params.nodeSelector, tolerations: params.tolerations, imagePullSecrets: [{ name: 'regsecret' }], }, }, }, }, { apiVersion: 'v1', kind: 'Service', metadata: { labels: { app: params.name }, name: params.name, }, spec: { selector: { app: params.name, }, ports: [ { port: params.servicePort, targetPort: params.containerPort, }, ], }, }, { apiVersion: 'extensions/v1beta1', kind: 'Ingress', metadata: { annotations: { 'kubernetes.io/ingress.class': params.ingressClass, }, labels: { app: params.name }, name: params.name, }, spec: { rules: [ { host: params.domain, http: { paths: [ { backend: { serviceName: params.name, servicePort: params.servicePort, }, }, ], }, }, ], }, }, ] 

In dieser Datei wurden sofort drei Kubernetes-Entitäten beschrieben: Deployment , Service und Ingress . Auf Wunsch können wir sie in verschiedene Komponenten aufteilen, aber in diesem Stadium reicht uns eine.


Die Syntax von jsonnet ist der von regulärem json sehr ähnlich, im Prinzip ist reguläres jsonnet bereits gültig. Daher ist es möglicherweise zunächst einfacher, Onlinedienste wie yaml2json zu verwenden, um Ihr übliches yaml nach json zu konvertieren, oder wenn Ihre Komponenten keine Variablen enthalten Sie können in Form von gewöhnlichem Yam beschrieben werden.


Wenn Sie mit jsonnet arbeiten, empfehle ich dringend, ein Plugin fĂĽr Ihren Editor zu installieren

FĂĽr vim gibt es beispielsweise ein vim-jsonnet-Plug-in , das die Syntaxhervorhebung aktiviert und bei jedem Speichern automatisch jsonnet fmt ausfĂĽhrt (dazu muss jsonnet installiert sein).

Alles ist fertig, jetzt können wir mit dem Deployment beginnen:


Um zu sehen, was passiert ist, machen wir Folgendes:


 qbec show default 

Am Ausgang sehen Sie gerenderte Yaml-Manifeste, die auf den Standardcluster angewendet werden.


Ok, jetzt bewerben:


 qbec apply default 

Am Ausgang sehen Sie immer, was in Ihrem Cluster getan wird. Qbec fordert Sie auf, die Änderungen zu akzeptieren. Durch Eingabe von y können Sie Ihre Absichten bestätigen.


Fertig, jetzt ist unsere Anwendung angedockt!


Wenn Sie Änderungen vornehmen, können Sie immer Folgendes tun:


 qbec diff default 

um zu sehen, wie sich diese Ă„nderungen auf die aktuelle Bereitstellung auswirken


Vergessen Sie nicht, unsere Ă„nderungen zu ĂĽbernehmen:


 cd ../.. git add deploy/website git commit -m "Add deploy for website" 



5. Probieren Sie Gitlab-runner mit Kubernetes-executor aus


Bis vor kurzem habe ich auf einer vorbereiteten Maschine (LXC-Container) mit Shell oder Docker-Executor nur den üblichen Gitlab-Runner verwendet . Anfangs hatten wir mehrere dieser Läufer global in unserem Hitlab definiert. Sie sammelten Docker-Bilder für alle Projekte.


Aber wie die Praxis gezeigt hat, ist diese Option sowohl in Bezug auf die Praktikabilität als auch in Bezug auf die Sicherheit nicht optimal. Es ist viel besser und ideologisch korrekter, für jedes Projekt und sogar für jede Umgebung separate Läufer einzusetzen.


GlĂĽcklicherweise ist dies ĂĽberhaupt kein Problem, da wir gitlab-runner jetzt als Teil unseres Projekts direkt auf Kubernetes bereitstellen werden.


Gitlab bietet ein fertiges Steuerdiagramm für die Bereitstellung von gitlab-runner in Kubernetes. Alles was Sie tun müssen, ist das Registrierungs-Token für unser Projekt unter Einstellungen -> CI / CD -> Läufer herauszufinden und es zu übergeben.


 helm repo add gitlab https://charts.gitlab.io helm install gitlab-runner \ --set gitlabUrl=https://gitlab.com \ --set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc \ --set rbac.create=true \ gitlab/gitlab-runner 

Wo:


  • https://gitlab.com ist die Adresse Ihres Gitlab-Servers.
  • yga8y-jdCusVDn_t4Wxc - Registrierungs-Token fĂĽr Ihr Projekt.
  • rbac.create = true - Gibt dem Läufer die erforderliche Anzahl von Berechtigungen, um Pods fĂĽr die AusfĂĽhrung unserer Aufgaben mit dem kubernetes-executor erstellen zu können.

Wenn alles richtig gemacht ist, sollten Sie den registrierten Läufer im Abschnitt Läufer in den Einstellungen Ihres Projekts sehen.


Screenshot des hinzugefügten Läufers


Ist es so einfach - ja so einfach! Kein Ärger mehr mit der manuellen Registrierung von Läufern, von nun an werden Läufer automatisch erstellt und zerstört.




6. Bereitstellung von Helmkarten mit QBEC


Da wir beschlossen haben, gitlab-runner als Teil unseres Projekts zu betrachten, ist es an der Zeit, es in unserem Git-Repository zu beschreiben.


Wir könnten es als separate Komponente der Website bezeichnen , aber wir planen, in Zukunft sehr oft unterschiedliche Kopien der Website bereitzustellen , im Gegensatz zu gitlab-runner , das für jeden Kubernetes-Cluster nur einmal bereitgestellt wird. Also lasst uns eine separate Anwendung dafür initialisieren:


 cd deploy qbec init gitlab-runner cd gitlab-runner 

Dieses Mal werden wir Kubernetes Entitäten nicht manuell beschreiben, sondern ein fertiges Helm-Diagramm erstellen. Einer der Vorteile von qbec ist die Möglichkeit, Helm-Diagramme direkt aus dem Git-Repository zu rendern.


Lassen Sie es uns mit Git-Submodul einstecken:


 git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner 

Jetzt enthält das Verzeichnis vendor / gitlab-runner unser Repository mit einem Diagramm für gitlab-runner.


Ebenso können Sie andere Repositorys, beispielsweise das gesamte Repository, mit den offiziellen Charts https://github.com/helm/charts verbinden

Beschreiben wir die Komponente components / gitlab-runner.jsonnet :


 local env = { name: std.extVar('qbec.io/env'), namespace: std.extVar('qbec.io/defaultNs'), }; local p = import '../params.libsonnet'; local params = p.components.gitlabRunner; std.native('expandHelmTemplate')( '../vendor/gitlab-runner', params.values, { nameTemplate: params.name, namespace: env.namespace, thisFile: std.thisFile, verbose: true, } ) 

Als erstes Argument fĂĽr expandHelmTemplate ĂĽbergeben wir den Pfad zum Diagramm, dann params.values , die wir aus den Umgebungsparametern entnehmen , und dann ein Objekt mit


  • nameTemplate - Versionsname
  • Namespace - Der Namespace wurde an helm ĂĽbergeben
  • thisFile - Erforderlicher Parameter, der den Pfad zur aktuellen Datei ĂĽbergibt
  • verbose - Zeigt den Befehl helm template mit allen Argumenten beim Rendern des Diagramms an

Nun werden wir die Parameter fĂĽr unsere Komponente in environ / base.libsonnet beschreiben :


 local secrets = import '../secrets/base.libsonnet'; { components: { gitlabRunner: { name: 'gitlab-runner', values: { gitlabUrl: 'https://gitlab.com/', rbac: { create: true, }, runnerRegistrationToken: secrets.runnerRegistrationToken, }, }, }, } 

Beachten Sie das runnerRegistrationToken, das wir aus der Datei external secrets / base.libsonnet entnehmen. Erstellen wir es:


 { runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc', } 

ĂśberprĂĽfen Sie, ob alles funktioniert:


 qbec show default 

Wenn alles in Ordnung ist, können wir unsere frühere Version über die Helm-Version entfernen:


 helm uninstall gitlab-runner 

und bereitstellen, aber schon ĂĽber qbec:


 qbec apply default 



7. EinfĂĽhrung in Git-Crypt


Git-Crypt ist ein Tool, mit dem Sie eine transparente Verschlüsselung für Ihr Repository einrichten können.


Im Moment sieht die Struktur unseres Verzeichnisses fĂĽr gitlab-runner so aus:


 . ├── components │  ├── gitlab-runner.jsonnet ├── environments │  ├── base.libsonnet │  └── default.libsonnet ├── params.libsonnet ├── qbec.yaml ├── secrets │  └── base.libsonnet └── vendor └── gitlab-runner (submodule) 

Geheimnisse in Git aufzubewahren ist aber nicht sicher, oder? Also mĂĽssen wir sie richtig verschlĂĽsseln.


Normalerweise ist dies aus Gründen einer einzelnen Variablen nicht immer sinnvoll. Sie können Geheimnisse an qbec und über die Umgebungsvariablen Ihres CI-Systems weitergeben.
Es ist jedoch anzumerken, dass es komplexere Projekte gibt, die viel mehr Geheimnisse enthalten können, und es äußerst schwierig sein wird, sie alle durch Umgebungsvariablen zu übertragen.

Außerdem würde ich Ihnen in diesem Fall kein so wunderbares Tool wie Git-Crypt vorstellen können .

git-crypt ist auch deshalb praktisch, weil Sie damit den gesamten Verlauf von Geheimnissen speichern sowie Konflikte auf die gleiche Weise vergleichen, zusammenführen und lösen können, wie wir dies bei Git getan haben.

Zunächst müssen wir nach der Installation von git-crypt Schlüssel für unser Repository generieren:


 git crypt init 

Wenn Sie einen PGP-Schlüssel haben, können Sie sich sofort als Mitbearbeiter für dieses Projekt hinzufügen:


 git-crypt add-gpg-user kvapss@gmail.com 

So können Sie dieses Repository jederzeit mit Ihrem privaten Schlüssel entschlüsseln.


Wenn Sie keinen PGP-Schlüssel haben und dieser nicht erwartet wird, können Sie den Projektschlüssel auch in die andere Richtung exportieren:


 git crypt export-key /path/to/keyfile 

Auf diese Weise kann jeder mit einer exportierten SchlĂĽsseldatei Ihr Repository entschlĂĽsseln.


Es ist Zeit, unser erstes Geheimnis aufzubauen.
Ich möchte Sie daran erinnern, dass wir uns noch im Verzeichnis deploy / gitlab-runner / befinden, in dem sich das Verzeichnis secrets / befindet. Verschlüsseln wir alle darin enthaltenen Dateien. Dazu erstellen wir die Datei secrets / .gitattributes mit dem folgenden Inhalt:


 * filter=git-crypt diff=git-crypt .gitattributes !filter !diff 

Wie Sie dem Inhalt entnehmen können, werden alle Dateien von mask * mit Ausnahme von .gitattributes selbst über git-crypt ausgeführt


Wir können dies überprüfen, indem wir Folgendes ausführen:


 git crypt status -e 

Am Ausgang erhalten wir eine Liste aller Dateien im Repository, fĂĽr die die VerschlĂĽsselung aktiviert ist


Das ist alles, jetzt können wir unsere Änderungen sicher übernehmen:


 cd ../.. git add . git commit -m "Add deploy for gitlab-runner" 

Um das Repository zu blockieren, gehen Sie wie folgt vor:


 git crypt lock 

und dann verwandeln sich alle verschlüsselten Dateien in etwas Binäres, es wird unmöglich sein, sie zu lesen.
So entschlĂĽsseln Sie ein Repository:


 git crypt unlock 



8. Erstellen Sie ein Toolbox-Image


Ein Toolbox-Image ist ein solches Image mit allen Tools, die wir zum Bereitstellen unseres Projekts verwenden. Es wird vom gitlab-Läufer verwendet, um typische Bereitstellungsaufgaben auszuführen.


Alles ist hier einfach, wir erstellen eine neue Docker-Datei / Toolbox / Dockerfile mit folgendem Inhalt:


 FROM alpine:3.11 RUN apk add --no-cache git git-crypt RUN QBEC_VER=0.10.3 \ && wget -O- https://github.com/splunk/qbec/releases/download/v${QBEC_VER}/qbec-linux-amd64.tar.gz \ | tar -C /tmp -xzf - \ && mv /tmp/qbec /tmp/jsonnet-qbec /usr/local/bin/ RUN KUBECTL_VER=1.17.0 \ && wget -O /usr/local/bin/kubectl \ https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/linux/amd64/kubectl \ && chmod +x /usr/local/bin/kubectl RUN HELM_VER=3.0.2 \ && wget -O- https://get.helm.sh/helm-v${HELM_VER}-linux-amd64.tar.gz \ | tar -C /tmp -zxf - \ && mv /tmp/linux-amd64/helm /usr/local/bin/helm 

Wie Sie sehen, installieren wir in diesem Image alle Dienstprogramme, die wir zum Bereitstellen unserer Anwendung verwendet haben. Wir brauchen hier kein Kubectl , aber Sie möchten vielleicht schon beim Einrichten der Pipeline damit spielen.


Außerdem müssen wir die Rolle für die von gitlab-runner generierten Pods konfigurieren, um mit Kubernetes kommunizieren und ein Deployment durchführen zu können.


Gehen Sie dazu mit gitlab-runner in das Verzeichnis:


 cd deploy/gitlab-runner 

und fĂĽge eine neue Komponente components / rbac.jsonnet hinzu :


 local env = { name: std.extVar('qbec.io/env'), namespace: std.extVar('qbec.io/defaultNs'), }; local p = import '../params.libsonnet'; local params = p.components.rbac; [ { apiVersion: 'v1', kind: 'ServiceAccount', metadata: { labels: { app: params.name, }, name: params.name, }, }, { apiVersion: 'rbac.authorization.k8s.io/v1', kind: 'Role', metadata: { labels: { app: params.name, }, name: params.name, }, rules: [ { apiGroups: [ '*', ], resources: [ '*', ], verbs: [ '*', ], }, ], }, { apiVersion: 'rbac.authorization.k8s.io/v1', kind: 'RoleBinding', metadata: { labels: { app: params.name, }, name: params.name, }, roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'Role', name: params.name, }, subjects: [ { kind: 'ServiceAccount', name: params.name, namespace: env.namespace, }, ], }, ] 

Wir werden auch die neuen Parameter in environ / base.libsonnet beschreiben , die jetzt so aussehen:


 local secrets = import '../secrets/base.libsonnet'; { components: { gitlabRunner: { name: 'gitlab-runner', values: { gitlabUrl: 'https://gitlab.com/', rbac: { create: true, }, runnerRegistrationToken: secrets.runnerRegistrationToken, runners: { serviceAccountName: $.components.rbac.name, image: 'registry.gitlab.com/kvaps/docs.example.org/toolbox:v0.0.1', }, }, }, rbac: { name: 'gitlab-runner-deploy', }, }, } 

Hinweis $ .components.rbac.name bezieht sich auf den Namen der Komponente rbac


Lassen Sie uns überprüfen, was sich geändert hat:


 qbec diff default 

und wende unsere Ă„nderungen auf Kubernetes an:


 qbec apply default 

Vergessen Sie auch nicht, unsere Ă„nderungen an git vorzunehmen:


 cd ../.. git add dockerfiles/toolbox git commit -m "Add Dockerfile for toolbox" git add deploy/gitlab-runner git commit -m "Configure gitlab-runner to use toolbox" 



9. Unsere erste Pipeline und Assemblierung von Bildern nach Tags


Im Stammverzeichnis des Projekts erstellen wir die Datei .gitlab-ci.yml mit folgendem Inhalt:


 .build_docker_image: stage: build image: name: gcr.io/kaniko-project/executor:debug-v0.15.0 entrypoint: [""] before_script: - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json build_toolbox: extends: .build_docker_image script: - /kaniko/executor --cache --context $CI_PROJECT_DIR/dockerfiles/toolbox --dockerfile $CI_PROJECT_DIR/dockerfiles/toolbox/Dockerfile --destination $CI_REGISTRY_IMAGE/toolbox:$CI_COMMIT_TAG only: refs: - tags build_website: extends: .build_docker_image variables: GIT_SUBMODULE_STRATEGY: normal script: - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_TAG only: refs: - tags 

Bitte beachten Sie, dass wir GIT_SUBMODULE_STRATEGY verwenden: Normal fĂĽr Jobs, bei denen Sie Submodule vor der AusfĂĽhrung explizit initialisieren mĂĽssen.


Vergessen Sie nicht, unsere Ă„nderungen zu ĂĽbernehmen:


 git add .gitlab-ci.yml git commit -m "Automate docker build" 

Ich denke, Sie können es sicher Version v0.0.1 nennen und ein Tag hängen:


 git tag v0.0.1 

Wir werden Tags hängen, wenn wir eine neue Version veröffentlichen müssen. Tags in Docker-Bildern werden an Git-Tags gebunden. Jeder Push mit einem neuen Tag initialisiert die Assemblierung von Bildern mit diesem Tag.


FĂĽhren Sie git push - tags aus und sehen Sie sich unsere erste Pipeline an:


Screenshot der ersten Pipeline


Es ist zu beachten, dass das Assemblieren nach Tags zum Assemblieren von Docker-Images geeignet ist, jedoch nicht zum Bereitstellen einer Anwendung in Kubernetes. Da auch alten Commits neue Tags zugewiesen werden können, führt in diesem Fall die Initialisierung der Pipeline für diese zum Deployment der alten Version.

Um dieses Problem zu lösen, werden in der Regel Docker-Images an Tags angehängt und die Anwendung im Master- Zweig bereitgestellt, in dem Versionen der gesammelten Images fest codiert sind. In diesem Fall können Sie das Rollback mit einem einfachen Master- Tag "Zurücksetzen" initialisieren.



10. Automatisierung der Bereitstellung


Damit Gitlab-runner unsere Geheimnisse entschlĂĽsseln kann, mĂĽssen wir den Repository-SchlĂĽssel exportieren und zu den Umgebungsvariablen unseres CIs hinzufĂĽgen:


 git crypt export-key /tmp/docs-repo.key base64 -w0 /tmp/docs-repo.key; echo 

Speichern Sie den resultierenden String in Gitlab. Gehen Sie dazu zu den Einstellungen unseres Projekts:
Einstellungen -> CI / CD -> Variablen


Und erstelle eine neue Variable:


TypSchlĂĽsselWertGeschĂĽtztMaskiertScope
FileGITCRYPT_KEY<your string>true ( false )trueAll environments


.gitlab-ci.yml :


 .deploy_qbec_app: stage: deploy only: refs: - master deploy_gitlab_runner: extends: .deploy_qbec_app variables: GIT_SUBMODULE_STRATEGY: normal before_script: - base64 -d "$GITCRYPT_KEY" | git-crypt unlock - script: - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes deploy_website: extends: .deploy_qbec_app script: - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes 

qbec:


  • --root some/app —
  • --force:k8s-context __incluster__ — , gtilab-runner. , qbec Kubernetes- kubeconfig
  • --wait — qbec , Ready exit-code.
  • --yes — Are you sure? .

:


 git add .gitlab-ci.yml git commit -m "Automate deploy" 

git push :





11. push master


, . digest master-.


: website push master , Kubernetes.


.gitlab-ci.yml :


 build_website: extends: .build_docker_image variables: GIT_SUBMODULE_STRATEGY: normal script: - mkdir -p $CI_PROJECT_DIR/artifacts - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest artifacts: paths: - artifacts/ only: refs: - master - tags deploy_website: extends: .deploy_qbec_app script: - DIGEST="$(cat artifacts/website.digest)" - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" 

, master refs build_website $CI_COMMIT_REF_NAME $CI_COMMIT_TAG , Git . , , docker-registry.


docker- , Kubernetes, , .


--vm:ext-str digest="$DIGEST" qbec — jsonnet. . , , , .


Kaniko digest ( --digest-file )
.


deploy/website/environments/base.libsonnet :


 { components: { website: { name: 'example-docs', image: 'registry.gitlab.com/kvaps/docs.example.org/website@' + std.extVar('digest'), replicas: 1, containerPort: 80, servicePort: 80, nodeSelector: {}, tolerations: [], ingressClass: 'nginx', domain: 'docs.example.org', }, }, } 

, master docker- website , Kubernetes.


:


 git add . git commit -m "Configure dynamic build" 

, git push - :


master


gitlab-runner push, , , , .gitlab-ci.yml :


 deploy_gitlab_runner: extends: .deploy_qbec_app variables: GIT_SUBMODULE_STRATEGY: normal before_script: - base64 -d "$GITCRYPT_KEY" | git-crypt unlock - script: - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes only: changes: - deploy/gitlab-runner/**/* 

changes deploy/gitlab-runner/


:


 git add .gitlab-ci.yml git commit -m "Reduce gitlab-runner deploy" 

git push , - :





12. Dynamic environments


.


build_website .gitlab-ci.yml , only , Gitlab :


 build_website: extends: .build_docker_image variables: GIT_SUBMODULE_STRATEGY: normal script: - mkdir -p $CI_PROJECT_DIR/artifacts - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest artifacts: paths: - artifacts/ 

deploy_website , environment :


 deploy_website: extends: .deploy_qbec_app environment: name: prod url: https://docs.example.org script: - DIGEST="$(cat artifacts/website.digest)" - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" 

Gitlab prod .


:


 deploy_website: extends: .deploy_qbec_app environment: name: prod url: https://docs.example.org script: - DIGEST="$(cat artifacts/website.digest)" - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" deploy_review: extends: .deploy_qbec_app environment: name: review/$CI_COMMIT_REF_NAME url: http://$CI_ENVIRONMENT_SLUG.docs.example.org on_stop: stop_review script: - DIGEST="$(cat artifacts/website.digest)" - qbec apply review --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG" only: refs: - branches except: refs: - master stop_review: extends: .deploy_qbec_app environment: name: review/$CI_COMMIT_REF_NAME action: stop stage: deploy before_script: - git clone "$CI_REPOSITORY_URL" master - cd master script: - qbec delete review --root deploy/website --force:k8s-context __incluster__ --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG" variables: GIT_STRATEGY: none only: refs: - branches except: refs: - master when: manual 

push master preview .


qbec: --app-tag — , Kubernetes qbec .
review, .


qbec apply review , qbec apply default — (review default):


review deploy/website/qbec.yaml


 spec: environments: review: defaultNamespace: docs server: https://kubernetes.example.org:8443 

deploy/website/params.libsonnet :


 local env = std.extVar('qbec.io/env'); local paramsMap = { _: import './environments/base.libsonnet', default: import './environments/default.libsonnet', review: import './environments/review.libsonnet', }; if std.objectHas(paramsMap, env) then paramsMap[env] else error 'environment ' + env + ' not defined in ' + std.thisFile 

deploy/website/environments/review.libsonnet :


 // this file has the param overrides for the default environment local base = import './base.libsonnet'; local slug = std.extVar('qbec.io/tag'); local subdomain = std.extVar('subdomain'); base { components+: { website+: { name: 'example-docs-' + slug, domain: subdomain + '.docs.example.org', }, }, } 

stop_review , gitlab checkout GIT_STRATEGY: none , master - review .
, .
review , .


:


 git add . git commit -m "Enable automatic review" 

git push , git checkout -b test , git push origin test , :


environments Gitlab


? — , : git checkout master , git push origin :test , environment .


, , .gitlab-ci.yml .
protected-, master , .



13. Review Apps


Review Apps , .


, .gitlab/route-map.yml , :


 # Indices - source: /content\/(.+?)_index\.(md|html)/ public: '\1' # Pages - source: /content\/(.+?)\.(md|html)/ public: '\1/' 

:


 git add .gitlab/ git commit -m "Enable review apps" 

git push , :


Review App


Job is done!


:



, Bild

Source: https://habr.com/ru/post/de481662/


All Articles