Strategie zum Bereitstellen / Hosten von Javascript-basierten statischen Websites in Containern

7

Dies kommt von Zeit zu Zeit in mehreren unserer Entwicklerteams vor, ohne dass wir den "richtigen" Weg gefunden haben:

Wir verwenden viele reaktionsbasierte Webanwendungen, die zu statischen Websites "kompiliert" werden, bei denen es sich nur um wenige HTML-, JS- und CSS-Dateien handelt.

Das "Erstellen" dieser Apps erfordert jedoch eine Reihe von Variablen, die Feature-Flags aktivieren / deaktivieren, Backend-URLs konfigurieren usw. Dies bedeutet, dass wir keine Binärdatei im herkömmlichen Sinne "erstellen" und beim Bereitstellen nur eine Konfigurationsdatei anwenden können. Zeit - Für den "Build" selbst müssen diese umgebungsspezifischen Variablen festgelegt sein. Daher können wir nur zur Bereitstellungszeit "erstellen".

Im Moment lösen wir dies, indem wir die erforderlichen Umgebungsvariablen in den Docker-Container einfügen und einen Start-Cmd nach dem Vorbild von ausführen

npm build && nginx run

Dies hat einige Nachteile:

  1. Der Erstellungsprozess benötigt im Verhältnis zu den Laufzeitanforderungen des Containers viel CPU / Speicher. Das heißt, wir müssen den Container für den Erstellungsprozess skalieren, anstatt die Laufzeitanforderungen - was sich falsch anfühlt
  2. Build-Fehler sind schwer zu "verfolgen". Wir können Healthchecks in Kubernetes verwenden, aber wenn ein Build 2 Minuten dauert, müssen wir noch 3 Minuten warten (1 extra für die Sicherheit), bevor wir den Healthcheck-Endpunkt des Containers testen können, um festzustellen, ob er aktiv ist.
  3. Bereitstellungen können lange dauern: Wenn wir Kubernetes für eine "serielle" Bereitstellung konfigurieren, wird jeder Pod gestartet und auf die "initialDelay" -Periode von 2-3 Minuten gewartet, bevor mit der nächsten begonnen wird. Dies bedeutet, dass wir uns leicht eine Bereitstellungszeit von 10 Minuten ansehen können, wenn die Bereitstellung auf 3-4 Pods skaliert wird.

Das alles fühlt sich für mich sehr suboptimal an. Es würde mich sehr interessieren zu hören, wie die Community das Rätsel "Build at Deployment Time" mit modernen Javascript-Webapps löst.

Mir ist klar, dass wir für Kubernetes "Init-Container" verwenden könnten, die den Build ausführen, die Artikel in einen dauerhaften Speicher stellen und die App-Container dann beim Start einfach aus dem dauerhaften Speicher ziehen lassen, aber dies fühlt sich immer noch eher so an, als würde man das Problem "umgehen" als Lösen des Grundproblems.

Trondh
quelle
Entschuldigung, ich verstehe nicht - warum laufen Sie npm buildzur Laufzeit anstatt während des Container-Builds?
Xiong Chiamiov
@XiongChiamiov Ich gehe davon aus, dass die Frontend-Konfiguration während des Builds erfolgt und ohne Neuerstellung nicht geändert werden kann (ich würde argumentieren, dass sie in einer enthaltenen Datei getrennt werden sollte, die im schlimmsten Fall beim Start des Containers "sed" sein könnte)
Tensibai
genau. npm buildist nur ein beliebiger Befehl. Könnte sein react buildoder sanity buildoder was auch immer das JS-Framework erwartet. Wir sind gezwungen, dies bei der Containerbereitstellung zu tun, da wir dann die Umgebung kennen build.
Trondh

Antworten:

5

Aus meiner Sicht wäre der beste Ansatz:

  1. Separater Erstellungsprozess mit Jenkins, der das NodeJS-Projekt in die Verteilung integriert und in das Docker-Image einschließt
  2. Starten Sie die Docker-Registrierung, die Docker-Images von Jenkins ansammelt (auf diese Registrierung sollte über den Kubernetes-Cluster zugegriffen werden können).
  3. Verschieben Sie Umgebungsvariablen in Jenkins Geheimnisse oder verwenden Sie ein separates Tool, um Konfigurationen von externem Git-Repo zu sammeln und zu kombinieren (wir verwenden Spring Cloud Config über die REST-API, um JSON / YML-Definitionen für jede Anwendung in jeder Umgebung zu sammeln).

Mit Jenkins können Sie ein kontinuierliches Liefersystem basierend auf generischen Pipelines konfigurieren. Möglicher Fluss wäre:

  1. Entwickler beenden ihre Arbeit in Korrespondenz mit GitFlow (letzte Pull-Anfrage in Release-Zweig zusammengeführt)
  2. Webhook löst Jenkins-Pipeline aus:
    • Stufe 1: Umgebungsdefinition sammeln
    • Stufe 2: Erstellen Sie die NodeJS-App mit npm
    • Stufe 3: Erstellen eines Nginx Docker-Images mit Verteilung
    • Stufe 4: Schieben Sie das Docker-Image in die Docker-Registrierung
    • Stufe 5: Bereitstellen / Aktualisieren des Dienstes in Kubernetes unter Verwendung von Standarddefinitionen
  3. Benachrichtigungen bezüglich der Ergebnisse senden

Dieser Vorgang kann mit Rancher visualisiert werden. Ich kann Ihre Fragen im Chat beantworten.

Maksim
quelle
Sie schlagen also vor, ein Bild pro Umgebung zu haben? Das ist eine Problemumgehung, die eine Menge Platz für nur einige Variablen verbraucht ...
Tensibai
@ Tensibai Sie sprechen über ~ 15 MB Bilder pro Umgebung. Mit Rotation und zusätzlichem Hausmeisterservice würde 1 GB ausreichen, um alle Projekte abzuwickeln.
Maksim
Wie würden Sie basierend auf dieser Lösung mehrere "Konfigurationen" einer App erstellen, beispielsweise eine für "Staging" und eine für "Prod"? (Nehmen wir an, es gibt eine Einstellung namens "BackendApi", die sich zum Beispiel unterscheidet.
Trondh
@Trondh wie gesagt wir verwenden Spring Cloud Config. Für jede Anwendung erhält sie eine Grundkonfiguration und wendet zusätzliche Konfigurationen von an profiles. Wenn Sie dies beispielsweise anfordern, erhalten https://config/backend.ymlSie die Konfiguration von application.ymlund zusätzliche Eigenschaften von backend.yml. Denn https://config/backend-stage.ymles wird mit Eigenschaften antworten, die von application.yml<- backend.yml<- angewendet werden backend-stage.yml.
Maksim
@Maksim 15 MB für ein paar Bytes Text sind eine Verschwendung. Irgendwann mit Hunderten von Microservices in verschiedenen Umgebungen mit verschiedenen Versionen werden sie zu TB-Daten für dieselben wenigen Bytes am Ursprung. Das ist meiner Meinung nach ein falscher Weg, wenn ich richtig verstanden habe, was Sie vorschlagen, da Sie nicht sicherstellen können, dass das Image in QA und Prod wirklich das gleiche ist wie aus zwei Builds (selbst mit demselben Code werden sie es nicht sein Die gleiche ID und Validierung wird zu einem Chaos. Aber vielleicht habe ich es falsch verstanden und Sie haben nur ein Bild und eine Konfiguration beim Hochfahren des Pods, wenn es so ist, habe ich Ihren Beitrag nicht richtig verstanden.
Tensibai