Inhaltsübersicht

1. Einleitung

Taking a look at the infrastructure in my first few days of working at Xoxoday, I quickly realized that the infrastructure was relatively nascent. The multiple loose ends I noticed were potential indicators of technical debt, strictly speaking in terms of Infrastructure setup. The loose ends included the design, architecture, tooling, monitoring, and logging, along with various processes & practices.

Considering the nature of business, Xoxoday is a user-facing web service that could typically be categorized under Software as a Service (SaaS). That puts a lot of pressure on specific technical requirements such as availability, uptime, reliability, robustness & scalability.

A typical e-commerce website such as Xoxoday with high global traffic necessitates that we adapt to the latest and unique design patterns, architecture, tools & practices. This is to ensure that we keep up with the quality of service expected by the user base and ideally exceed the same.

Eine weitere kritische Anforderung, die sich aus Gründen der Einhaltung von Datenschutz- und Sicherheitsvorschriften ergibt, besteht darin, dass die meisten Kunden sich um den Standort der Daten kümmern. In der Regel wird erwartet, dass die Daten im gewünschten Land oder in der gewünschten Region verbleiben und diese nicht verlassen.

Daraus ergab sich die Notwendigkeit, die parallele Bereitstellung mehrerer Produktionsumgebungen zu erwägen, und zwar in Form atomarer, voneinander unabhängiger Bereitstellungen. Wir nennen dies eine Polycloud, auch wenn der Begriff umstritten sein mag. Diese Anforderung erforderte eine hochmoderne Automatisierung, da wir nun die Verwaltung und Wartung unserer Infrastruktur in mehreren Regionen replizieren mussten.

Nach reiflicher Überlegung wurde uns daher schnell klar, dass wir die besten Prozesse und Praktiken zur Automatisierung der Infrastruktur zusammen mit einem Portfolio moderner Cloud-basierter Technologien benötigen, um Cloud-nativ zu werden und unseren Nutzern den besten Service und die beste Erfahrung zu bieten.

Der Vorteil für uns war, dass die Entwickler ihre Dienste bereits in Microservices aufgeteilt hatten. Das hat uns geholfen, die neuesten Trends und Praktiken im Bereich der Infrastruktur schnell zu übernehmen.

2. Notwendigkeit der Automatisierung

Die Service-Automatisierung in der Cloud war in den letzten Jahren ein heißes Thema, das zum Aufkommen vieler hervorragender Tools und Technologien geführt hat. Unsere Bedürfnisse wurden in der Regel durch die Art des Unternehmens und seine Anforderungen bestimmt. Für uns waren die folgenden Faktoren entscheidend:

  • Hohe Verfügbarkeit
  • Auto-Skalierung
  • Selbstheilung

Darüber hinaus bietet die Automatisierung die Möglichkeit, Infrastruktur als Code zu realisieren, was es uns ermöglicht, mit der Infrastruktur zu interagieren und gleichzeitig die fantastischen Vorteile von Versionskontrollsystemen zu nutzen.

Dies war ein großer Paradigmenwechsel, da es uns ermöglichte, die Infrastruktur proaktiv zu betrachten und die reaktiven (imperativen) Interaktionen zu reduzieren. Es erleichterte Rollbacks und half uns, Änderungen in der gesamten Infrastruktur effizient zu verfolgen. Außerdem bedeutete die Automatisierung, dass unsere DevOps-Teams auch zu ungeraden Stunden in Ruhe arbeiten konnten.

Ein weiterer Vorteil der Automatisierung ist, dass sie verschiedene Möglichkeiten zur Modernisierung und Verbesserung der Entwicklerproduktivität eröffnet. Wir werden dieses Thema im folgenden Abschnitt über die kontinuierliche Bereitstellung näher beleuchten.

3. Kontinuierliche Integration, Bereitstellung und Einsätze

Releases können zu einem schmerzhaften Albtraum werden, wenn die richtigen Pipelines, Tools und Richtlinien nicht vorhanden sind. Dies bedeutete, dass das entscheidende Rückgrat von DevOps die CI/CD-Pipelines waren, die uns beim Testen, Integrieren, Erstellen und Bereitstellen unseres Codes für die Produktion halfen.

Wir wollten in der Lage sein, mehrere Produktionsimplementierungen an einem Tag durchzuführen, und zwar nahtlos. Wenn etwas schief ging, wollten wir die Aktualisierungen nahtlos und schnell zurücknehmen können.

Wie bereits erwähnt, hatten unsere Entwickler eine fantastische Arbeit geleistet, indem sie die monolithischen Dienste in Microservices umgewandelt hatten, die die Türen zur Native-Cloud-Welt öffneten. Dennoch brachte dies eine Reihe von Herausforderungen mit sich. Die größte Herausforderung waren die Integrationstests.

Außerdem ermöglichten uns unsere Architektur und einige hervorragende Cloud-native Tools einen innovativen Ansatz bei der Softwareentwicklung. Wir unterbrachen die traditionellen Konventionen, um uns an die neue und aufregende Welt der Microservices anzupassen.

Die bewährte Praxis, mit mehreren Umgebungen zu arbeiten, ermöglichte es uns, unser Funktionsportfolio zu testen und zu prüfen, bevor wir es in die Produktion überführten. Sobald die Arbeit an bestimmten Funktionen, Fehlern oder Verbesserungen abgeschlossen war, eröffneten unsere Entwickler eine Pull-Anfrage für den "dev"-Zweig auf Git.

Sobald der PR genehmigt war, wurden die Commits in den Entwicklungszweig übernommen. Wir hatten einen manuellen Auslösemechanismus vorgesehen, um den Entwicklungszweig für die Entwickler bereitzustellen. (auf den wir später noch eingehen werden)

Die Codebasis in diesem Zweig wurde in der Entwicklerumgebung bereitgestellt, einer speziellen Umgebung für Entwickler zum Testen ihrer neuesten Funktionen. Es gab zwei Entwicklerumgebungen, eine für die Entwickler zum Testen ihrer Funktionen und Microservices und die andere für Integrationstests.

Nachdem die Codebasis getestet und vom QA-Team genehmigt worden war, wurden die Commits in den "Staging- oder UAT-Zweig" überführt. Von hier aus wurde die Codebasis (manuell ausgelöst) in der Staging-Umgebung bereitgestellt, die wir so nah wie möglich an der Produktion halten wollten.

Ein weiteres Augenmerk lag auf der Staging-Umgebung, bevor wir überlegten, welche Funktionen und Microservices in die Produktion überführt werden sollten. Wir hatten eine andere Umgebung, die Demo-Umgebung, die ausschließlich dazu diente, potenziellen Kunden, die sich für unsere Dienste interessierten, unsere Dienste vorzuführen.

Wir haben die bestmöglichen Tools für CI/CD untersucht und analysiert und uns schließlich für Github Actions entschieden. Kurz gesagt, Github Actions bot uns eine integrierte Umgebung, die gut mit dem GitHub-Workflow zusammenspielt und ein minimales Onboarding für die Entwicklerteams erfordert.

Neben der engen Integration bot es uns auch die modernsten CI/CD-Mechanismen. Es ist superschnell, erschwinglich, einfach zu konfigurieren und anzupassen. Und es hatte eine super einfache YAML-basierte Syntax, um die Jobs zu definieren, die es uns ermöglichte, die CI/CD-Definition mit der Codebasis zu verbinden und sie mit den fantastischen Möglichkeiten von Git zu verfolgen.

a. Kontinuierliche Integration

Wir haben einige Möglichkeiten erforscht, Builds automatisch auszulösen, haben aber schnell erkannt, dass es besser ist, den Entwicklern die Kontrolle darüber zu überlassen, wann gebaut wird. Es gibt viele Gründe, die für oder gegen eine automatische oder manuelle Bereitstellung sprechen.

Es ist besser, die Erwartungen der Teams aufeinander abzustimmen und diese Entscheidung nach dem Prinzip der Bequemlichkeit zu fällen, denn zu viel Automatisierung birgt potenzielle Fallstricke.

Für unsere Nicht-Produktionsumgebungen hatten wir einen manuellen Auslöser, um die neuesten Builds zu erstellen und in den jeweiligen Umgebungen bereitzustellen. In unseren Produktionsumgebungen wurden die Builds automatisch über einen Git-Tag-Push ausgelöst. Mit Git-Tags konnten wir ein Versionsschema einhalten und die Versionen in der Produktionsumgebung bei Bedarf einfach zurücksetzen.

Lassen wir die Unit-Tests und die funktionalen Tests beiseite, die die Entwickler für ihre Commits durchgeführt haben, und konzentrieren wir uns auf die Infrastrukturseite in unserem CI/CD. Sobald der Build-Job ausgelöst wurde, authentifiziert er sich grundsätzlich bei ECR, verwaltet den Image-Cache und führt den Docker-Build-Befehl aus, um ein Container-Image zu erstellen.

Dieses Container-Image wird in das entsprechende ECR-Repository übertragen. Es ist ein einfaches Setup, aber extrem leistungsfähig und flexibel, da es uns erlaubt, den Auslöser an die Entwickler zu übergeben und gleichzeitig den gesamten Prozess zu automatisieren.

b. Kontinuierliche Einsätze

Wir haben den Entwicklern einen manuellen Auslöser zur Verfügung gestellt, um die neuesten Builds in den jeweiligen Umgebungen bereitzustellen. Wir verwendeten Docker-Image-Tags, um zwischen den Container-Images für den einzelnen Git-Zweig und die Umgebung zu unterscheiden.

Unsere Microservices liefen auf Kubernetes. Ein typischer Deployment-Auftrag läuft folgendermaßen ab:

  1. Aktualisieren der Konfigurationsdateien und Variablen: Wir löschen die vorhandenen Configmaps und erstellen sie neu, da Kubernetes eine vorhandene Configmap nicht aktualisiert. Unsere Konfigurationsdateien werden in ein privates Git-Repository übertragen, und die sensiblen Informationen werden in AWS Secrets Manager gespeichert. Daher können wir mit Git die Konfigurationsdateien nachverfolgen, was das Löschen und Neuerstellen der Configmaps in Kubernetes erleichtert.
  2. Aktualisieren Sie die Bereitstellung und den Dienst auf Kubernetes (falls es Änderungen an den Yaml-Dateien gibt)
  3. Und schließlich führen Sie ein Kubernetes-Rollout-Deployment durch, da Kubernetes die Deployments/Pods nicht automatisch aktualisiert, wenn Änderungen an den Konfigurationsdateien (configmaps) vorgenommen werden.

Wir haben die folgenden Ressourcen aus der GitHub-Aktions-Community verwendet, um uns die Verbindung, Authentifizierung und Kommunikation mit EKS/ECR und Docker zu erleichtern.

4. Architektur

Wir haben AWS für unsere Infrastrukturanforderungen verwendet. AWS bietet uns ein umfangreiches Ökosystem von Services, mit denen wir unsere Flotte von Microservices und die zugehörige Backend- und Middleware effizient ausführen und verwalten können.

These microservices are running atop Kubernetes managed by AWS EKS. AWS EKS offering saves us a lot of time and effort that Xoxoday would otherwise spend on managing the lifecycle of the Kubernetes cluster itself. That allows us to take advantage of the cloud native ecosystem while focusing on our services and working on them.

Kubernetes bringt etwa ein Jahrzehnt Erfahrung aus dem Betrieb der globalen Infrastruktur von Google ein. Es bietet uns verschiedene Funktionen. Um nur einige zu nennen: Selbstheilung, Autoskalierung, hohe Verfügbarkeit bei gleichzeitiger Konsolidierung und Steigerung der Effizienz, mit der wir unsere zugrunde liegende Infrastruktur nutzen. Wir nutzen den Ingress, um unsere Dienste über eine Mischung aus Anwendungs- und klassischen Load Balancern und route53 DNS-Eintragsautomatisierung der Außenwelt zugänglich zu machen.

Diese Services stellen eine Verbindung zu unserem Backend her, das in einer Mischung aus verwalteten AWS-Services wie verwaltetem Kafka, RDS und selbst gehosteten EC2-Instanzen gehostet wird. Für die automatisierte Einrichtung von EKS nutzen wir in hohem Maße Terraform, eine einzigartige Mischung aus Terraform und AWS Launch Templates, um die EC2-Instanzen zu erzeugen und automatisch zu verwalten.

Darüber hinaus wird SaltStack für komplexe Manöver zur Verwaltung der automatischen Einrichtung der EC2-Instanzenflotte verwendet. Es ermöglicht uns die automatische Verwaltung, Aktualisierung und Wartung des Betriebssystems und der darauf laufenden Dienste. SaltStack verfügt über einen robusten Funktionsumfang und eine hervorragend konzipierte, flexible und anpassungsfähige Architektur. Anschließend stellen wir die Dienste automatisch bereit und übernehmen die neu konfigurierten Backend-Einstellungen (IP-Adressen usw.), füllen die Kubernetes-Konfigurationsdateien auf, aktualisieren die Configmaps und verteilen sie in der Umgebung.

So können wir die zustandslosen Dienste, die in der Kubernetes-Umgebung laufen, konfigurieren und rekonfigurieren, damit sie bei dynamischen Änderungen an der Backend-Einrichtung automatisch bereitgestellt und konfiguriert werden. Unsere Container-Images werden auf AWS ECR gespeichert, da es uns einen weiteren fantastischen Service bietet, der sich nahtlos in unsere gegebenen Technologien und Architekturen einfügt.

5. Protokollierung, Überwachung, Alarmierung und APM

Wir wären blind, wenn wir nicht über die entsprechenden Feedback-Mechanismen verfügten, um zu verstehen, was in unserer Infrastruktur vor sich geht, insbesondere wenn wir über ein Polycloud-Szenario sprechen.

a. Elasticsearch/Fluentd/Kibana

Wir nutzen den einzigartigen EFK-Stack für unsere protokollbezogenen Belange. Damit können wir eine dynamische Reihe von Microservices erstellen und pflegen, die in Containern und virtuellen Maschinen ausgeführt werden, während die Protokolle in einer zentralen Pipeline gespeichert werden.

Dies ermöglicht uns den Zugriff auf Protokolle für Instanzen/Container, die aus verschiedenen Gründen zerstört werden. Und das macht es uns einfacher, sinnvoll auf mehrere Anliegen in der Produktion zuzugreifen und diese zu lösen. Hier sind andere Elasticsearch-Alternativen für Sie zu finden.

b. Grafana/Prometheus

Das Grafana-Dashboard zusammen mit der Prometheus-Zeitseriendatenbank ermöglicht es uns, verschiedene Details über unsere Infrastruktur wie CPU/RAM/Speicher usw. festzuhalten und uns in nahezu Echtzeit über den aktuellen Status der Infrastruktur auf dem Laufenden zu halten.

So können wir Warnmechanismen implementieren, die es unserem DevOps-Team erleichtern, mit Zwischenfällen in unserer Infrastruktur umzugehen. Trotz aller Vorbereitungen und der besten Architektur und des besten Designs können immer noch Dinge kaputt gehen. Die Grafana Prometheus Systeme, zusammen mit Alertmanager, ermöglichen es, Vorfälle in der Produktion zu entschärfen.

Darüber hinaus profitiert dieser Stack vom Application Programming Management, das uns eine Vielzahl von Metriken zur Verfügung stellt, die uns wichtige Einblicke in die Produkte, ihre Nutzung und mehr geben.

6. Unterbrechung der Entwicklungsumgebungen

Since we were walking on the path of microservices, we had to manage over 40 microservices which when orchestrated together to form our web service www.xoxoday.com. This implies a lot of communication between these microservices, and the sheer number of them makes it impossible and impractical to run it locally on the developer’s machine.

Daher mussten wir kreativ werden und in die Tiefen der Native-Cloud-Welt vordringen, um eine mögliche Lösung zu finden. Wir haben viele gesehen, um nur einige zu nennen:

- Telepräsenz

- Skaffold

- Kubefwd

Derzeit ist Kubefwd unser Favorit und das nützlichste Tool, das unsere Anforderungen erfüllt, aber wir werden in Zukunft vielleicht auch andere Tools ausprobieren. Mit Kubefwd können wir Kubernetes-Dienste auf der lokalen Workstation über denselben DNS zugänglich machen, als ob sich der Rechner des lokalen Entwicklers innerhalb des Kubernetes-Clusters befinden würde!

Dies bedeutet einen erstaunlichen Produktivitätsschub, der die Effizienz der Entwickler steigert und gleichzeitig das Gesamterlebnis verbessert und die Zeitspanne bis zur Veröffentlichung einer neuen Funktion verkürzt.

7. Schlussfolgerung

Nachdem wir monatelang unsere Infrastrukturanforderungen unter die Lupe genommen und mit den geschäftlichen Anforderungen, dem zukünftigen Wachstum und den aktuellen Trends in der Infrastruktur- und Cloud-Native-Welt in Einklang gebracht haben, sind wir nun endlich an einem Punkt angelangt, an dem wir zuversichtlich auf die kommenden Tage blicken können.

Unsere Infrastruktur hat nicht nur die Evolutionssprünge eines Jahrzehnts vollzogen, sondern es ist ihr auch gelungen, die Belastung der DevOps-Teams zu verringern und gleichzeitig die Produktivität der Entwickler drastisch zu erhöhen. Endlich ist unser Setup billiger und leistet mehr, und zwar besser!

Mit diesem Setup können wir unsere gesamten Dienste, das Frontend und das Backend einfach replizieren und innerhalb weniger Minuten automatisch einrichten. So können wir mehrere atomare, unabhängige Produktionsumgebungen erstellen, sie mit unseren CI/CD-Pipelines nachrüsten und das komplexe Setup automatisch und einfach verwalten und pflegen.

Der wichtigste Vorteil ist jedoch, dass unsere DevOps-Teams klein bleiben und linear skalieren können, während sie gleichzeitig eine exponentielle globale Skalierung unserer Dienste ermöglichen. Und das Tüpfelchen auf dem i ist, dass das DevOps-Team ruhige Abende und tolle Wochenenden verbringen kann, ohne reaktiv Produktionsprobleme bekämpfen zu müssen, da unser Design und unsere Architektur es uns ermöglichen, diese Herausforderungen proaktiv anzugehen.

Eines der fantastischen Dinge an unserem Setup ist, dass wir dank der Automatisierung Spot-Instanzen für unsere nicht produktiven und nicht kritischen Systeme nutzen können. Dies hilft uns bei der weiteren Optimierung und Konsolidierung der Kosten, während wir gleichzeitig über eine große Rechenleistung verfügen. Das bedeutet äußerst kosteneffiziente Setups, und falls AWS beschließt, unsere Spot-Instances abzuschalten - kein Problem, innerhalb weniger Minuten skalieren wir unsere dedizierten Instances automatisch und nutzen so das Beste aus beiden Welten!

Die aktuellen Trends in der Welt der Infrastruktur, insbesondere die Cloud Native-Entwicklungen, machen es zu einem aufregenden Jahrzehnt für DevOps; es fühlt sich an, als wären wir kurz davor, das Nirwana zu erreichen. Die schiere Anzahl der Tools, Lösungen, Services und Ökosysteme, die auftauchen, kann manchmal überwältigend sein. Rückblickend betrachtet steuern wir jedoch auf eine aufregende Ära felsenfester Webdienste zu, die robust und immer verfügbar sind und den Benutzern ein fantastisches Erlebnis bieten.

Pranav Salunke