Produktionsbereite Bilder für k8s

In dieser Geschichte geht es darum, wie wir Container in der Lebensmittelumgebung verwenden, insbesondere unter Kubernetes. Der Artikel befasst sich mit der Sammlung von Metriken und Protokollen aus Containern sowie mit der Erstellung von Bildern.

Bild

Wir sind vom Fintech-Unternehmen Exness, das Dienstleistungen für den Online-Handel und Fintech-Produkte für B2B und B2C entwickelt. In unserer Forschungs- und Entwicklungsabteilung gibt es viele verschiedene Teams mit mehr als 100 Mitarbeitern in der Entwicklungsabteilung.

Wir vertreten das Team, das für die Plattform zum Sammeln und Ausführen von Code durch unsere Entwickler verantwortlich ist. Insbesondere sind wir für das Sammeln, Speichern und Bereitstellen von Metriken, Protokollen und Ereignissen aus Anwendungen verantwortlich. Derzeit betreiben wir rund dreitausend Docker-Container in der Produktumgebung, unterstützen unseren 50-TB-Big-Data-Speicher und bieten Architekturlösungen, die auf unserer Infrastruktur basieren: Kubernetes, Rancher und verschiedene öffentliche Cloud-Anbieter. 

Unsere Motivation


Was brennt? Niemand kann antworten. Wo ist der Herd? Es ist schwer zu verstehen. Wann hat es Feuer gefangen? Sie können es herausfinden, aber nicht sofort. 



Warum stehen einige Container, während andere fallen? Welcher Container war schuld? In der Tat sind die Container außen gleich, aber innen hat jeder seinen eigenen Neo.



Unsere Entwickler sind gebildete Leute. Sie leisten gute Dienstleistungen, die das Unternehmen profitieren lassen. Aber es gibt Fälschungen, wenn Behälter mit Anwendungen zufällig gehen. Ein Container verbraucht zu viel CPU, der andere verbraucht Netzwerk, der dritte verbraucht E / A-Operationen und der vierte ist im Allgemeinen unklar, was er mit Sockets macht. All dies fällt und das Schiff sinkt. 

Agenten


Um zu verstehen, was im Inneren vor sich geht, haben wir uns entschlossen, Agenten direkt in Container zu füllen.



Diese Agenten sind Containment-Programme, die Container in einem solchen Zustand halten, dass sie sich nicht gegenseitig beschädigen. Agenten sind standardisiert, und dies ermöglicht einen standardisierten Ansatz für die Containerhandhabung. 

In unserem Fall müssen Agenten Protokolle in einem Standardformat mit Tags und Trab bereitstellen. Sie sollten uns auch standardisierte Metriken zur Verfügung stellen, die in Bezug auf Geschäftsanwendungen erweiterbar sind.

Agenten sind auch Dienstprogramme für Betrieb und Wartung, die in verschiedenen Orchestrierungssystemen arbeiten können und unterschiedliche Images unterstützen (Debian, Alpine, Centos usw.).

Schließlich müssen Agenten eine einfache CI / CD mit Docker-Dateien unterstützen. Andernfalls fällt das Schiff auseinander, da die Container auf "gebogenen" Schienen angeliefert werden.

Montageprozess und Zielgeräte-Image


Damit alles standardisiert und handhabbar ist, müssen Sie einen Standardmontageprozess einhalten. Deshalb haben wir uns entschlossen, Container für Container zu sammeln - eine solche Rekursion.



Hier werden die Container durch feste Konturen dargestellt. Zur gleichen Zeit beschlossen sie, Verteilungen in sie zu setzen, damit "das Leben nicht Himbeere scheint". Warum dies getan wurde, werden wir unten beschreiben.
 
Das Ergebnis ist ein Build-Tool - ein Container einer bestimmten Version, der auf bestimmte Versionen von Distributionen und bestimmte Versionen von Skripten verweist.

Wie benutzen wir es? Wir haben einen Docker Hub, in dem der Container liegt. Wir spiegeln es in unserem System, um externe Abhängigkeiten zu beseitigen. Der resultierende Behälter ist gelb markiert. Wir erstellen eine Vorlage, um alle benötigten Distributionen und Skripte im Container zu installieren. Danach sammeln wir ein betriebsbereites Image: Die Entwickler fügen den Code und einige spezielle Abhängigkeiten ein. 

Warum ist dieser Ansatz gut? 

  • Erstens die vollständige Versionskontrolle der Build-Tools - Build-Container, Skripte und Distributionsversionen. 
  • Zweitens haben wir die Standardisierung erreicht: Auf die gleiche Weise erstellen wir Vorlagen, die mittelschwer und betriebsbereit sind. 
  • Drittens bieten Container uns Portabilität. Heute verwenden wir Gitlab und morgen werden wir zu TeamCity oder Jenkins wechseln und auf die gleiche Weise können wir unsere Container starten. 
  • Viertens: Minimierung von Abhängigkeiten. Es ist kein Zufall, dass wir Distributionen in den Container legen, da wir sie dadurch nicht jedes Mal aus dem Internet herunterladen können. 
  • Fünftens hat sich die Assembler-Geschwindigkeit erhöht - die Verfügbarkeit lokaler Kopien von Bildern ermöglicht es Ihnen, keine Zeit mit dem Herunterladen zu verschwenden, da es ein lokales Bild gibt. 

Mit anderen Worten, wir haben einen kontrollierten und flexiblen Montageprozess erreicht. Wir verwenden dieselben Tools, um Container mit vollständiger Versionierung zu erstellen. 

Wie unser Build-Verfahren funktioniert




Die Assembly wird mit einem Befehl gestartet. Der Vorgang wird im Bild ausgeführt (rot hervorgehoben). Der Entwickler hat eine Docker-Datei (gelb hervorgehoben), die wir rendern, indem wir die Variablen durch Werte ersetzen. Und auf dem Weg fügen wir Kopf- und Fußzeilen hinzu - das sind unsere Agenten. 

Die Kopfzeile fügt Verteilungen aus den entsprechenden Bildern hinzu. In der Fußzeile werden unsere Dienste installiert, der Start der Arbeitslast, der Protokollierung und anderer Agenten konfiguriert, der Einstiegspunkt ersetzt usw. 



Wir haben lange darüber nachgedacht, ob wir einen Vorgesetzten einstellen sollen. Am Ende entschieden sie, dass wir ihn brauchten. Wählen Sie S6. Der Supervisor bietet die Containerverwaltung: Er ermöglicht Ihnen, im Falle eines Sturzes im Hauptprozess eine Verbindung zu ihm herzustellen, und bietet die manuelle Containerverwaltung, ohne sie neu zu erstellen. Protokolle und Metriken sind Prozesse, die in einem Container ausgeführt werden. Sie müssen auch irgendwie kontrolliert werden, und wir tun dies mit Hilfe eines Vorgesetzten. Schließlich kümmert sich der S6 um Housekeeping, Signalverarbeitung und andere Aufgaben.

Da wir nach dem Zusammenbau und dem Start verschiedene Orchestrierungssysteme verwenden, muss der Container verstehen, in welcher Umgebung er sich befindet, und auf die Situation reagieren. Zum Beispiel:
Auf diese Weise können wir ein Image sammeln und in verschiedenen Orchestrierungssystemen starten. Es wird unter Berücksichtigung der Besonderheiten dieses Orchestrierungssystems gestartet.

 

Für denselben Container erhalten wir in Docker und Kubernetes unterschiedliche Prozessbäume:



Die Nutzdaten werden unter dem S6-Supervisor ausgeführt. Achten Sie auf Sammler und Ereignisse - dies sind unsere Agenten, die für Protokolle und Metriken verantwortlich sind. Kubernetes hat sie nicht, aber Docker hat sie. Warum? 

Wenn Sie sich die Spezifikation des "Herdes" (im Folgenden - Kubernetes-Pod) ansehen, werden Sie sehen, dass der Ereigniscontainer im Herd ausgeführt wird, in dem sich ein separater Kollektorcontainer befindet, der die Funktion zum Sammeln von Metriken und Protokollen ausführt. Wir können die Funktionen von Kubernetes nutzen: Container in einem Herd, in einem einzigen Prozess und / oder in einem Netzwerkbereich ausführen. Stellen Sie Ihre Agenten vor und führen Sie einige Funktionen aus. Wenn derselbe Container in Docker gestartet wird, erhält er bei der Ausgabe dieselben Funktionen, dh er kann Protokolle und Metriken bereitstellen, da die Agenten im Inneren gestartet werden. 

Metriken und Protokolle


Die Bereitstellung von Metriken und Protokollen ist eine schwierige Aufgabe. Ihre Entscheidung hat mehrere Aspekte.
Die Infrastruktur wird erstellt, um die Nutzlast und nicht die Massenlieferung von Protokollen zu erfüllen. Das heißt, dieser Prozess sollte mit minimalen Anforderungen an Containerressourcen durchgeführt werden. Wir bemühen uns, unseren Entwicklern zu helfen: "Nehmen Sie den Docker Hub-Container, starten Sie ihn und wir können die Protokolle liefern." 

Der zweite Aspekt ist die Begrenzung des Protokollvolumens. Wenn in mehreren Containern das Protokollvolumen stark ansteigt (die Anwendung zeigt die Stapelverfolgung in einer Schleife an), steigt die Belastung der CPU, der Kommunikationskanäle, des Protokollverarbeitungssystems und dies wirkt sich auf den Betrieb des gesamten Hosts und anderer Container auf dem Host aus. Dies führt manchmal dazu "Fall" des Gastgebers. 

Der dritte Aspekt: ​​Sie müssen so viele Methoden zur Erfassung von Metriken wie möglich unterstützen. Vom Lesen von Dateien und Abfragen des Prometheus-Endpunkts bis zur Verwendung spezifischer Anwendungsprotokolle.

Und der letzte Aspekt - Sie müssen den Ressourcenverbrauch minimieren.

Wir haben uns für eine Open-Source-Go-Lösung namens Telegraf entschieden. Dies ist ein universeller Anschluss, der mehr als 140 Arten von Eingangskanälen (Eingangs-Plugins) und 30 Arten von Ausgängen (Ausgangs-Plugins) unterstützt. Wir haben es fertiggestellt und werden nun erklären, wie es mit Kubernetes als Beispiel verwendet wird. 



Angenommen, ein Entwickler stellt eine Last bereit und Kubernetes erhält eine Anforderung zum Erstellen eines Herdes. Zu diesem Zeitpunkt wird automatisch ein Container namens Collector für jeden Pod erstellt (wir verwenden den Mutations-Webhook). Sammler ist unser Agent. Zu Beginn konfiguriert sich dieser Container für die Zusammenarbeit mit Prometheus und dem Protokollsammelsystem.

  • Dazu verwendet er die Anmerkungen des Herdes und erstellt je nach Inhalt beispielsweise den Endpunkt des Prometheus; 
  • Basierend auf der Spezifikation des Herds und den spezifischen Einstellungen der Container wird entschieden, wie die Protokolle geliefert werden sollen.

Wir sammeln Protokolle über die Docker-API: Es reicht für Entwickler, sie in stdout oder stderr abzulegen, und dann wird Collector es herausfinden. Protokolle werden mit einer gewissen Verzögerung per Block gesammelt, um eine mögliche Überlastung des Hosts zu verhindern. 

Metriken werden für Workload-Instanzen (Prozesse) in Containern erfasst. Alles wird markiert: Namespace, unter usw. und dann in das Prometheus-Format konvertiert - und steht zur Sammlung zur Verfügung (außer für Protokolle). Außerdem senden wir Protokolle, Metriken und Ereignisse an Kafka und weiter:

  • Protokolle sind bei Graylog erhältlich (zur visuellen Analyse).
  • Protokolle, Metriken und Ereignisse werden zur Langzeitspeicherung an Clickhouse gesendet.

Ebenso funktioniert alles in AWS, nur ersetzen wir Graylog von Kafka durch Cloudwatch. Wir senden dort Protokolle, und alles läuft sehr bequem: Es ist sofort klar, wem der Cluster und der Container gehören. Gleiches gilt für Google Stackdriver. Das heißt, unser Programm funktioniert sowohl vor Ort mit Kafka als auch in der Cloud. 

Wenn wir keine Kubernetes mit Pods haben, ist das Schema etwas komplizierter, funktioniert aber nach denselben Prinzipien.



Die gleichen Prozesse werden im Container ausgeführt und mit S6 orchestriert. Dieselben Prozesse werden im selben Container ausgeführt.

Zusammenfassend


Wir haben eine Komplettlösung zum Zusammenstellen und Starten von Bildern mit Optionen zum Sammeln und Bereitstellen von Protokollen und Metriken erstellt:

  • Entwickelte einen standardisierten Ansatz für die Zusammenstellung von Bildern, basierend auf den entwickelten CI-Vorlagen;
  • Datenerfassungsagenten sind unsere Erweiterungen für Telegraf. Wir haben sie in der Produktion gut laufen lassen;
  • Wir verwenden Mutation Webhook, um Container mit Agenten in den Pods zu implementieren. 
  • Integriert in das Kubernetes / Rancher-Ökosystem;
  • Wir können dieselben Container in verschiedenen Orchestrierungssystemen ausführen und das erwartete Ergebnis erzielen.
  • Erstellt eine vollständig dynamische Containerverwaltungskonfiguration. 

Mitautor: Ilya Prudnikov

All Articles