Hallo zusammen! Mein Name ist Pawel Agaletski. Ich arbeite als Teamleiter in dem Team, das das Liefersystem von Lamoda entwickelt. Im Jahr 2018 habe ich auf der Konferenz HighLoad++ gesprochen und heute möchte ich die Aufzeichnung meines Vortrags vorstellen.
Mein Thema befasst sich mit der Erfahrung unseres Unternehmens beim Deployment von Systemen und Services in verschiedene Umgebungen. Angefangen in unseren prähistorischen Zeiten, als wir alle Systeme auf regulären virtuellen Servern deployten, bis hin zum schrittweisen Übergang von Nomad zum Deployment in Kubernetes. Ich werde erzählen, warum wir das gemacht haben und welche Probleme wir dabei hatten.

Deployment von Anwendungen auf VM
Beginnen wir damit, dass vor drei Jahren alle Systeme und Services des Unternehmens auf regulären virtuellen Servern deployed wurden. Technisch war es so organisiert, dass der gesamte Code unserer Systeme durch automatisierte Builds mit Jenkins erstellt wurde. Über Ansible wurde dieser Code aus unserem Versionskontrollsystem auf die virtuellen Server verteilt. Dabei wurde jedes System, das wir in unserem Unternehmen hatten, mindestens auf 2 Servern deployed: einer davon war der Head, der andere der Tail. Diese beiden Systeme waren in allen ihren Einstellungen, Leistung, Konfiguration und anderem absolut identisch. Der einzige Unterschied war, dass der Head den Benutzertraffic erhielt, während der Tail niemals Benutzertaffic erhielt.
Warum wurde das gemacht?
Als wir neue Releases unserer Anwendung deployten, wollten wir eine nahtlose Verteilung gewährleisten, das heißt, ohne spürbare Folgen für die Benutzer. Dies wurde dadurch erreicht, dass das neu compilierte Release mithilfe von Ansible auf den Tail ausgeführt wurde. Dort konnten die Personen, die für das Deployment verantwortlich waren, überprüfen und sicherstellen, dass alles in Ordnung war: alle Metriken, Bereiche und Anwendungen funktionierten; die erforderlichen Skripte wurden gestartet. Erst nachdem sie sichergestellt hatten, dass alles in Ordnung war, wurde der Traffic umgeschaltet. Er begann auf denjenigen Server zu fließen, der zuvor der Tail war. Und der Server, der zuvor der Head gewesen war, blieb ohne Benutzertraffic, behielt jedoch die vorherige Version unserer Anwendung auf ihm.
So war es für die Nutzer nahtlos. Weil der Wechsel sofort erfolgt, da es einfach der Wechsel des Load Balancers ist. Man kann sehr leicht auf die vorherige Version zurückrollen, indem man einfach den Load Balancer wieder zurückschaltet. Außerdem konnten wir die Leistungsfähigkeit der Anwendung in der Produktion testen, noch bevor der Benutzerverkehr einsetzt, was sehr praktisch war.
Welche Vorteile haben wir dabei gesehen?
- Zunächst einmal, es funktioniert einfach dann ist das klar. Allen ist klar, wie ein solches Deployment-Schema funktioniert, weil die meisten Menschen irgendwann auf gewöhnliche virtuelle Server deployt haben.
- Es ist dann doch zuverlässig, da die Deployment-Technologie einfach ist und von Tausenden von Unternehmen erprobt wurde. Millionen von Servern werden genau so deployed. Es ist schwer, etwas kaputt zu machen.
- Und schließlich konnten wir atomare Deploymentsrealisieren. Deployments, die für die Nutzer gleichzeitig stattfinden, ohne einen spürbaren Übergang zwischen der alten und der neuen Version.
Aber dabei haben wir auch einige Nachteile festgestellt:
- Neben der Produktionsumgebung gibt es Entwicklungsumgebungen und andere Umgebungen. Zum Beispiel QA und Preproduction. Zu diesem Zeitpunkt hatten wir viele Server und etwa 60 Dienste. Aus diesem Grund mussten wir für jeden Dienst die jeweils aktuelle Version der virtuellen Maschine unterstützen. Wenn Sie Bibliotheken aktualisieren oder neue Abhängigkeiten hinzufügen möchten, müssen Sie dies in allen Umgebungen tun. Außerdem musste die Zeit, wann Sie die nächste neue Version Ihrer Anwendung deployen möchten, mit dem Zeitpunkt synchronisiert werden, an dem DevOps die notwendigen Umgebungsanpassungen vornimmt. In einem solchen Fall kann man leicht in eine Situation geraten, in der die Umgebung in allen aufeinander folgenden Umgebungen leicht abweicht. Zum Beispiel könnten in der QA-Umgebung andere Bibliotheksversionen sein als in der Produktion, was zu Problemen führen würde.
- Die Schwierigkeit bei der Aktualisierung von Abhängigkeiten Ihrer Anwendung. Das hängt nicht von Ihnen ab, sondern von einem anderen Team. Genauer gesagt, vom DevOps-Team, das die Server betreut. Sie müssen eine entsprechende Aufgabe für sie formulieren und eine Beschreibung dessen geben, was Sie erreichen möchten.
- Zu diesem Zeitpunkt wollten wir auch unsere großen Monolithen in einzelne kleine Services aufteilen, da wir verstanden, dass es immer mehr werden würden. Zu diesem Zeitpunkt hatten wir bereits über 100 davon. Es war erforderlich, für jeden neuen Service eine separate neue virtuelle Maschine zu erstellen, die ebenfalls gewartet und bereitgestellt werden musste. Darüber hinaus benötigten wir nicht nur eine Maschine, sondern mindestens zwei. Hinzu kam noch eine QA-Umgebung. Das führt zu Problemen und macht die Erstellung und den Start neuer Systeme für Sie schwieriger. Komplex, kostspielig und zeitaufwändig.
Deshalb haben wir beschlossen, dass es bequemer wäre, vom Deployment traditioneller virtueller Maschinen auf das Deployment unserer Anwendungen in einem Docker-Container umzusteigen. Mit Docker benötigen Sie ein System, das die Anwendung im Cluster starten kann, da Sie einen Container nicht einfach so hochfahren können. Man möchte normalerweise überwachen, wie viele Container hochgefahren sind, damit sie automatisch hochgefahren werden. Aus diesem Grund mussten wir ein Verwaltungssystem auswählen.
Wir haben lange überlegt, welches wir auswählen sollten. Das Problem war, dass unser Deployment-Stack für herkömmliche virtuelle Server zu diesem Zeitpunkt etwas veraltet war, da dort nicht die neuesten Versionen der Betriebssysteme verwendet wurden. Irgendwann war dort sogar FreeBSD installiert, was nicht sehr bequem zu warten war. Wir wussten, dass wir so schnell wie möglich auf Docker migrieren müssen. Unsere DevOps schauten auf ihre Erfahrungen mit verschiedenen Lösungen und wählten ein System wie Nomad.
Umstieg auf Nomad
Nomad ist ein Produkt von HashiCorp. Sie sind auch bekannt für andere Lösungen:

„Consul“ ist ein Tool für die Dienstentdeckung.
„Terraform“ ist ein System zur Verwaltung von Servern, mit dem Sie diese über Konfiguration, auch bekannt als infrastructure-as-code, einrichten können.
„Vagrant“ ermöglicht Ihnen das Bereitstellen von virtuellen Maschinen lokal oder in der Cloud durch bestimmte Konfigurationsdateien.
Nomad erschien uns zu diesem Zeitpunkt als eine ausreichend einfache Lösung, auf die man schnell umsteigen kann, ohne die gesamte Infrastruktur verändern zu müssen. Außerdem ist es relativ einfach zu erlernen. Deshalb haben wir es als unser System zur Verwaltung unserer Container ausgewählt.
Was brauchen Sie, um Ihr System überhaupt in Nomad zu deployen?
- Vor allem benötigen Sie ein Docker-Image. Ihre Anwendung. Sie müssen sie erstellen und in ein Docker-Image-Repository legen. In unserem Fall ist das Artifactory – ein solches System, das es ermöglicht, verschiedene Artefakte unterschiedlicher Art hochzuladen. Es kann Archive, Docker-Images, PHP Composer-Pakete, NPM-Pakete usw. speichern.
- Es wird auch benötigt Konfigurationsdatei, das Nomad sagt, was, wohin und in welcher Menge Sie bereitstellen möchten.
Wenn wir von Nomad sprechen, verwendet es HCL, das für "HashiCorp Configuration Language" steht, als Format für die Informationsdatei. HashiCorp Configuration Language. Es handelt sich um eine Obermenge von YAML, die es Ihnen ermöglicht, Ihren Dienst in den Begriffen von Nomad zu beschreiben.

Es ermöglicht Ihnen, anzugeben, wie viele Container Sie bereitstellen möchten, aus welchen Images verschiedene Parameter beim Bereitstellen übergeben werden. Dadurch geben Sie diese Datei an Nomad weiter, und es startet die Container in Produktion gemäß dieser Datei.
In unserem Fall haben wir festgestellt, dass es nicht sehr praktisch ist, für jeden Dienst absolut identische HCL-Dateien zu schreiben, da es viele Dienste gibt und man diese gelegentlich aktualisieren möchte. Es kommt vor, dass ein Dienst nicht in einem, sondern in mehreren Instanzen bereitgestellt wird. Zum Beispiel hat eines der Systeme, das wir in Produktion haben, über 100 Instanzen. Sie werden aus den gleichen Images gestartet, unterscheiden sich jedoch in den Konfigurationseinstellungen und Konfigurationsdateien.
Deshalb haben wir beschlossen, dass es für uns praktisch ist, alle unsere Konfigurationsdateien für die Bereitstellung in einem gemeinsamen Repository zu speichern. Auf diese Weise sind sie überschaubar: Sie lassen sich leicht warten und man kann sehen, welche Systeme wir haben. Bei Bedarf ist es auch nicht schwierig, etwas zu aktualisieren oder zu ändern. Eine neue Anwendung hinzuzufügen ist ebenfalls einfach – es reicht, eine Konfigurationsdatei im Inneren eines neuen Verzeichnisses zu erstellen. Darin befinden sich Dateien: service.hcl, die die Beschreibung unseres Dienstes enthält, und einige env-Dateien, die es ermöglichen, diesen Dienst, wenn er in Produktion bereitgestellt wird, zu konfigurieren.

Einige unserer Systeme sind jedoch in der Produktion nicht in einer, sondern in mehreren Instanzen bereitgestellt. Daher haben wir beschlossen, dass es uns praktisch ist, die Konfigurationen nicht im reinen Format zu speichern, sondern in einer templateisierten Form. Als Templatesprache haben wir jinja 2. In diesem Format speichern wir sowohl die Konfigurationen des Dienstes selbst als auch die erforderlichen env-Dateien.
Darüber hinaus haben wir ein allgemeines Skript zur Bereitstellung in das Repository eingefügt, das es Ihnen ermöglicht, Ihren Dienst in der Produktionsumgebung, in der gewünschten Umgebung und auf dem gewünschten Ziel zu starten und bereitzustellen. Wenn wir unsere HCL-Konfiguration in eine Vorlage umgewandelt haben, sieht die HCL-Datei, die zuvor eine gewöhnliche Nomad-Konfiguration war, in diesem Fall etwas anders aus.

Das bedeutet, dass wir einige Variablenplatzhalter in der Konfiguration durch Variableinfügungen ersetzt haben, die aus env-Dateien oder anderen Quellen stammen. Darüber hinaus haben wir die Möglichkeit erhalten, HCL-Dateien dynamisch zu erstellen, das heißt, wir können nicht nur gewöhnliche Variableinfügungen verwenden. Da Jinja Schleifen und Bedingungen unterstützt, können auch Konfigurationsdateien erstellt werden, die sich je nach Zielort Ihrer Anwendungen ändern.
Zum Beispiel möchten Sie Ihren Dienst in der Pre-Production und in der Produktion bereitstellen. Angenommen, dass Sie in der Pre-Production keine Cron-Skripte ausführen möchten, sondern einfach den Dienst auf einer separaten Domain sehen möchten, um sicherzustellen, dass er funktioniert. Für jeden, der einen Dienst bereitstellt, sieht der Prozess sehr einfach und transparent aus. Es reicht aus, die Datei deploy.sh auszuführen, anzugeben, welchen Dienst Sie bereitstellen möchten und auf welches Ziel. Zum Beispiel möchten Sie ein bestimmtes System nach Russland, Weißrussland oder Kasachstan bereitstellen. Dazu müssen Sie einfach einen der Parameter ändern, und die richtige Konfigurationsdatei wird erstellt.
Wenn der Nomad-Dienst bereits in Ihrem Cluster bereitgestellt ist, sieht er folgendermaßen aus.

Zunächst benötigen Sie einen externen Load-Balancer, der den gesamten Benutzerverkehr akzeptiert. Er wird zusammen mit Consul arbeiten und bei Consul abfragen, wo sich der spezifische Dienst befindet, auf welchem Knoten und nach welchem IP-Adresse Domainnamen. Die Dienste erscheinen in Consul direkt aus Nomad. Da dies Produkte desselben Unternehmens sind, sind sie gut miteinander verbunden. Man kann sagen, dass Nomad standardmäßig alle in ihm gestarteten Dienste in Consul registrieren kann.
Nachdem Ihr externer Load-Balancer weiß, an welchen Dienst der Verkehr gesendet werden muss, leitet er ihn in den entsprechenden Container oder in mehrere Container, die zu Ihrer Anwendung gehören. Dabei muss man selbstverständlich auch an die Sicherheit denken. Obwohl alle Dienste auf denselben virtuellen Maschinen in Containern betrieben werden, ist es in der Regel erforderlich, den freien Zugang von einem Dienst zu einem anderen zu unterbinden. Wir haben dies durch Segmentierung erreicht. Jeder Dienst wurde in seinem eigenen virtuellen Netzwerk betrieben, in dem Routing-Regeln und Regeln zur Erlaubnis/Verweigerung des Zugriffs auf andere Systeme und Dienste festgelegt waren. Diese konnten sich sowohl innerhalb als auch außerhalb dieses Clusters befinden. Wenn Sie beispielsweise verhindern möchten, dass ein Dienst eine Verbindung zu einer bestimmten Datenbank herstellt, können Sie dies durch Segmentierung auf Netzwerkebene tun. Das bedeutet, dass Sie selbst versehentlich nicht aus einer Testumgebung auf Ihre Produktionsdatenbank zugreifen können.
Was hat uns der Übergangsprozess an personellen Ressourcen gekostet?
Der Übergang des gesamten Unternehmens zu Nomad hat etwa 5-6 Monate gedauert. Wir haben dienstweise gewechselt, jedoch in einem recht schnellen Tempo. Jedes Team musste seine eigenen Container für die Dienste erstellen.
Bei uns ist es üblich, dass jedes Team selbst für die Docker-Images seiner Systeme verantwortlich ist. Die DevOps bieten die erforderliche Infrastruktur für das Deployment an, also die Unterstützung des Clusters selbst, die Unterstützung des CI-Systems usw. Zu diesem Zeitpunkt waren über 60 Systeme zu Nomad umgezogen, was etwa 2000 Container ausmachte.
Die DevOps sind für die gesamte Infrastruktur aller mit dem Deployment verbundenen Server verantwortlich. Jedes Entwicklungsteam hingegen ist dafür verantwortlich, Container für sein spezifisches System zu implementieren, da nur das Team weiß, was es eigentlich in diesem oder jenem Container benötigt.
Gründe für den Verzicht auf Nomad
Welche Vorteile haben wir durch den Umstieg auf das Deployment mit Nomad und Docker erhalten?
- Wir gleiche Bedingungen gewährleistet Für alle Umgebungen. In der Entwicklungs-, QA-Umgebung, in der Pre-Production und in der Produktion werden dieselben Container-Images mit denselben Abhängigkeiten verwendet. Dementsprechend haben Sie praktisch keine Chance, dass in der Produktion etwas landet, was Sie zuvor lokal oder in einer Testumgebung getestet haben.
- Wir haben auch festgestellt, dass es ausreichend einfach ist, einen neuen Servicehinzuzufügen. Jede neue Systeme aus Sicht des Deployments wird sehr einfach gestartet. Es reicht, in das Repository zu gehen, das die Konfigurationen speichert, dort die entsprechende Konfiguration für Ihr System hinzuzufügen, und Sie sind bereit. Sie können Ihr System ohne zusätzliche Anstrengungen von DevOps in die Produktion bringen.
- Alle Konfigurationsdateien in einem gemeinsamen Repository waren einsehbar.In dem Moment, als wir unsere Systeme mit Hilfe von virtuellen Serverndeployten, verwendeten wir Ansible, in dem die Konfigurationen im selben Repository lagen. Dennoch war es für die meisten Entwickler etwas komplizierter, damit zu arbeiten. Hier wurde das Volumen an Konfigurationen und Code, den Sie hinzufügen müssen, um einen Service zu deployen, deutlich kleiner. Außerdem ist es für DevOps sehr einfach, es zu ändern oder zu aktualisieren. Im Fall von Übergängen, zum Beispiel bei einer neuen Version von Nomad, können sie einfach alle operativen Dateien, die an derselben Stelle liegen, massenhaft aktualisieren.
Aber wir sind auch auf einige Nachteile gestoßen:
Es stellte sich heraus, dass wir nicht nahtlose Deployments im Fall von Nomad erreichen konnten. Beim Rollout von Containern aus unterschiedlichen Bedingungen konnte es passieren, dass dieser gestartet wurde und Nomad ihn als bereit für den Datenverkehr ansah. Dies geschah noch bevor die Anwendung darin gestartet wurde. Aus diesem Grund begann das System für kurze Zeit, 500-Fehler auszugeben, weil der Datenverkehr auf einen Container gelenkt wurde, der noch nicht bereit war, ihn zu akzeptieren.
Wir stießen auf einige Bugs. Der gravierendste Fehler besteht darin, dass Nomad große Cluster nicht optimal verwaltet, wenn Sie viele Systeme und Container haben. Wenn Sie einen der Server, der zum Nomad-Cluster gehört, außer Betrieb nehmen möchten, besteht eine erhebliche Wahrscheinlichkeit, dass sich das Cluster schlecht verhält und auseinanderfällt. Ein Teil der Container kann beispielsweise ausfallen und nicht wieder hochgefahren werden — das könnte Ihnen später erheblich kosten, insbesondere wenn sich all Ihre Systeme in der Produktion im Nomad-verwalteten Cluster befinden.
Deshalb haben wir beschlossen, darüber nachzudenken, wo wir als Nächstes hingehen wollen. Zu diesem Zeitpunkt hatten wir viel klarer erkannt, was wir erreichen möchten. Und zwar: Wir wünschen uns Zuverlässigkeit, ein paar mehr Funktionen als Nomad bietet, sowie ein reiferes und stabileres System.
In dieser Hinsicht fiel unsere Wahl auf Kubernetes als die populärste Plattform zum Starten von Clustern. Besonders unter der Voraussetzung, dass die Größe und Anzahl unserer Container recht groß war. Für solche Zwecke erschien Kubernetes als das geeignetste System von denen, die wir uns angesehen hatten.
Der Übergang zu Kubernetes
Ich werde ein wenig über die grundlegenden Konzepte von Kubernetes erzählen und wie sie sich von Nomad unterscheiden.

Zunächst ist das grundlegendste Konzept in Kubernetes das des Pods. Pod — es ist eine Gruppe von einem oder mehreren Containern, die immer zusammen gestartet werden. Sie arbeiten, als ob sie immer auf derselben virtuellen Maschine ausgeführt werden. Sie sind über die IP-Adresse 127.0.0.1 auf verschiedenen Ports miteinander erreichbar.
Angenommen, Sie haben eine PHP-Anwendung, die aus Nginx und PHP-FPM besteht — ein klassisches Setup. Wahrscheinlich möchten Sie, dass sowohl die Nginx- als auch die PHP-FPM-Container immer gemeinsam laufen. Kubernetes ermöglicht es, dies zu erreichen, indem man sie als einen gemeinsamen Pod beschreibt. Genau das konnten wir mit Nomad nicht erreichen.
Das zweite Konzept ist deployment. Der Punkt ist, dass ein Pod für sich genommen eine flüchtige Sache ist, die startet und verschwindet. Ob Sie zuerst alle Ihre vorherigen Container löschen und dann sofort neue Versionen starten möchten oder ob Sie sie schrittweise ausrollen wollen — genau für diesen Prozess ist das Konzept des Deployments verantwortlich. Es beschreibt, wie Sie Ihre Pods bereitstellen, in welcher Anzahl und wie Sie sie aktualisieren.
Das dritte Konzept ist service. Ihr Service ist praktisch Ihr System, das einen bestimmten Datenverkehr aufnimmt und ihn dann an einen oder mehrere Pods weiterleitet, die Ihrem Service entsprechen. Das bedeutet, dass Sie sagen können, dass der gesamte eingehende Datenverkehr zu einem bestimmten Service mit einem bestimmten Namen an genau diese Pods gesendet werden soll. Gleichzeitig sorgt es für eine Lastverteilung des Datenverkehrs. Sie können also zwei Pods Ihrer Anwendung starten, und der gesamte eingehende Datenverkehr wird gleichmäßig auf die entsprechenden Pods dieses Services verteilt.
Und das vierte grundlegende Konzept — Ingress. Dies ist ein Service, der in einem Kubernetes-Cluster gestartet wird. Er fungiert als externer Lastenausgleich, der alle Anfragen entgegennimmt. Über die API von Kubernetes kann Ingress bestimmen, wohin diese Anfragen gesendet werden müssen. Dabei geschieht dies sehr flexibel. Sie können sagen, dass alle Anfragen an diesen Host und eine bestimmte URL an diesen Service gesendet werden. Und diese Anfragen, die an diesen Host und eine andere URL kommen, werden an einen anderen Service gesendet.
Das Tolle aus Sicht des Entwicklers ist, dass Sie all dies selbst verwalten können. Indem Sie die Ingress-Konfiguration festlegen, können Sie den gesamten Datenverkehr, der an eine bestimmte API kommt, an separate Container weiterleiten, die beispielsweise in Go geschrieben sind. Der Datenverkehr, der an dieselbe Domain, aber an eine andere URL kommt, kann an Container weitergeleitet werden, die in PHP geschrieben sind, wo viel Logik enthalten ist, die jedoch nicht sehr schnell ist.
Wenn man all diese Konzepte mit Nomad vergleicht, kann man sagen, dass die ersten drei Konzepte zusammen als Service betrachtet werden. Das letzte Konzept ist in Nomad nicht vorhanden. Wir haben an seiner Stelle einen externen Lastenausgleich verwendet: das kann haproxy, nginx, nginx+ usw. sein. Im Fall von Kubernetes brauchen Sie dieses zusätzliche Konzept nicht separat einzuführen. Wenn man jedoch Ingress näher betrachtet, ist es entweder nginx, haproxy oder traefik, aber sozusagen in Kubernetes integriert.
Alle von mir beschriebenen Konzepte sind im Wesentlichen Ressourcen, die innerhalb eines Kubernetes-Clusters existieren. Zur Beschreibung dieser Ressourcen wird im Cluster das yaml-Format verwendet, das lesbarer und vertrauter ist als HCL-Dateien im Fall von Nomad. Strukturell beschreiben sie jedoch beispielsweise bei Pods dasselbe. Sie sagen – ich möchte solche Pods dorthin deployen, mit solchen Images, in einer bestimmten Anzahl.

Neben diesem haben wir verstanden, dass wir nicht manuell jede einzelne Ressource erstellen wollen: Deployment, Services, Ingress und so weiter. Stattdessen wollten wir bei der Bereitstellung jedes unserer Systeme in Begriffen von Kubernetes beschreiben, um nicht manuell alle notwendigen Ressourcenabhängigkeiten in der richtigen Reihenfolge neu erstellen zu müssen. Als solches System, das uns dies ermöglicht hat, wurde Helm gewählt.
Grundlegende Konzepte in Helm
Helm ist ein Paketmanager für Kubernetes. Er funktioniert sehr ähnlich wie Paketmanager in Programmiersprachen. Sie ermöglichen es Ihnen, einen Service zu speichern, der beispielsweise aus dem Deployment von nginx, dem Deployment von php-fpm, einer Konfiguration für Ingress, ConfigMaps (dies ist ein Objekt, das es Ihnen ermöglicht, env und andere Parameter für Ihr System festzulegen) in Form von sogenannten Charts besteht. Dabei arbeitet Helm über Kubernetes. Das bedeutet, dass es kein separates System ist, sondern einfach ein weiterer Service, der innerhalb des Clusters läuft. Sie interagieren mit ihm über seine API mittels eines Konsolenbefehls. Sein Komfort und seine Vorzüge liegen darin, dass selbst wenn Helm abstürzt oder Sie ihn aus dem Cluster entfernen, Ihre Services nicht verschwinden, da Helm im Grunde nur dazu dient, das System zu starten. Für die Funktionsfähigkeit und den Zustand der Services ist dann Kubernetes selbst verantwortlich.
Außerdem haben wir erkannt, dass die Vorlagenbildung, die wir zuvor manuell durch die Integration von Jinja in unsere Konfigurationen durchführen mussten, eine der Hauptfunktionen von Helm ist. Alle Konfigurationen, die Sie für Ihre Systeme erstellen, werden in Helm als Vorlagen gespeichert, die ein wenig wie Jinja, aber tatsächlich mit der Template-Sprache von Go, in der Helm geschrieben ist, verwendet werden, genau wie Kubernetes.
Helm fügt uns noch einige zusätzliche Konzepte hinzu.
Diagramm — das ist die Beschreibung Ihres Services. In anderen Paketmanagern würde man es Paket, Bundle oder etwas Ähnliches nennen. Hier heißt es Chart.
Values – das sind die Variablen, die Sie zur Erstellung Ihrer Konfigurationen aus den Vorlagen verwenden möchten.
ReleaseJedes Mal erhält der Service, der mit Helm bereitgestellt wird, eine inkrementelle Version des Releases. Helm erinnert sich, wie die Konfiguration des Services bei der vorherigen, vorvorherigen Version usw. war. Daher reicht es aus, den Befehl 'helm callback' auszuführen und die vorherige Version des Releases anzugeben, wenn ein Rollback erforderlich ist. Selbst wenn die entsprechende Konfiguration im Moment des Rollbacks nicht in Ihrem Repository verfügbar ist, erinnert sich Helm trotzdem, wie sie war, und stellt Ihr System auf den Zustand zurück, in dem es sich beim vorherigen Release befand.
Im Fall von Helm werden auch die üblichen Konfigurationen für Kubernetes in Templates umgewandelt, die die Möglichkeit bieten, Variablen, Funktionen und bedingte Operatoren zu verwenden. Auf diese Weise können Sie die Konfiguration Ihres Services je nach Umgebung zusammenstellen.

In der Praxis haben wir uns entschieden, etwas anders vorzugehen als bei Nomad. Während in Nomad in einem Repository sowohl die Konfigurationen für das Deployment als auch die n-Variablen, die zum Deployen unseres Services erforderlich sind, gespeichert wurden, haben wir uns hier entschieden, sie in zwei separate Repositories zu unterteilen. Im Repository 'deploy' werden nur die n-Variablen gespeichert, die für das Deployment benötigt werden, während im Repository 'helm' die Konfigurationen oder Charts gespeichert werden.

Was hat uns das gebracht?
Obwohl wir in den Konfigurationsdateien keine wirklich sensiblen Daten speichern. Zum Beispiel Passwörter für Datenbanken werden als Secrets in Kubernetes gespeichert, dennoch gibt es dort immer noch bestimmte Dinge, auf die wir nicht allen Zugriff gewähren möchten. Daher ist der Zugang zum Repository 'deploy' strenger geregelt, während das Repository 'helm' einfach die Beschreibung des Services enthält. Aus diesem Grund kann dort sicher eine breitere Personengruppe Zugang erhalten.
Da wir nicht nur eine Produktionsumgebung, sondern auch andere Umgebungen haben, können wir dank dieser Trennung unsere Helm-Charts wiederverwenden, um Services nicht nur in der Produktionsumgebung, sondern beispielsweise auch in der QA-Umgebung bereitzustellen. Sogar um sie lokal zu implementieren, unter Verwendung von Minikube — das ist ein Tool für den lokalen Betrieb von Kubernetes.
In jedem Repository haben wir eine Trennung in separate Verzeichnisse für jeden Dienst gelassen. Das bedeutet, dass sich in jedem Verzeichnis Templates befinden, die zu dem entsprechenden Chart gehören und die Ressourcen beschreiben, die für den Start unseres Systems bereitgestellt werden müssen. Im Repository „deploy“ haben wir nur die Environments hinterlassen. In diesem Fall haben wir auf die Verwendung von Jinja-Templating verzichtet, da Helm selbst Templating out of the box bietet – das ist eine seiner Hauptfunktionen.
Wir haben ein Skript für das Deployment – deploy.sh – hinterlassen, das den Start für das Deployment mit Helm vereinfacht und standardisiert. So sieht das Deployment-Interface für jeden, der ein Deployment durchführen möchte, genau so aus wie bei einem Deployment über Nomad. Das gleiche deploy.sh, der Name Ihres Dienstes und wohin Sie ihn deployen möchten. Dadurch wird Helm im Hintergrund gestartet. Es sammelt die Konfigurationen aus den Templates, setzt die erforderlichen Values-Dateien ein und deployt dann, indem es sie in Kubernetes übergibt.
Das DBMS Tarantool ist ein attraktives, zukunftsträchtiges Produkt zur Erstellung von hochbelasteten Anwendungen.
Der Kubernetes-Dienst erscheint komplexer als Nomad.

Hier kommt der ausgehende Datenverkehr in den Ingress. Dies ist genau der Front-Controller, der alle Anfragen entgegennimmt und sie anschließend an die entsprechenden Dienste weiterleitet. Er bestimmt diese auf der Grundlage von Konfigurationen, die Teil der Beschreibung Ihrer Anwendung in Helm sind und die die Entwickler selbst festlegen. Der Dienst sendet die Anfragen an seine Pods, also an bestimmte Container, und balanciert dabei den eingehenden Verkehr zwischen allen Containern, die zu diesem Dienst gehören. Und natürlich sollten wir nicht vergessen, dass wir auf Netzwerkebene nicht auf Sicherheit verzichten dürfen. Daher arbeitet die Segmentierung im Kubernetes-Cluster, die auf Tagging basiert. Alle Dienste haben bestimmte Tags, an die die Zugriffsrechte der Dienste auf bestimmte externe/intern Ressourcen innerhalb oder außerhalb des Clusters gebunden sind.
Bei unserem Wechsel haben wir festgestellt, dass Kubernetes alle Funktionen von Nomad, das wir zuvor verwendet haben, bietet und zudem viele neue Möglichkeiten hinzufügt. Es lässt sich durch Plugins erweitern und durch benutzerdefinierte Ressourcentypen anpassen. Das bedeutet, dass Sie nicht nur etwas verwenden können, was out-of-the-box in Kubernetes bereitgestellt wird, sondern auch Ihre eigenen Ressourcen und Dienste schaffen, die auf ihre Ressourcen zugreifen können. Dies bietet zusätzliche Möglichkeiten, Ihr System zu erweitern, ohne Kubernetes neu installieren oder Änderungen vornehmen zu müssen.
Ein Beispiel für eine solche Verwendung ist Prometheus, der bei uns innerhalb des Kubernetes-Clusters läuft. Um zu beginnen, Metriken von einem bestimmten Dienst zu sammeln, müssen wir dem Dienstbeschreibung einen zusätzlichen Ressourcentyp hinzufügen, den sogenannten Service-Monitor. Prometheus kann, weil er in Kubernetes läuft, benutzerdefinierte Ressourcentypen lesen und beginnt automatisch, Metriken aus dem neuen System zu sammeln. Das ist sehr praktisch.
Der erste Deployment, den wir in Kubernetes gemacht haben, war im März 2018. Seit dieser Zeit haben wir nie Probleme damit gehabt. Es arbeitet stabil, ohne wesentliche Bugs. Außerdem können wir es weiter ausbauen. Heute reicht uns die Funktionalität, die es bietet, und wir mögen das Entwicklungstempo von Kubernetes sehr. Zurzeit sind mehr als 3000 Container in Kubernetes. Der Cluster umfasst mehrere Nodes. Dabei ist er wartbar, stabil und sehr gut kontrollierbar.
Quelle: habr.com
