Bereitstellung von Anwendungen auf mehreren Kubernetes-Clustern mit Helm

Wie Dailymotion Kubernetes nutzt: Anwendungen bereitstellen

Wir bei Dailymotion haben vor 3 Jahren begonnen, Kubernetes in der Produktion zu verwenden. Das Bereitstellen von Anwendungen auf mehreren Clustern war jedoch eine Herausforderung, weshalb wir in den letzten Jahren daran gearbeitet haben, unsere Tools und Arbeitsabläufe zu verbessern.

Wie alles begann

Hier erklären wir, wie wir unsere Anwendungen auf mehreren Kubernetes-Clustern weltweit bereitstellen.

Um mehrere Kubernetes-Objekte gleichzeitig bereitzustellen, verwenden wir Helm, und alle unsere Charts werden in einem Git-Repository gespeichert. Um einen vollständigen Anwendungs-Stack aus mehreren Diensten bereitzustellen, nutzen wir ein sogenanntes Oberchart. Im Grunde ist dies ein Chart, das Abhängigkeiten deklariert und es ermöglicht, API und ihre Dienste mit einem einzigen Befehl zu initialisieren.

Zusätzlich haben wir ein kleines Python-Skript über Helm geschrieben, um Prüfungen durchzuführen, Charts zu erstellen, Geheimnisse hinzuzufügen und Anwendungen bereitzustellen. All diese Aufgaben werden auf einer zentralen CI-Plattform mithilfe eines Docker-Images erledigt.

Lassen Sie uns zur Sache kommen.

Hinweis: Während Sie dies lesen, wurde bereits der erste Release-Kandidat von Helm 3 angekündigt. Die Hauptversion enthält eine Reihe von Verbesserungen, die darauf abzielen, einige der Probleme zu lösen, mit denen wir zuvor konfrontiert waren.

Entwicklungsworkflow für Charts

Für Anwendungen verwenden wir Branching und haben beschlossen, denselben Ansatz für Charts anzuwenden.

  • Branch dev wird verwendet, um Charts zu erstellen, die in Entwicklungsclustern getestet werden.
  • Wenn ein Pull-Request an masterübergeben wird, erfolgt die Überprüfung in der Staging-Umgebung.
  • Schließlich erstellen wir einen Pull-Request, um die Änderungen in den Branch prod zu übertragen und sie in der Produktion anzuwenden.

Jede Umgebung hat ihr eigenes privates Repository, das unsere Charts speichert, und wir verwenden Chartmuseum mit äußerst nützlichen APIs. So gewährleisten wir eine strikte Isolation zwischen den Umgebungen und testen die Charts unter realen Bedingungen, bevor wir sie in der Produktion verwenden.

Chart-Repositories in verschiedenen Umgebungen

Es ist wichtig zu erwähnen, dass, wenn Entwickler einen dev-Branch einreichen, die Version ihres Charts automatisch in das dev Chartmuseum hochgeladen wird. So benutzen alle Entwickler ein gemeinsames dev-Repository und müssen ihre Chart-Version genau angeben, um versehentlich nicht die Änderungen anderer zu verwenden.

Darüber hinaus überprüft unser kleines Python-Skript Kubernetes-Objekte anhand der Kubernetes OpenAPI-Spezifikationen mit Kubeval, bevor sie im Chartmuseum veröffentlicht werden.

Allgemeine Beschreibung des Chart-Entwicklungsworkflows

  1. Konfiguration der Pipeline-Aufgaben gemäß den Spezifikationen gazr.io zur Qualitätssicherung (Linting, Unit-Test).
  2. Hochladen des Docker-Images mit den Python-Tools, die unsere Anwendungen bereitstellen.
  3. Einrichten der Umgebung basierend auf dem Branch-Namen.
  4. Überprüfung der Kubernetes-YAML-Dateien mit Kubeval.
  5. Automatische Erhöhung der Chart-Version und der seiner übergeordneten Charts (Charts, die von dem veränderbaren Chart abhängen).
  6. Hochladen des Charts ins Chartmuseum, das mit seiner Umgebung übereinstimmt.

Verwaltung der Unterschiede in Clustern

Cluster-Föderation

Es gab eine Zeit, als wir die Föderation von Kubernetes-Clustern verwendet haben., wo man Kubernetes-Objekte von einem einzigen API-Endpunkt aus deklarieren konnte. Doch es gab Probleme. Einige Kubernetes-Objekte konnten beispielsweise nicht am Föderationsendpunkt erstellt werden, was die Verwaltung von kombinierten Objekten und anderen für einzelne Cluster erschwerte.

Um das Problem zu lösen, begannen wir, die Cluster unabhängig zu verwalten, was den Prozess erheblich vereinfachte (wir nutzten die erste Version der Föderation; in der zweiten Version könnten Änderungen vorgenommen worden sein).

Geodistribuierte Plattform

Unsere Plattform ist derzeit über 6 Regionen verteilt – 3 lokal und 3 in der Cloud.


Verteilte Bereitstellung

Globale Helm-Werte

4 globale Helm-Werte ermöglichen es, Unterschiede zwischen Clustern zu definieren. Für alle unsere Charts gibt es minimale Standardwerte.

global:
  cloud: True
  env: staging
  region: us-central1
  clusterName: staging-us-central1

Globale Werte

Diese Werte helfen, den Kontext für unsere Anwendungen zu definieren und werden für verschiedene Aufgaben verwendet: Überwachung, Verfolgung, Protokollierung, externe Aufrufe, Skalierung usw.

  • «cloud»: Wir bieten eine hybride Kubernetes-Plattform an. Unser API wird beispielsweise in GCP-Zonen sowie in unseren Rechenzentren bereitgestellt.
  • «env»: Einige Werte können für nicht-funktionierende Umgebungen variieren. Zum Beispiel die Definitionen von Ressourcen und die Konfiguration der automatischen Skalierung.
  • «region»: Diese Information hilft, den Standort des Clusters zu bestimmen und kann verwendet werden, um die nächstgelegenen Endpunkte für externe Dienste zu ermitteln.
  • «clusterName»: wenn und wann wir einen Wert für ein einzelnes Cluster festlegen möchten.

Hier ist ein konkretes Beispiel:

{{/* Gibt die Horizontal Pod Autoscaler-Replikate für GraphQL zurück */}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}

Beispiel für eine Helm-Vorlage

Diese Logik ist in einer Hilfs-Vorlage definiert, um das Kubernetes YAML nicht zu überfüllen.

Anwendungserklärung

Unsere Bereitstellungstools basieren auf mehreren YAML-Dateien. Unten sehen Sie ein Beispiel dafür, wie wir einen Dienst und dessen Skalierungstopologie (Anzahl der Replikate) im Cluster deklarieren.

Releases:
  - foo.world

foo.world:                # Veröffentlichungsname
  services:               # Liste der Dailymotion Apps/Projekte
    foobar:
      chart_name: foo-foobar
      repo: git@github.com:dailymotion/foobar
      contexts:
        prod-europe-west1:
          deployments:
            - name: foo-bar-baz
              replicas: 18
            - name: another-deployment
              replicas: 3

Definition des Dienstes

Dies ist das Schema aller Schritte, die unseren Bereitstellungsprozess definieren. Der letzte Schritt stellt die Anwendung gleichzeitig auf mehreren Arbeitsclustern bereit.


Bereitstellungsschritte in Jenkins

Und die Geheimnisse?

Was die Sicherheit angeht, verfolgen wir alle Geheimnisse aus verschiedenen Quellen und speichern sie in einem einzigartigen Speicher. Vault in Paris.

Unsere Bereitstellungstools extrahieren die Werte der Geheimnisse aus dem Vault und fügen sie, wenn es Zeit für die Bereitstellung ist, in Helm ein.

Zu diesem Zweck haben wir eine Zuordnung zwischen den Geheimnissen im Vault und den geheimen Informationen definiert, die unsere Anwendungen benötigen:

geheimnisse:                                                                                                                                                                                                        
     - geheimnis_id: "stack1-app1-passwort"                                                                                                                                                                                  
       kontexte:                                                                                                                                                                                                   
         - name: "standard"                                                                                                                                                                                         
           vaultPfad: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultSchlüssel: "passwort"                                                                                                                                                                                    
         - name: "cluster1"                                                                                                                                                                           
           vaultPfad: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultSchlüssel: "passwort"

  • Wir haben allgemeine Richtlinien festgelegt, die beim Speichern von Geheimnissen im Vault beachtet werden müssen.
  • Wenn das Geheimnis sich auf einen bestimmten Kontext oder Cluster bezieht, Sie müssen einen spezifischen Eintrag hinzufügen. (Hier hat der Kontext cluster1 einen eigenen Wert für das Geheimnis stack-app1-password).
  • Andernfalls wird der Wert standardmäßig verwendet.
  • Für jeden Punkt in dieser Liste wird das Kubernetes-Geheimnis als Schlüssel-Wert-Paar eingefügt. Daher ist die Vorlage für das Geheimnis in unseren Charts sehr einfach.

apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
  {{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
  name: "{{ .Chart.Name }}"
  labels:
    chartVersion: "{{ .Chart.Version }}"
    tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: Opaque

Probleme und Einschränkungen

Arbeiten mit mehreren Repositories

Derzeit trennen wir die Entwicklung von Charts und Anwendungen. Das bedeutet, dass Entwickler in zwei Git-Repositories arbeiten müssen: eines für die Anwendung und das andere für die Definition ihrer Bereitstellung in Kubernetes. Zwei Git-Repositories bedeuten zwei Arbeitsabläufe, und es ist für einen Neuling leicht, sich darin zu verwirren.

Allgemeine Charts zu verwalten ist mühsam

Wie bereits erwähnt, sind allgemeine Charts sehr praktisch zur Definition von Abhängigkeiten und zur schnellen Bereitstellung mehrerer Anwendungen. Aber wir verwenden --reuse-values, um zu vermeiden, dass alle Werte bei jeder Bereitstellung der in diesem generischen Chart enthaltenen Anwendung übertragen werden.

Im kontinuierlichen Lieferprozess haben wir insgesamt nur zwei Werte, die sich regelmäßig ändern: die Anzahl der Replikate und das Image-Tag (Version). Andere, stabilere Werte werden manuell geändert, was ziemlich kompliziert ist. Darüber hinaus kann ein Fehler bei der Bereitstellung des generischen Charts zu schwerwiegenden Ausfällen führen, wie wir aus eigener Erfahrung lernen mussten.

Aktualisierung mehrerer Konfigurationsdateien

Wenn ein Entwickler eine neue Anwendung hinzufügt, muss er mehrere Dateien ändern: die Anwendungsdeklaration, die Liste der Geheimnisse, die Hinzufügung der Anwendung zu den Abhängigkeiten, wenn sie im generischen Chart enthalten ist.

Die Berechtigungen von Jenkins sind im Vault zu weitreichend

Jetzt haben wir einen AppRole, der alle Geheimnisse aus dem Vault liest.

Der Rollback-Prozess ist nicht automatisiert

Für einen Rollback muss ein Befehl auf mehreren Clustern ausgeführt werden, was Fehler mit sich bringen kann. Wir führen diesen Schritt manuell durch, um sicherzustellen, dass die richtige Versionskennung angegeben wird.

Wir bewegen uns in Richtung GitOps

Unser Ziel

Wir möchten das Chart im Repository der Anwendung, die es bereitstellt, zurückgeben.

Der Arbeitsablauf wird derselbe sein wie bei der Entwicklung. Beispielsweise wird die Bereitstellung automatisch gestartet, wenn ein Branch in den Master-Branch übertragen wird. Der Hauptunterschied zwischen diesem Ansatz und dem aktuellen Arbeitsablauf besteht darin, dass alles in Git verwaltet wird (sowohl die Anwendung als auch die Art und Weise ihrer Bereitstellung in Kubernetes).

Es gibt mehrere Vorteile:

  • Viel verständlicher für den Entwickler. Es ist einfacher, Änderungen im lokalen Chart anzuwenden.
  • Die Definition der Bereitstellung eines Services kann an derselben Stelle wie der Code des Services angegeben werden.
  • Verwaltung der Löschung allgemeiner Charts. Der Service wird seine eigene Helm-Version haben. Dies ermöglicht die Verwaltung des Lebenszyklus der Anwendung (Rollback, Upgrade) auf einem sehr granularen Niveau, um andere Services nicht zu beeinträchtigen.
  • Vorteile von Git zur Verwaltung von Charts: Rückgängig machen von Änderungen, Auditprotokoll usw. Wenn eine Änderung des Charts zurückgesetzt werden muss, kann dies mit Git erfolgen. Die Bereitstellung wird automatisch gestartet.
  • Man kann über die Verbesserung des Entwicklungsprozesses mit Tools wie Skaffold nachdenken., mit dem Entwickler Änderungen in einer produktionsähnlichen Umgebung testen können.

Zwei-stufige Migration

Unsere Entwickler verwenden diesen Arbeitsablauf seit 2 Jahren, daher benötigen wir eine möglichst reibungslose Migration. Aus diesem Grund haben wir einen Zwischenschritt auf dem Weg zum Ziel hinzugefügt.
Der erste Schritt ist einfach:

  • Wir behalten eine ähnliche Struktur für die Konfiguration des Anwendungsdeployments bei, jedoch in einem Objekt namens DailymotionRelease.

apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
  name: "app1.ns1"
  environment: "dev"
  branch: "mybranch"
spec:
  slack_channel: "#admin"
  chart_name: "app1"
  scaling:
    - context: "dev-us-central1-0"
      replicas:
        - name: "hermes"
          count: 2
    - context: "dev-europe-west1-0"
      replicas:
        - name: "app1-deploy"
          count: 2
  secrets:
    - secret_id: "app1"
      contexts:
        - name: "default"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"
        - name: "dev-europe-west1-0"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"

  • 1 Release pro Anwendung (ohne aggregierte Charts).
  • Charts im Git-Repository der Anwendung.

Wir haben mit allen Entwicklern gesprochen, sodass der Migrationsprozess bereits begonnen hat. Der erste Schritt wird weiterhin über die CI-Plattform überwacht. Bald werde ich einen weiteren Beitrag über den zweiten Schritt schreiben: wie wir auf den GitOps-Arbeitsablauf umgestiegen sind mit FluxIch werde erzählen, wie wir alles eingerichtet haben und mit welchen Schwierigkeiten wir konfrontiert waren (mehrere Repositories, Geheimnisse usw.). Bleiben Sie dran für Neuigkeiten.

Hier haben wir versucht, unseren Fortschritt im Arbeitsprozess der Anwendungsbereitstellung in den letzten Jahren zu beschreiben, der zu Überlegungen zum GitOps-Ansatz geführt hat. Wir haben unser Ziel noch nicht erreicht und werden über die Ergebnisse berichten, sind aber jetzt überzeugt, dass es richtig war, alles zu vereinfachen und an die Gewohnheiten der Entwickler anzunähern.

Quelle: habr.com

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