Hallo zusammen! Mein Name ist Pawel Agaletzki. Ich bin Teamleiter in der Gruppe, die das Liefersystem von Lamoda entwickelt. 2018 habe ich auf der Konferenz HighLoad++ gesprochen, und heute möchte ich die Zusammenfassung meines Vortrags vorstellen.
Mein Thema befasst sich mit den Erfahrungen unseres Unternehmens beim Deployment von Systemen und Services in verschiedene Umgebungen. Angefangen von unseren archaischen Zeiten, als wir alle Systeme auf herkömmlichen virtuellen Servern bereitstellten, bis hin zu unserem schrittweisen Übergang von Nomad zu Kubernetes. Ich werde erläutern, warum wir das getan haben und welche Probleme wir dabei hatten.

Anwendungen auf VM bereitstellen
Vor drei Jahren wurden alle Systeme und Dienste des Unternehmens auf regulären virtuellen Servern bereitgestellt. Technisch gesehen war es so organisiert, dass der gesamte Code unserer Systeme durch automatische Builds mit Jenkins verwaltet wurde. Mithilfe von Ansible wurde dieser Code aus unserem Versionskontrollsystem auf die virtuellen Server ausgerollt. Jede Systeminstanz in unserem Unternehmen wurde dabei auf mindestens zwei Servern bereitgestellt: einer davon als Head, der andere als Tail. Diese beiden Systeme waren in Bezug auf ihre Einstellungen, Leistung, Konfiguration und vieles mehr absolut identisch. Der einzige Unterschied war, dass der Head den BenutzertypTraffic empfing, während der Tail niemals BenutzertypTraffic erhielt.
Warum wurde das so gemacht?
Bei der Bereitstellung neuer Versionen unserer Anwendung wollten wir einen nahtlosen Rollout gewährleisten, ohne spürbare Auswirkungen für die Benutzer. Dies wurde erreicht, indem der neueste Release mithilfe von Ansible auf den Tail-Server ausgerollt wurde. Dort konnten die für das Deployment verantwortlichen Personen überprüfen und sicherstellen, dass alles in Ordnung war: alle Metriken, Abschnitte und Anwendungen funktionierten und die benötigten Skripte wurden gestartet. Erst nachdem sie sicher waren, dass alles in Ordnung war, wurde der Verkehr umgeschaltet. Er begann auf den Server zu laufen, der zuvor der Tail war. Der Server, der vorher der Head war, blieb ohne Benutzerverkehre, lief jedoch mit der vorherigen Version unserer Anwendung.
So blieb es für die Benutzer nahtlos, da der Wechsel sofort erfolgt, da es sich lediglich um einen Wechsel des Lastverteilers handelt. Man kann sehr einfach auf die vorherige Version zurückschalten, indem man einfach den Lastverteiler wieder zurückschaltet. Außerdem konnten wir die Leistung der Anwendung im Live-Betrieb überprüfen, noch bevor der Benutzerverkehr darauf gerichtet wurde, was äußerst praktisch war.
Was haben wir in all dem als Vorteile erkannt?
- Zunächst einmal ist das ausreichend es funktioniert einfach. Jeder versteht, wie ein solches Deploymentschema funktioniert, da die meisten Menschen schon einmal auf herkömmlichen virtuellen Servern deployed haben.
- Das ist ausreichend zuverlässig, da die Deployment-Technologie einfach ist und sich bereits tausendfach bewährt hat. Millionen von Servern werden genau so bereitgestellt. Es ist schwierig, etwas dabei kaputt zu machen.
- Und schließlich konnten wir atomare Deploymentserhalten. Deployments, die für die Benutzer gleichzeitig erfolgen, ohne einen merklichen Wechsel zwischen der alten und der neuen Version.
Aber dabei haben wir auch einige Nachteile gesehen:
- Neben der Produktionsumgebung und der Entwicklungsumgebung gibt es auch andere Umgebungen. Zum Beispiel QA und Pre-Production. Zu diesem Zeitpunkt hatten wir viele Server und etwa 60 Dienste. Aus diesem Grund mussten wir für jeden Dienst die jeweils aktuelle Version pflegen. virtuellen Maschinen. Und wenn Sie Bibliotheken aktualisieren oder neue Abhängigkeiten installieren möchten, müssen Sie das in allen Umgebungen tun. Auch die Synchronisation der Zeit, zu der Sie die nächste Version Ihrer Anwendung bereitstellen möchten, mit dem Zeitpunkt, zu dem DevOps die notwendigen Umgebungen konfiguriert, ist wichtig. In diesem Fall kann es leicht passieren, dass sich die Umgebungen in den verschiedenen Instanzen unterscheiden. Beispielsweise können in der QA-Umgebung andere Bibliotheksversionen verwendet werden als in der Produktionsumgebung, was zu Problemen führt.
- Die Herausforderung 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 unterstützt. Sie sollten ihnen die entsprechende Aufgabe zuteilen und eine Beschreibung dessen geben, was Sie vorhaben.
- Zu diesem Zeitpunkt wollten wir auch unsere großen Monolithen in kleinere Dienste aufteilen, weil uns bewusst war, dass die Anzahl weiter wachsen würde. Bereits zu diesem Zeitpunkt hatten wir über 100 davon. Für jeden neuen Dienst musste eine separate virtuelle Maschine erstellt werden, die ebenfalls gewartet und bereitgestellt werden musste. Darüber hinaus benötigten wir nicht nur eine Maschine, sondern mindestens zwei. Hinzu kam auch eine QA-Umgebung. Dies führt zu Problemen und macht die Erstellung und den Start neuer Systeme für Sie komplizierter. teurer und zeitaufwändiger.
Deshalb haben wir uns entschieden, dass es sinnvoller wäre, von der Bereitstellung herkömmlicher virtueller Maschinen auf die Bereitstellung unserer Anwendungen in Docker-Containern umzusteigen. Bei der Verwendung von Docker benötigen wir ein System, das in der Lage ist, Anwendungen im Cluster auszuführen, da Container nicht einfach so hochgefahren werden können. In der Regel möchte man überwachen, wie viele Container aktiv sind, damit sie automatisch gestartet werden. Aus diesem Grund mussten wir ein Management-System auswählen.
Wir haben lange darüber nachgedacht, welche Lösung wir wählen sollten. Zu diesem Zeitpunkt war der Einsatz von traditionellen virtuellen Servern für unser Deployment etwas veraltet, da dort nicht die neuesten Versionen der Betriebssysteme verwendet wurden. Irgendwann lief dort sogar FreeBSD, was die Wartung nicht besonders einfach machte. Wir wussten, dass wir so schnell wie möglich auf Docker migrieren mussten. Unser DevOps-Team hat seine Erfahrungen mit verschiedenen Lösungen betrachtet und sich für ein System wie Nomad entschieden.
Übergang zu Nomad
Nomad ist ein Produkt von HashiCorp. Diese Firma ist auch für ihre anderen Lösungen bekannt:

„Consul“ — ein Tool zur Service-Entdeckung.
„Terraform“ — ein System zur Verwaltung von Servern, das es Ihnen ermöglicht, diese über Konfigurationen, das sogenannte Infrastructure-as-Code, einzurichten.
„Vagrant“ ermöglicht Ihnen das Bereitstellen von virtuellen Maschinen sowohl lokal als auch in der Cloud durch spezifische Konfigurationsdateien.
Nomad erschien uns damals als eine recht einfache Lösung, auf die wir schnell umsteigen konnten, ohne die gesamte Infrastruktur zu verändern. Außerdem ist es relativ leicht zu erlernen. Daher haben wir es als unser Filtersystem für den Container gewählt.
Was benötigen Sie, um Ihr System überhaupt in Nomad zu deployen?
- Zunächst einmal brauchen Sie ein Docker-Image Ihrer Anwendung. Dieses muss erstellt und in ein Docker-Registry gespeichert werden. In unserem Fall ist das Artifactory – eine Plattform, die es ermöglicht, verschiedene Artefakte unterschiedlichster Art hochzuladen. Sie kann Archive, Docker-Images, PHP Composer-Pakete, NPM-Pakete und mehr speichern.
- Auch notwendig ist eine Konfigurationsdatei, die Nomad mitteilt, was, wohin und in welcher Menge Sie deployen möchten.
Wenn wir von Nomad sprechen, verwendet es als Dateiformat die HCL-Sprache, was für HashiCorp Configuration Languagesteht. Dies ist ein Über-Set von YAML, das es Ihnen ermöglicht, Ihren Service in den Begriffen von Nomad zu beschreiben.

Es ermöglicht Ihnen anzugeben, wie viele Container Sie bereitstellen möchten und aus welchen Images Sie ihnen verschiedene Parameter beim Deployment übergeben. Auf diese Weise füttern Sie dieses Nomad-Dokument, und es startet die Container gemäß den Vorgaben in der Produktion.
In unserem Fall haben wir erkannt, dass es nicht sehr praktisch ist, für jeden Dienst absolut identische HCL-Dateien zu schreiben, da es viele Dienste gibt und man diese manchmal aktualisieren möchte. Es kann vorkommen, dass ein Dienst nicht nur einmal bereitgestellt wird, sondern in verschiedenen Instanzen. Zum Beispiel hat eines der Systeme, das wir in der Produktion haben, über 100 Instanzen im Einsatz. Diese werden aus denselben Images gestartet, unterscheiden sich jedoch in den Konfigurationseinstellungen und Konfigurationsdateien.
Deshalb haben wir uns entschieden, alle unsere Konfigurationsdateien für das Deployment in einem gemeinsamen Repository zu speichern. So sind sie zugänglich, leicht zu warten und wir können sehen, welche Systeme wir haben. Bei Bedarf ist es auch einfach, etwas zu aktualisieren oder zu ändern. Eine neue Systemkonfiguration hinzuzufügen, ist ebenfalls unkompliziert – man muss lediglich eine Konfigurationsdatei im neuen Verzeichnis anlegen. In diesem Verzeichnis befinden sich die Dateien: service.hcl, die unsere Dienstbeschreibung enthält, sowie einige env-Dateien, die es ermöglichen, diesen Dienst beim Deployment in der Produktion zu konfigurieren.

Einige unserer Systeme sind jedoch nicht nur in einem Exemplar, sondern mehrfach in der Produktion bereitgestellt. Daher haben wir uns entschieden, nicht die Konfigurationen in reiner Form zu speichern, sondern deren templatebasierten Ansatz. Als Template-Sprache haben wir uns für jinja 2entschieden. In diesem Format werden sowohl die Konfigurationen des Dienstes als auch die benötigten env-Dateien gespeichert.
Zusätzlich haben wir ein gemeinsames Skript-Deployment in das Repository eingefügt, das es Ihnen ermöglicht, Ihren Dienst in die Produktion zu starten und auf die gewünschte Umgebung sowie das gewünschte Ziel zu deployen. In dem Moment, als wir unsere HCL-Konfiguration in eine Vorlage umgewandelt haben, hat die HCL-Datei, die zuvor eine normale Nomad-Konfiguration war, ein etwas anderes Aussehen angenommen.

Das heißt, wir haben einige Platzhalter in der Konfiguration durch Variablen ersetzt, die aus env-Dateien oder anderen Quellen stammen. Darüber hinaus haben wir die Möglichkeit geschaffen, HCL-Dateien dynamisch zu generieren, das heißt, wir können nicht nur gewöhnliche Variablenersetzungen anwenden. Da Jinja Schleifen und Bedingungen unterstützt, können auch Konfigurationsdateien erstellt werden, die sich je nach Zielort Ihrer Anwendungen ändern.
Angenommen, Sie möchten Ihren Service in einer Pre-Production- und in einer Production-Umgebung bereitstellen. Nehmen wir an, dass Sie in der Pre-Production keine Cron-Skripte ausführen möchten, sondern einfach den Service unter einer separaten Domain sehen wollen, um sicherzustellen, dass er funktioniert. Für alle, die einen Service bereitstellen, sieht der Prozess sehr einfach und transparent aus. Sie müssen lediglich die Datei deploy.sh ausführen, angeben, welchen Service Sie bereitstellen möchten und in welches Ziel. Zum Beispiel, wenn Sie ein System nach Deutschland, in die Schweiz oder nach Österreich bereitstellen möchten. Dafür müssen Sie einfach einen der Parameter ändern, und es wird die richtige Konfigurationsdatei generiert.
Sobald der Nomad-Service in Ihrem Cluster bereitgestellt ist, sieht er folgendermaßen aus.

Zunächst benötigen Sie einen externen Load Balancer, der den gesamten Benutzertypverkehr empfängt. Dieser wird zusammen mit Consul arbeiten und von ihm erfahren, wo, auf welchem Knoten, nach welchem... die IP-Adresse gebunden sind Hier befindet sich ein bestimmter Dienst, der zu einem bestimmten Domainnamen passt. Dienste in Consul entstehen direkt aus Nomad. Da es sich um Produkte desselben Unternehmens handelt, sind sie gut miteinander verbunden. Man kann sagen, dass Nomad von Haus aus alle darin gestarteten Dienste innerhalb von Consul registrieren kann.
Nachdem Ihr externes Load-Balancer-System entschieden hat, an welchen Dienst der Traffic gesendet werden soll, leitet es diesen an den entsprechenden Container oder an mehrere Container weiter, die zu Ihrer Anwendung gehören. Dabei muss selbstverständlich auch die Sicherheit berücksichtigt werden. Obwohl alle Dienste auf denselben virtuellen Maschinen in Containern ausgeführt werden, erfordert dies normalerweise, dass der ungehinderte Zugang von einem Dienst zu einem anderen eingeschränkt wird. Dies haben wir durch Segmentierung erreicht. Jeder Dienst wurde in seinem eigenen virtuellen Netzwerk betrieben, in dem Routing-Regeln sowie Regeln zur Gewährung oder Verweigerung des Zugangs zu anderen Systemen und Diensten festgelegt wurden. Diese konnten sich sowohl innerhalb als auch außerhalb dieses Clusters befinden. Wenn Sie beispielsweise einem Dienst den Zugriff auf eine bestimmte Datenbank verweigern möchten, können Sie dies durch Segmentierung auf Netzwerkebene erreichen. Das bedeutet, dass Sie selbst bei einem Fehler nicht versehentlich von einer Testumgebung auf Ihre Produktionsdatenbank zugreifen können.
Was hat uns der Übergangsprozess in Bezug auf personelle Ressourcen gekostet?
Der Übergang des gesamten Unternehmens zu Nomad dauerte etwa 5-6 Monate. Wir haben schrittweise, aber in einem recht zügigen Tempo migriert. Jedes Team musste seine eigenen Container für die jeweiligen Services erstellen.
Wir verfolgen den Ansatz, dass jedes Team eigenverantwortlich für die Docker-Images seiner Systeme zuständig ist. Die DevOps-Abteilung stellt hingegen die notwendige Infrastruktur für das Deployment bereit, darunter die Unterstützung des Clusters, der CI-Systeme und so weiter. Zu diesem Zeitpunkt waren über 60 Systeme zu Nomad gewechselt, was etwa 2.000 Container ergab.
Die DevOps-Abteilung ist für die allgemeine Infrastruktur im Zusammenhang mit Deployments und Servern verantwortlich. Jedes Entwicklungsteam hingegen kümmert sich um die Implementierung der Container für sein spezifisches System, da nur das Team weiß, was genau in jedem einzelnen Container benötigt wird.
Gründe für die Ablehnung von Nomad
Welche Vorteile haben wir durch die Migration zu Deployments mit Nomad und Docker erzielt?
- Wir wir haben gleichwertige Bedingungen geschaffen Für alle Umgebungen. In der Entwicklung, QA-Umgebung, im Pre-Production und in der Produktion werden dieselben Container-Images mit denselben Abhängigkeiten verwendet. Somit haben Sie praktisch keine Chance, dass in der Produktion etwas landet, das Sie vorher nicht lokal oder in der Testumgebung getestet haben.
- Wir haben außerdem festgestellt, dass es durchaus einfach ist, einen neuen Servicehinzuzufügen. Alle neuen Systeme lassen sich aus Sicht des Deployments sehr einfach starten. Es reicht, in das Repository zu gehen, das die Konfigurationen speichert, eine weitere Konfiguration für Ihr System hinzuzufügen, und schon ist alles bereit. Sie können Ihr System ohne zusätzlichen Aufwand 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 Servern, wir haben Ansible verwendet, bei dem die Konfigurationen im selben Repository gespeichert waren. Für die meisten Entwickler war es jedoch etwas komplizierter, damit zu arbeiten. Hier ist das Volumen an Konfigurationen und Code, den Sie hinzufügen müssen, um einen Service bereitzustellen, deutlich geringer geworden. Zudem ist es für DevOps sehr einfach, Anpassungen vorzunehmen oder Änderungen vorzunehmen. Im Falle von Updates, beispielsweise auf eine neue Version von Nomad, können sie alle Betriebsdateien, die sich am gleichen Speicherort befinden, massenhaft aktualisieren.
Aber wir sind auch auf einige Nachteile gestoßen:
Es stellte sich heraus, dass wir nicht die nahtlosen Deployments erreichen konnten bei Nomad. Beim Rollout von Containern aus unterschiedlichen Umgebungen konnte es passieren, dass der Container bereits gestartet war, und Nomad ihn als bereit empfand, Verkehr zu akzeptieren. Dies geschah, bevor die Anwendung in ihm wirklich gestartet war. Aus diesem Grund begann das System kurzfristig, 500er-Fehler auszugeben, da der Verkehr auf einen Container geleitet wurde, der noch nicht bereit war, ihn zu akzeptieren.
Wir stießen auf einige Bugs. Der wesentliche Fehler besteht darin, dass Nomad große Cluster nicht gut verwalten kann, insbesondere wenn Sie viele Systeme und Container haben. Wenn Sie einen der Server, der Teil des Nomad-Clusters ist, außer Betrieb nehmen möchten, gibt es eine beträchtliche Wahrscheinlichkeit, dass der Cluster nicht stabil bleibt und auseinanderfällt. Einige Container können beispielsweise abstürzen und sich nicht wiederherstellen lassen – das wird Ihnen teuer zu stehen kommen, wenn all Ihre Systeme im Produktionsumfeld tatsächlich im Nomad-Cluster verwaltet werden.
Deshalb haben wir beschlossen, über die nächsten Schritte nachzudenken. Zu diesem Zeitpunkt wurde uns viel klarer, was wir erreichen möchten. Insbesondere streben wir nach Verlässlichkeit, zusätzliche Funktionen im Vergleich zu Nomad und ein ausgereifteres, stabileres System.
In diesem Zusammenhang fiel unsere Wahl auf Kubernetes, die beliebteste Plattform zur Bereitstellung von Clustern. Besonders angesichts der Tatsache, dass die Größe und Anzahl unserer Container recht groß war. Für diese Zwecke erschien uns Kubernetes als das passendste System von den Möglichkeiten, die wir in Betracht ziehen konnten.
Umstieg auf Kubernetes
Ich möchte Ihnen kurz die grundlegenden Konzepte von Kubernetes erklären und wie sie sich von Nomad unterscheiden.

Zuallererst ist das grundlegendste Konzept in Kubernetes das Pod-Konzept. Pod ist eine Gruppe von einem oder mehreren Containern, die immer zusammen gestartet werden. Sie arbeiten, als ob sie immer auf einer einzigen virtuellen Maschine laufen. Sie sind über die IP-Adresse 127.0.0.1 auf verschiedenen Ports miteinander verbunden.
Nehmen wir an, Sie haben eine PHP-Anwendung, die aus nginx und php-fpm besteht – eine klassische Architektur. Wahrscheinlich möchten Sie, dass die nginx- und php-fpm-Container immer zusammen vorhanden sind. Kubernetes ermöglicht dies, indem Sie sie als ein gemeinsames Pod beschreiben. Genau das konnten wir mit Nomad nicht erreichen.
Das zweite Konzept ist deployment. Ein Pod ist an sich eine flüchtige Entität, sie wird gestartet und verschwindet. Möchten Sie zuerst alle Ihre vorherigen Container vernichten und dann sofort neue Versionen starten, oder möchten Sie diese schrittweise ausrollen? 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 im Grunde Ihr System, das bestimmten Datenverkehr annimmt und diesen dann an einen oder mehrere Pods weiterleitet, die zu Ihrem Service gehören. Das heißt, er ermöglicht es, zu definieren, dass der gesamte eingehende Datenverkehr für einen bestimmten Service mit einem bestimmten Namen auf genau diese Pods geleitet werden soll. Dabei sorgt er für eine effiziente Lastenverteilung. Das bedeutet, Sie können zwei Pods Ihrer Anwendung betreiben, und der gesamte eingehende Datenverkehr wird gleichmäßig auf die entsprechenden Pods verteilt.
Und das vierte grundlegende Konzept — Ingress. Dies ist ein Service, der in einem Kubernetes-Cluster ausgeführt wird. Er fungiert als externer Lastenausgleich, der alle Anfragen entgegennimmt. Über die API von Kubernetes kann Ingress flexibel bestimmen, wohin diese Anfragen geleitet werden sollen. Sie können beispielsweise festlegen, dass alle Anfragen an diesen Host und eine bestimmte URL an diesen Service gesendet werden. Und Anfragen, die an diesen Host und eine andere URL kommen, leiten wir an einen anderen Service weiter.
Das Beste aus Sicht eines App-Entwicklers ist, dass Sie alles selbst verwalten können. Mit der Konfiguration von Ingress können Sie den gesamten Verkehr, der auf eine bestimmte API trifft, an separate Container weiterleiten, die zum Beispiel in Go geschrieben sind. Der Verkehr, der auf dieselbe Domain, aber auf eine andere URL trifft, kann an Container, die in PHP geschrieben sind und viel Logik enthalten, weitergeleitet werden, diese sind jedoch nicht sehr leistungsfähig.
Wenn man all diese Konzepte mit Nomad vergleicht, kann man sagen, dass die ersten drei Konzepte zusammen als Service betrachtet werden. Das letzte Konzept fehlt jedoch in Nomad. Stattdessen haben wir einen externen Lastenausgleich verwendet: Das kann haproxy, nginx, nginx+ usw. sein. Im Fall von Kubernetes benötigen Sie dieses zusätzliche Konzept nicht. Wenn Sie sich jedoch Ingress von innen ansehen, handelt es sich entweder um nginx, haproxy oder traefik, aber integriert in Kubernetes.
Alle von mir beschriebenen Konzepte sind im Grunde genommen Ressourcen, die innerhalb eines Kubernetes-Clusters existieren. Zur Beschreibung dieser Ressourcen wird im Kubernetes-Umfeld das YAML-Format verwendet, das leserlicher und vertrauter ist als die HCL-Dateien bei Nomad. Strukturtechnisch beschreiben sie im Fall eines Pods dasselbe. Sie geben an: Ich möchte diese Pods dorthin bereitstellen, mit diesen Images, in dieser Anzahl.

Darüber hinaus haben wir erkannt, dass wir nicht manuell jede einzelne Ressource erstellen möchten: Deployments, Services, Ingress und andere. Stattdessen wollten wir bei der Bereitstellung unser gesamtes System in den Begriffen von Kubernetes beschreiben, um nicht alle erforderlichen Ressourcenabhängigkeiten manuell in der richtigen Reihenfolge neu zu erstellen. Helm wurde als das System ausgewählt, das uns dies ermöglicht.
Wichtige Konzepte in Helm
Helm ist von KISS ist in Shell geschrieben und umfasst etwa 500 Zeilen Code. Alle Pakete werden aus Quelltexten erstellt. Die Abhängigkeiten werden berücksichtigt und zusätzliche Patches können angewendet werden. für Kubernetes. Es ähnelt der Funktionsweise von Paketmanagern in Programmiersprachen. Damit können Sie einen Service speichern, der beispielsweise aus einem nginx-Deployment, einem php-fpm-Deployment, einer Konfiguration für Ingress und ConfigMaps (dies ist ein Element, das es Ihnen ermöglicht, Umgebungsvariablen und andere Parameter für Ihr System festzulegen) besteht, in Form von sogenannten Charts. läuft auf Kubernetes. Das heißt, es handelt sich nicht um ein separates System, sondern einfach um einen weiteren Service, der innerhalb des Clusters ausgeführt wird. Sie interagieren über die API mit ihm durch einen Konsolenbefehl. Der Vorteil und die Schönheit dabei ist, dass selbst wenn Helm abstürzt oder Sie es aus dem Cluster entfernen, Ihre Services nicht verschwinden, da Helm im Grunde nur für den Start des Systems zuständig ist. Für die Funktionsfähigkeit und den Zustand der Dienste ist dann Kubernetes selbst verantwortlich.
Außerdem haben wir verstanden, dass Templating, das wir zuvor selbst durch die Integration von Jinja in unsere Konfigurationen durchführen mussten, eine der Hauptfunktionen von Helm darstellt. Alle Konfigurationen, die Sie für Ihre Systeme erstellen, werden in Helm in Form von Vorlagen gespeichert, die ein wenig wie Jinja aussehen, tatsächlich jedoch die Templating-Sprache Go verwenden, auf der Helm, ebenso wie Kubernetes, geschrieben ist.
Helm fügt uns noch einige zusätzliche Konzepte hinzu.
Chart — das ist die Beschreibung Ihres Services. In anderen Paketmanagern würde man es als Paket, Bundle oder etwas Ähnliches bezeichnen. Hier wird es Chart genannt.
Values – das sind die Variablen, die Sie für den Zusammenbau Ihrer Konfigurationen aus den Vorlagen verwenden möchten.
Release. Bei jedem Deployment über Helm erhält der Service eine inkrementelle Versionsnummer. Helm merkt sich die Konfiguration des Services aus der vorherigen und der übernächsten Version und so weiter. Wenn ein Rollback erforderlich ist, genügt es, den Befehl helm rollback mit der Angabe der vorherigen Version auszuführen. Selbst wenn die entsprechende Konfiguration in Ihrem Repository zum Zeitpunkt des Rollbacks nicht verfügbar ist, behält Helm trotzdem die Informationen und stellt Ihre Systemkonfiguration auf den Zustand des vorherigen Releases zurück.
Wenn wir Helm verwenden, verwandeln sich die üblichen Konfigurationen für Kubernetes ebenfalls in Templates, in denen Variablen, Funktionen und bedingte Operatoren verwendet werden können. So können Sie die Konfiguration Ihres Services je nach Umgebung erstellen.

Praktisch haben wir uns entschieden, etwas anders vorzugehen als in unserem Fall mit Nomad. Während in Nomad in einem Repository sowohl die Deploy-Konfigurationen als auch die n-Variablen, die benötigt werden, um unseren Service bereitzustellen, gespeichert wurden, haben wir hier entschieden, diese in zwei separate Repositories zu unterteilen. Im Repository „deploy“ befinden sich nur die für das Deployment benötigten n-Variablen, 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, wie zum Beispiel Datenbankpasswörter, die als Secrets in Kubernetes gespeichert werden, gibt es dennoch einzelne Dinge, auf die wir nicht allen Zugriff gewähren möchten. Daher ist der Zugriff auf das Repository „deploy“ stärker eingeschränkt, während das Repository „helm“ lediglich die Beschreibung des Services enthält. Aus diesem Grund kann der Zugang zu diesem Repository sicher einem weiteren Personenkreis gewährt werden.
Da wir nicht nur Produktionsumgebungen, sondern auch andere Umgebungen haben, können wir durch diese Trennung unsere Helm-Charts wiederverwenden, um Dienste nicht nur in der Produktion, sondern beispielsweise auch in der QA-Umgebung bereitzustellen. Sogar um sie lokal zu starten, verwenden wir Minikube — das ist ein Werkzeug für das lokale Erstellen von Kubernetes.
Innerhalb jedes Repositories haben wir eine Trennung in separate Verzeichnisse für jeden Dienst belassen. Das heißt, in jedem Verzeichnis befinden sich die Vorlagen, die zu dem entsprechenden Chart gehören und die Ressourcen beschreiben, die benötigt werden, um unser System zu starten. Im Repository „deploy“ haben wir nur die Umgebungsvariablen gelassen. In diesem Fall haben wir keine Jinja-Templates verwendet, weil Helm selbst von Haus aus eine Template-Engine bietet – das ist eine seiner Hauptfunktionen.
Wir haben ein Skript für den Deployment-Prozess – deploy.sh – bereitgestellt, das das Starten von Deployments mit Helm vereinfacht und standardisiert. Für jeden, der bereitstellen möchte, sieht die Deploy-Oberfläche genau gleich aus wie bei einem Deployment über Nomad. Das gleiche deploy.sh, der Name Ihres Dienstes und das Ziel, wohin Sie es bereitstellen möchten. Dadurch wird Helm im Hintergrund ausgeführt. Dieses sammelt die Konfigurationsdateien aus den Vorlagen, setzt die erforderlichen Werte aus den values-Dateien ein und führt dann das Deployment in Kubernetes durch.
Fazit
Der Kubernetes-Dienst wirkt komplexer als Nomad.

Hier kommt der ausgehende Datenverkehr im Ingress an. Das ist der Front-Controller, der alle Anfragen entgegennimmt und sie anschließend an die entsprechenden Services weiterleitet. Er bestimmt diese basierend auf den Konfigurationen, die Teil der Beschreibung Ihrer Anwendung in Helm sind und die von den Entwicklern festgelegt werden. Der Service sendet die Anfragen an seine Pods, also spezifische Container, und balanciert den eingehenden Datenverkehr zwischen allen Containern, die zu diesem Service gehören. Und natürlich sollten wir nicht vergessen, dass wir die Netzwerksicherheit nicht vernachlässigen sollten. Daher ist im Kubernetes-Cluster eine Segmentierung implementiert, die auf Tagging basiert. Alle Services haben bestimmte Tags, die die Zugriffsrechte der Services auf bestimmte externe/internen Ressourcen innerhalb oder außerhalb des Clusters binden.
Bei der Migration haben wir festgestellt, dass Kubernetes alle Funktionen von Nomad bietet, das wir zuvor verwendet haben, und darüber hinaus viele neue Möglichkeiten hinzufügt. Es ist erweiterbar durch Plugins und durch benutzerdefinierte Ressourcenarten. Das bedeutet, dass Sie nicht nur etwas verwenden können, das standardmäßig mit Kubernetes geliefert wird, sondern auch Ihre eigenen Ressourcen und Dienste erstellen können, die diese Ressourcen abfragen. Dies eröffnet zusätzliche Erweiterungsmöglichkeiten für Ihr System, ohne dass eine Neuinstallation von Kubernetes erforderlich ist und ohne Änderungen notwendig sind.
Ein Beispiel für eine solche Nutzung ist Prometheus, das innerhalb unseres Kubernetes-Clusters läuft. Um mit dem Sammeln von Metriken eines bestimmten Dienstes zu beginnen, müssen wir dem Dienst eine zusätzliche Ressource hinzufügen, die als Service-Monitor bezeichnet wird. Da Prometheus in der Lage ist, benutzerdefinierte Ressourcentypen zu lesen, während es in Kubernetes läuft, beginnt es automatisch, Metriken von neuen Systemen zu sammeln. Das ist sehr praktisch.
Die erste Bereitstellung, die wir in Kubernetes durchgeführt haben, fand im März 2018 statt. Seitdem hatten wir nie Probleme damit. Es läuft stabil ohne nennenswerte Fehler. Zudem können wir es weiter ausbauen. Aktuell reichen uns die Möglichkeiten, die es bietet, und wir sind sehr zufrieden mit den Entwicklungsgeschwindigkeiten von Kubernetes. Momentan befinden sich über 3000 Container in Kubernetes. Der Cluster läuft auf mehreren Nodes. Dabei ist er verwaltbar, stabil und sehr kontrollierbar.
Quelle: habr.com
