Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Hallo! In letzter Zeit wurden viele coole Automatisierungstools sowohl für die Erstellung von Docker-Images als auch für die Bereitstellung auf Kubernetes veröffentlicht. In diesem Zusammenhang habe ich beschlossen, mit Gitlab herumzuspielen, seine Fähigkeiten zu untersuchen und natürlich eine Pipeline einzurichten.

Diese Seite wurde inspiriert von kubernetes.io, die aus generiert wird Quellcodes Automatisch und für jede gesendete Pull-Anfrage generiert der Roboter automatisch eine Vorschauversion der Site mit Ihren Änderungen und stellt einen Link zum Anzeigen bereit.

Ich habe versucht, einen ähnlichen Prozess von Grund auf zu entwickeln, basierte jedoch vollständig auf Gitlab CI und kostenlosen Tools, die ich früher zum Bereitstellen von Anwendungen auf Kubernetes verwendet habe. Heute werde ich euch endlich mehr darüber erzählen.

Der Artikel behandelt Tools wie:
Hugo, qbec, Kaniko, Git-Krypta и GitLab-CI mit der Schaffung dynamischer Umgebungen.

Inhalt

  1. Hugo kennenlernen
  2. Vorbereiten der Docker-Datei
  3. Kaniko kennenlernen
  4. Einführung in qbec
  5. Gitlab-Runner mit Kubernetes-Executor ausprobieren
  6. Stellen Sie Helm-Charts mit qbec bereit
  7. Einführung in Git-Crypt
  8. Erstellen Sie ein Toolbox-Bild
  9. Unsere erste Pipeline und Zusammenstellung von Bildern nach Tags
  10. Setzen Sie Automatisierung ein
  11. Artefakte und Montage beim Schieben zum Meister
  12. Dynamische Umgebungen
  13. Apps überprüfen

1. Hugo kennenlernen

Als Beispiel für unser Projekt werden wir versuchen, eine auf Hugo basierende Dokumentationsveröffentlichungsseite zu erstellen. Hugo ist ein statischer Inhaltsgenerator.

Für diejenigen, die mit statischen Generatoren nicht vertraut sind, werde ich etwas mehr darüber erzählen. Im Gegensatz zu normalen Site-Engines mit einer Datenbank und einer Art PHP, die auf Anfrage des Benutzers Seiten im Handumdrehen generieren, sind statische Generatoren etwas anders angeordnet. Sie ermöglichen es Ihnen, den Quellcode, normalerweise eine Reihe von Dateien in Markdown-Markup- und Theme-Vorlagen, zu übernehmen und sie dann zu einer vollständig fertigen Site zu kompilieren.

Das heißt, am Ausgang erhalten Sie eine Verzeichnisstruktur und eine Reihe generierter HTML-Dateien, die Sie einfach auf ein beliebiges günstiges Hosting hochladen können und eine funktionierende Website erhalten.

Sie können Hugo lokal installieren und ausprobieren:

Initialisieren der neuen Site:

hugo new site docs.example.org

Und gleichzeitig das Git-Repository:

cd docs.example.org
git init

Bisher ist unsere Website makellos und damit etwas darauf erscheint, müssen wir zunächst ein Thema verbinden. Ein Thema ist nur eine Reihe von Vorlagen und festgelegten Regeln, nach denen unsere Website erstellt wird.

Als Thema werden wir verwenden Lernen Sie, was meiner Meinung nach am besten für eine Site mit Dokumentation geeignet ist.

Besonders hervorheben möchte ich, dass wir die Theme-Dateien nicht im Repository unseres Projekts speichern müssen, sondern diese einfach über anbinden können git-Submodul:

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 verbundene Thema verbleibt als Link zu einem bestimmten Repository und als Commit darin, das heißt, es kann immer ohne Angst aus der Originalquelle abgerufen werden von inkompatiblen Änderungen.

Lassen Sie uns die Konfiguration korrigieren config.toml:

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

Bereits zu diesem Zeitpunkt können Sie Folgendes ausführen:

hugo server

Und an der Adresse http://localhost:1313/ Schauen Sie sich unsere neu erstellte Website an. Alle im Verzeichnis vorgenommenen Änderungen aktualisieren automatisch die geöffnete Seite im Browser, sehr praktisch!

Versuchen wir, eine Titelseite zu erstellen content/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Screenshot der neu erstellten Seite

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Um eine Site zu erstellen, führen Sie einfach Folgendes aus:

hugo

Verzeichnisinhalt Öffentlichkeit/ und wird Ihre Website sein.
Ja, übrigens, bringen wir es gleich rein .gitignore:

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 Repositorys zu definieren. Normalerweise verwende ich so etwas wie:

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

  • Dockerfiles/ - enthalten Verzeichnisse mit Docker-Dateien und allem, was zum Erstellen unserer Docker-Images benötigt wird.
  • einsetzen/ - enthält Verzeichnisse für die Bereitstellung unserer Anwendungen in Kubernetes

Daher werden wir nebenbei unsere erste Docker-Datei erstellen 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 können, enthält die Docker-Datei zwei AB, heißt diese Möglichkeit mehrstufiger Aufbau und ermöglicht es Ihnen, alles Unnötige aus dem endgültigen Docker-Image auszuschließen.
Somit enthält das endgültige Bild nur dunkelhttpd (leichter HTTP-Server) und Öffentlichkeit/ - der Inhalt unserer statisch generierten Seite.

Vergessen Sie nicht, unsere Änderungen zu übernehmen:

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

3. Kaniko kennenlernen

Als Ersteller von Docker-Images habe ich mich für die Verwendung entschieden Kaniko, da für die Funktion kein Docker-Daemon erforderlich ist und die Montage selbst auf jedem Computer durchgeführt und der Cache direkt in der Registrierung gespeichert werden kann, sodass kein vollwertiger persistenter Speicher erforderlich ist.

Um das Image zu erstellen, führen Sie einfach den Container mit aus Kaniko-Vollstrecker und den aktuellen Build-Kontext an ihn übergeben, Sie können dies 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

Wo Registry.gitlab.com/kvaps/docs.example.org/website – Der Name Ihres Docker-Images. Nach der Erstellung wird es automatisch in der Docker-Registrierung gestartet.

Parameter --Zwischenspeicher Ermöglicht das Zwischenspeichern von Ebenen in der Docker-Registrierung, in denen sie im angegebenen Beispiel gespeichert werden Registry.gitlab.com/kvaps/docs.example.org/website/cache, aber Sie können mit dem Parameter einen anderen Pfad angeben --cache-repo.

Screenshot der Docker-Registrierung

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

4. Einführung in qbec

Qbec ist ein Bereitstellungstool, mit dem Sie Ihre Anwendungsmanifeste deklarativ beschreiben und in Kubernetes bereitstellen können. Die Verwendung von Jsonnet als Hauptsyntax erleichtert die Beschreibung der Unterschiede für mehrere Umgebungen und eliminiert Codewiederholungen fast vollständig.

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 auch Helm-Diagramme rendern, indem Sie ihnen die erforderlichen Parameter übergeben und sie dann auf die gleiche Weise wie normale Manifeste bearbeiten, einschließlich der Möglichkeit, verschiedene Mutationen auf sie anzuwenden, wodurch wiederum die Verwendung von ChartMuseum überflüssig wird. Das heißt, Sie können Diagramme direkt aus Git dort speichern und rendern, wo sie hingehören.

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

mkdir deploy
cd deploy

Lassen Sie uns unsere erste Anwendung initialisieren:

qbec init website
cd website

Nun sieht der Aufbau unserer Anwendung so aus:

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

schau dir die Datei an qbec.yaml:

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

Hier interessiert uns vor allem spez. Umgebungen, qbec hat bereits die Standardumgebung für uns erstellt und die Serveradresse und den Namespace aus unserer aktuellen kubeconfig übernommen.
Jetzt bei der Bereitstellung Standard Umgebung wird qbec immer nur im angegebenen Kubernetes-Cluster und im angegebenen Namespace bereitgestellt, d. h. Sie müssen für die Bereitstellung nicht mehr zwischen Kontexten und Namespaces wechseln.
Bei Bedarf können Sie die Einstellungen in dieser Datei jederzeit aktualisieren.

Alle Ihre Umgebungen sind in beschrieben qbec.yaml, und in der Datei params.libsonnet, die besagt, wo Sie die Parameter dafür nehmen 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 regulären Yaml-Dateien beschrieben werden
  • Umgebungen/ - Hier beschreiben wir alle Variablen (Parameter) für unsere Umgebungen.

Standardmäßig haben wir zwei Dateien:

  • Umgebungen/base.libsonnet - Es enthält gemeinsame Parameter für alle Umgebungen
  • Umgebungen/default.libsonnet - enthält für die Umgebung neu definierte Parameter Standard

Öffnen wir Umgebungen/base.libsonnet und fügen Sie dort 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',
    },
  },
}

Lassen Sie uns auch unsere erste Komponente erstellen Komponenten/Website.jsonnet:

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 haben wir drei Kubernetes-Entitäten gleichzeitig beschrieben: Einsatz, Service и Eintritt. Auf Wunsch könnten wir sie in verschiedene Komponenten verschieben, aber in diesem Stadium reicht uns eine.

Syntax jsonnet Sehr ähnlich zu regulärem JSON. Im Prinzip ist reguläres JSON bereits ein gültiges JSONNET, sodass es für Sie zunächst möglicherweise einfacher ist, Online-Dienste wie zu nutzen yaml2json Um Ihr übliches Yaml in JSON zu konvertieren, oder wenn Ihre Komponenten keine Variablen enthalten, können sie in Form von regulärem Yaml beschrieben werden.

Bei der Arbeit mit jsonnet Ich empfehle Ihnen dringend, ein Plugin für Ihren Editor zu installieren

Es gibt zum Beispiel ein Plugin für vim vim-jsonnet, wodurch die Syntaxhervorhebung aktiviert und automatisch ausgeführt wird jsonnet fmt bei jedem Speichern (erfordert die Installation von jsonnet).

Alles ist bereit, jetzt können wir mit der Bereitstellung beginnen:

Um zu sehen, was wir haben, führen wir Folgendes aus:

qbec show default

In der Ausgabe sehen Sie die gerenderten Yaml-Manifeste, die auf den Standardcluster angewendet werden.

Ok, jetzt bewerben Sie sich:

qbec apply default

Auf der Ausgabe sehen Sie immer, was in Ihrem Cluster getan wird, qbec fordert Sie durch Eingabe auf, die Änderungen zu akzeptieren y Sie können Ihre Absichten bestätigen.

Fertig, jetzt ist unsere App bereitgestellt!

Wenn Änderungen vorgenommen werden, können Sie jederzeit Folgendes ausführen:

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 nur normales verwendet Gitlab-Runner auf einer vorbereiteten Maschine (LXC-Container) mit Shell- oder Docker-Executor. Anfangs hatten wir mehrere dieser Läufer global in unserem Gitlab definiert. Sie haben Docker-Images für alle Projekte erstellt.

Doch wie die Praxis gezeigt hat, ist diese Option sowohl hinsichtlich der Praktikabilität als auch hinsichtlich der Sicherheit nicht die idealste. Es ist viel besser und ideologisch korrekt, für jedes Projekt und sogar für jede Umgebung separate Läufer einzusetzen.

Glücklicherweise stellt dies überhaupt kein Problem dar, da wir jetzt mit der Bereitstellung beginnen Gitlab-Runner direkt im Rahmen unseres Projekts direkt in Kubernetes.

Gitlab bietet ein vorgefertigtes Helmdiagramm für die Bereitstellung von Gitlab-Runner auf Kubernetes. Alles was Sie also wissen müssen ist Registrierungs-Token für unser Projekt in Einstellungen -> CI/CD -> Läufer und gib es an das Ruder weiter:

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 - Registrierungstoken für Ihr Projekt.
  • rbac.create=true – gibt dem Läufer die erforderliche Anzahl an Berechtigungen, um Pods erstellen zu können, um unsere Aufgaben mit dem Kubernetes-Executor auszuführen.

Wenn alles richtig gemacht wurde, sollten Sie im Abschnitt den registrierten Läufer sehen Runners, in Ihren Projekteinstellungen.

Screenshot des hinzugefügten Läufers

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Ist das so einfach? - Ja, so einfach ist das! Kein Aufwand mehr mit der manuellen Registrierung von Runnern, denn von nun an werden Runner automatisch erstellt und gelöscht.

6. Stellen Sie Helm-Charts mit QBEC bereit

Da haben wir uns entschieden, darüber nachzudenken Gitlab-Runner Da es Teil unseres Projekts ist, ist es an der Zeit, es in unserem Git-Repository zu beschreiben.

Wir könnten es als separate Komponente beschreiben Website , aber wir planen, in Zukunft andere Kopien bereitzustellen Website sehr oft, anders als Gitlab-Runner, die nur einmal pro Kubernetes-Cluster bereitgestellt wird. Lassen Sie uns also 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 vorgefertigtes Helm-Diagramm verwenden. Einer der Vorteile von qbec ist die Möglichkeit, Helm-Diagramme direkt aus einem Git-Repository zu rendern.

Aktivieren wir es mit dem Git-Submodul:

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

Jetzt das Verzeichnis Anbieter/Gitlab-Runner enthält unser Repository mit einem Diagramm für Gitlab-Runner.

Andere Repositorien können auf ähnliche Weise angebunden werden, beispielsweise das gesamte Repositorium mit amtlichen Seekarten https://github.com/helm/charts

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

Das erste Argument dazu expandHelmTemplate Wir geben dann den Pfad zum Diagramm weiter params.values, die wir aus den Umgebungsparametern übernehmen, dann kommt das Objekt mit

  • nameTemplate - Veröffentlichungsname
  • Namensraum - Namespace an Helm übergeben
  • Diese Datei – ein erforderlicher Parameter, der den Pfad zur aktuellen Datei übergibt
  • ausführlich - Zeigt den Befehl an Helmvorlage mit allen Argumenten beim Rendern eines Diagramms

Beschreiben wir nun die Parameter für unsere Komponente in Umgebungen/base.libsonnet:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
      },
    },
  },
}

Beachten runnerRegistrationToken Wir holen aus einer externen Datei Secrets/base.libsonnet, lass es uns erstellen:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Schauen wir mal, ob alles funktioniert:

qbec show default

Wenn alles in Ordnung ist, können wir unsere zuvor über Helm bereitgestellte Version entfernen:

helm uninstall gitlab-runner

und stellen Sie es bereit, aber ü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 unsere Verzeichnisstruktur 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)

Aber das Speichern von Geheimnissen in Git ist nicht sicher, oder? Wir müssen sie also richtig verschlüsseln.

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

Außerdem könnte ich Ihnen in diesem Fall nichts über ein so wunderbares Werkzeug wie sagen Git-Krypta.

Git-Krypta Es ist auch insofern praktisch, als es Ihnen ermöglicht, den gesamten Verlauf der Geheimnisse zu speichern sowie Konflikte auf die gleiche Weise zu vergleichen, zusammenzuführen und zu lösen, wie wir es im Fall von Git getan haben.

Das Erste nach der Installation Git-Krypta Wir müssen Schlüssel für unser Repository generieren:

git crypt init

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

git-crypt add-gpg-user [email protected]

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

Wenn Sie keinen PGP-Schlüssel haben und dies auch nicht erwartet wird, können Sie den umgekehrten Weg gehen und den Projektschlüssel exportieren:

git crypt export-key /path/to/keyfile

Also jeder, der ein Export besitzt Schlüsseldatei wird in der Lage sein, Ihr Repository zu entschlüsseln.

Es ist Zeit, unser erstes Geheimnis zu lüften.
Ich möchte Sie daran erinnern, dass wir uns noch im Verzeichnis befinden Deploy/gitlab-runner/wo wir ein Verzeichnis haben Geheimnisse/, verschlüsseln wir alle darin enthaltenen Dateien, dafür erstellen wir eine Datei Geheimnisse/.gitattributes mit Inhalten wie diesem:

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

Wie aus dem Inhalt hervorgeht, sind alle Dateien nach Maske sortiert * wird durchlaufen Git-Krypta, mit Ausnahme der .gittributes

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

git crypt status -e

Als Ausgabe erhalten wir eine Liste aller Dateien im Repository, für die die Verschlüsselung aktiviert ist

Das war's, jetzt können wir unsere Änderungen sicher übernehmen:

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

Um das Repository zu blockieren, reicht es aus, Folgendes auszuführen:

git crypt lock

und sofort werden alle verschlüsselten Dateien zu einem binären Etwas, das nicht mehr gelesen werden kann.
Um das Repository zu entschlüsseln, führen Sie Folgendes aus:

git crypt unlock

8. Erstellen Sie ein Toolbox-Bild

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

Hier ist alles einfach, wir schaffen etwas Neues dockerfiles/toolbox/Dockerfile mit Inhalten wie diesem:

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 können, installieren wir in diesem Bild alle Dienstprogramme, die wir zur Bereitstellung unserer Anwendung verwendet haben. Wir brauchen hier nicht, es sei denn kubectl, aber vielleicht möchten Sie beim Einrichten der Pipeline damit herumspielen.

Um mit Kubernetes kommunizieren und dort bereitstellen zu können, müssen wir außerdem eine Rolle für die von gitlab-runner generierten Pods einrichten.

Gehen Sie dazu mit gitlab-runner'om in das Verzeichnis:

cd deploy/gitlab-runner

und fügen Sie eine neue Komponente hinzu Komponenten/rbac.jsonnet:

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 beschreiben auch die neuen Parameter in Umgebungen/base.libsonnet, was jetzt so aussieht:

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

Beachten $.components.rbac.name bezieht sich auf Name für Komponente rbac

Schauen wir uns an, was sich geändert hat:

qbec diff default

und wenden Sie unsere Änderungen auf Kubernetes an:

qbec apply default

Vergessen Sie auch nicht, unsere Änderungen an Git zu übergeben:

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 Zusammenstellung von Bildern nach Tags

An der Wurzel des Projekts werden wir erstellen .gitlab-ci.yml mit Inhalten wie diesem:

.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 verwenden GIT_SUBMODULE_STRATEGY: 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, man kann es getrost als Version bezeichnen v0.0.1 und fügen Sie ein Tag hinzu:

git tag v0.0.1

Wir werden Tags anbringen, wann immer wir eine neue Version veröffentlichen müssen. Tags in Docker-Images werden Git-Tags zugeordnet. Jeder Push mit einem neuen Tag initialisiert einen Image-Build mit diesem Tag.

Lass es uns tun git push --tags, und schauen Sie sich unsere erste Pipeline an:

Screenshot der ersten Pipeline

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Es ist erwähnenswert, dass Tag-basierte Builds gut zum Erstellen von Docker-Images geeignet sind, nicht jedoch zum Bereitstellen einer Anwendung auf 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, ist die Erstellung von Docker-Images normalerweise an Tags und die Bereitstellung der Anwendung im Zweig gebunden Master, in dem die Versionen der gesammelten Bilder fest codiert sind. In diesem Fall können Sie das Rollback mit einem einfachen Zurücksetzen initialisieren Master-Geäst.

10. Automatisierung bereitstellen

Damit Gitlab-Runner unsere Geheimnisse entschlüsseln kann, müssen wir den Repository-Schlüssel exportieren und ihn zu unseren CI-Umgebungsvariablen hinzufügen:

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

Den resultierenden String speichern wir in Gitlab, dazu gehen wir zu den Einstellungen unseres Projekts:
Einstellungen —> CI/CD —> Variablen

Und erstellen Sie eine neue Variable:

Typ
Wesentliche
Wert
Geschützt
Maskiert
Geltungsbereich

File
GITCRYPT_KEY
<your string>
true (Zum Zeitpunkt des Trainings können Sie false)
true
All environments

Screenshot der hinzugefügten Variable

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Jetzt aktualisieren wir unsere .gitlab-ci.yml ergänzend:

.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

Hier haben wir einige neue Optionen für qbec aktiviert:

  • --root einige/app - ermöglicht Ihnen, das Verzeichnis einer bestimmten Anwendung zu definieren
  • --force:k8s-context __incluster__ – Dies ist eine magische Variable, die besagt, dass die Bereitstellung in demselben Cluster erfolgt, in dem gtilab-runner ausgeführt wird. Dies ist notwendig, sonst versucht qbec, einen passenden Kubernetes-Server in Ihrer kubeconfig zu finden
  • -Warten – zwingt qbec, zu warten, bis die von ihm erstellten Ressourcen in den Status „Bereit“ gehen, und erst dann mit einem erfolgreichen Exit-Code abzuschließen.
  • -Ja - Deaktiviert lediglich die interaktive Shell Bist du sicher? während des Einsatzes.

Vergessen Sie nicht, unsere Änderungen zu übernehmen:

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

Und danach Git drücken Wir werden sehen, wie unsere Anwendungen bereitgestellt wurden:

Screenshot der zweiten Pipeline

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

11. Artefakte und Zusammenbau beim Pushen zum Meister

Normalerweise reichen die oben genannten Schritte aus, um fast jeden Microservice zu erstellen und bereitzustellen, aber wir möchten nicht jedes Mal ein Tag hinzufügen, wenn wir die Site aktualisieren müssen. Daher werden wir dynamischer vorgehen und eine Digest-Bereitstellung im Master-Zweig einrichten.

Die Idee ist einfach: Jetzt ist das Bild von uns Website wird jedes Mal neu erstellt, wenn Sie darauf drücken Masterund dann automatisch auf Kubernetes bereitstellen.

Lassen Sie uns diese beiden Jobs in unserem aktualisieren .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"

Bitte beachten Sie, dass wir eine Filiale hinzugefügt haben Master к Ref für die Arbeit build_website und wir verwenden jetzt $CI_COMMIT_REF_NAME statt $CI_COMMIT_TAG, das heißt, wir entfernen die Tags in Git und übertragen nun das Bild mit dem Namen des Commit-Zweigs, der Ihre Pipeline initialisiert hat. Es ist erwähnenswert, dass dies auch mit Tags funktioniert, die es uns ermöglichen, Snapshots der Site mit einer bestimmten Version in der Docker-Registrierung zu speichern.

Wenn der Name des Docker-Tags für die neue Version der Site unverändert bleiben kann, müssen wir die Änderungen für Kubernetes noch beschreiben, andernfalls wird die Anwendung einfach nicht aus dem neuen Image erneut bereitgestellt, da keine Änderungen in der Site bemerkt werden Bereitstellungsmanifest.

Option --vm:ext-str summary="$DIGEST" für qbec – ermöglicht die Übergabe einer externen Variablen an jsonnet. Wir möchten, dass unsere Anwendung bei jedem Release erneut im Cluster bereitgestellt wird. Wir können den Tag-Namen nicht mehr verwenden, der jetzt unverändert bleiben kann, da wir eine Verknüpfung zu einer bestimmten Version des Bildes herstellen und die Bereitstellung auslösen müssen, wenn sie sich ändert.

Hier hilft uns Kanikos Fähigkeit, den Digest des Bildes in einer Datei zu speichern (Option --digest-file)
Anschließend übertragen wir diese Datei und lesen sie zum Zeitpunkt der Bereitstellung.

Lassen Sie uns die Parameter für unsere aktualisieren Deploy/website/environments/base.libsonnet was nun so aussehen wird:

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

Fertig, jetzt ist ein Commit möglich Master Initialisiert den Build des Docker-Images für Website , und stellen Sie es dann auf Kubernetes bereit.

Vergessen Sie nicht, unsere Änderungen zu übernehmen:

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

Schauen Sie es sich danach an Git drücken wir sollten so etwas sehen:

Pipeline-Screenshot für Master

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Im Prinzip müssen wir Gitlab-Runner nicht bei jedem Push erneut bereitstellen, es sei denn, es hat sich natürlich nichts an seiner Konfiguration geändert. Lassen Sie uns dies beheben .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/**/*

Änderungen wird die Änderungen im Auge behalten Deploy/gitlab-runner/ und wird unseren Job nur dann auslösen, wenn es welche gibt

Vergessen Sie nicht, unsere Änderungen zu übernehmen:

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

Git drücken, das ist besser:

Screenshot der aktualisierten Pipeline

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

12. Dynamische Umgebungen

Es ist an der Zeit, unsere Pipeline mit dynamischen Umgebungen zu diversifizieren.

Lassen Sie uns zunächst den Job aktualisieren build_website in unserem .gitlab-ci.yml, den Block daraus entfernen einzige, was Gitlab dazu zwingt, es bei jedem Commit für einen Zweig auszulösen:

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/

Aktualisieren Sie dann den Job Deploy_Website, fügen Sie dort einen Block hinzu Umwelt:

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"

Dadurch kann Gitlab den Job verknüpfen Stoß Umgebung und zeigt den richtigen Link dazu an.

Fügen wir nun zwei weitere Jobs hinzu:

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

Sie werden durch Push an alle Filialen außer Master ausgelöst und stellen eine Vorschauversion der Site bereit.

Wir sehen eine neue Option für qbec: --app-tag - Es ermöglicht Ihnen, bereitgestellte Versionen der Anwendung zu kennzeichnen und nur innerhalb dieses Tags zu arbeiten; beim Erstellen und Zerstören von Ressourcen in Kubernetes wird qbec nur diese bearbeiten.
Daher können wir nicht für jede Überprüfung eine separate Umgebung erstellen, sondern einfach dieselbe wiederverwenden.

Hier verwenden wir auch Qbec-Überprüfung anwendenstatt qbec wendet Standard an - Dies ist genau der Moment, in dem wir versuchen werden, die Unterschiede für unsere Umgebungen (Überprüfung und Standard) zu beschreiben:

Hinzufügen Überprüfen Umgebung in Deploy/website/qbec.yaml

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

Dann erklären wir es 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

Und schreiben Sie benutzerdefinierte Parameter dafür ein 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',
    },
  },
}

Schauen wir uns auch den Beruf genauer an stop_review, wird es ausgelöst, wenn der Zweig entfernt wird und so dass Gitlab nicht versucht, darauf auszuchecken GIT_STRATEGY: keine, später klonen wir Master-Verzweigen und Bewertung darüber löschen.
Etwas verwirrend, aber einen schöneren Weg habe ich noch nicht gefunden.
Eine alternative Möglichkeit wäre, jede Bewertung in einem Hotel-Namespace bereitzustellen, der jederzeit vollständig gelöscht werden kann.

Vergessen Sie nicht, unsere Änderungen zu übernehmen:

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

Git drücken, git checkout -b test, Git-Push-Ursprungstest, überprüfen:

Screenshot der erstellten Umgebungen in Gitlab

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Alles arbeitet? - Großartig, löschen Sie unseren Testzweig: Git Kasse Master, git push origin :testüberprüfen wir, ob die Jobs zum Löschen der Umgebung fehlerfrei funktioniert haben.

Hier möchte ich gleich klarstellen, dass jeder Entwickler im Projekt Zweige erstellen und auch ändern kann .gitlab-ci.yml Datei- und Zugriffsgeheimnisvariablen.
Daher wird dringend empfohlen, ihre Verwendung nur für geschützte Zweige zuzulassen, beispielsweise in Master, oder erstellen Sie einen separaten Satz Variablen für jede Umgebung.

13 Bewertungs-Apps

Apps überprüfen Dies ist eine Gitlab-Funktion, mit der Sie für jede Datei im Repository eine Schaltfläche hinzufügen können, um sie schnell in der bereitgestellten Umgebung anzuzeigen.

Damit diese Schaltflächen angezeigt werden, müssen Sie eine Datei erstellen .gitlab/route-map.yml und beschreiben Sie darin alle Transformationen der Pfade, in unserem Fall wird es sehr einfach sein:

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

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

Vergessen Sie nicht, unsere Änderungen zu übernehmen:

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

Git drücken, und prüfe:

Screenshot der Schaltfläche „App überprüfen“.

Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Arbeit ist erledigt!

Projektquellen:

Vielen Dank für Ihre Aufmerksamkeit, ich hoffe, es hat Ihnen gefallen Ausprobieren neuer Tools zum Erstellen und Automatisieren der Bereitstellung in Kubernetes

Source: habr.com

Kommentar hinzufügen