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 , 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 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 , bevor sie im Chartmuseum veröffentlicht werden.
Allgemeine Beschreibung des Chart-Entwicklungsworkflows
- Konfiguration der Pipeline-Aufgaben gemäß den Spezifikationen zur Qualitätssicherung (Linting, Unit-Test).
- Hochladen des Docker-Images mit den Python-Tools, die unsere Anwendungen bereitstellen.
- Einrichten der Umgebung basierend auf dem Branch-Namen.
- Überprüfung der Kubernetes-YAML-Dateien mit Kubeval.
- Automatische Erhöhung der Chart-Version und der seiner übergeordneten Charts (Charts, die von dem veränderbaren Chart abhängen).
- 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 , 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-central1Globale 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: 3Definition 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. 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: OpaqueProbleme 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 , 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 Ich 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
