tech-trends

Vom Sourcecode zu einer fertigen Anwendung in einem Container. Das ist der “source to image” (S2I) Build.

// 22-01-2020

Einleitung

Eine schnelle Methode um vom Quellcode zu einer fertigen Anwendung in einem Container zu gelangen, ist der “source to image” (S2I) Build. Dabei wird der Code aus einem Git Repository geholt, kompiliert und die Anwendung in einem Container Image installiert.

In diesem Artikel zeigen wir Ihnen unseren S2I Maven Builder. Der Builder kann Java-basierte Git-Repositorys aus dem Quellcode mit Maven erstellen, sofern eine pom.xml vorhanden ist. Unser Beispiel baut auf der Hello World-Anwendung von Wildlfy/Quickstart auf. Darüber hinaus zeigen wir auch einige Vorteile sogenannter „verketteter“ Builds.

Anwendungsfall

Wir haben unseren Anwendungs-Quellcode in einem Git-Repository (Wildlfy/Quickstart) verfügbar. Wir möchten die Anwendung in einen Container umwandeln und mit Wildfly als Laufzeitumgebung in OpenShift oder OKD ausführen.

Hauptziele

  • Erstellen eines Image, das unsere Anwendung ausführen kann
  • Reduzieren der Imagegröße um die Übertragungs- und Deployment-Zeit zu minimieren
  • Der Quellcode der Anwendung soll nicht Teil des endgültigen Images sein
  • Keine unnötigen Pakete im endgültigen Image um die potenzielle Angriffsfläche minimal halten
  • Das Image muss in der Lage sein, mit einer zufälligen UID laufen zu können (keine root Berechtigung im Runtime-Image benötigt)

Bonusziele

  • Je schneller wir bauen können, desto glücklicher ist unsere Nutzerbasis
  • Robuste Builds: Wenn wir nicht schnell bauen können, sollten wir dennoch bauen können

Unsere Einschränkungen

  • Es ist uns nicht gestattet, Elemente im Quellcode-Repository zu ändern

Voraussetzungen

  • Zugriff auf einen OpenShift oder OKD-Cluster

Implementierung 1:
Hauptziele erfüllen

1) Neues Projekt erstellen

Wir erstellen ein neues Projekt in OpenShift, um alle Ressourcen zu speichern, die sich auf den S2I Maven Builder und die Hello World Anwendung beziehen.

 

oc new-project s2i-builder-maven \
     --display-name="S2I Maven Builder" \
     --description="This project contains all resources to build the S2I Maven Builder and use 
                    the builder to compile and run the hello world application. The hello world
                    application used here is available on github (wildfly/quickstart)."

2) Erstellen des Builder-Image

Wir erstellen ein S2I Maven Builder-Image aus unserem Git Repository und bezeichnen es als s2i-builder-maven. Dabei wird eine BuildConfig und ein ImageStream mit dem Namen s2i-builder-maven erstellt und der erste Build unseres Images ausgelöst.

 

oc new-build https://github.com/gepardec/openshift-builder-maven#1.0.0 --name=s2i-builder-maven

 

Hinweis: #1.0.0 am Ende der Repository-URL gibt ein Tag oder einen Branch an. Die Angabe eines Tags oder eines Branch ist optional. Wenn nichts angegeben ist, wird der Master ausgewählt.

3) Artefakt bauen

Jetzt haben wir unsere erste Komponente installiert: unseren S2I Maven Builder s2i-builder-maven. Als Nächstes verwenden wir unser neu erstelltes Builder-Image, um unsere Hello World-Anwendung aus dem Quellcode mit den S2I-Skripten des Builders zu erstellen.

ACHTUNG!: Da unsere Hello World-Anwendung die pom.xml unter helloworld/pom.xml hat, können wir contextDir=helloworld für unsere BuildConfig setzen. helloworld/pom.xml verweist jedoch auf eine pom.xml, die sich im Stammverzeichnis des Git-Repository befindet. Hier haben wir ein Problem mit der aktuellen Implementierung von contextDir.
Wie in der OpenShift 4.2-Dokumentation angegeben:

“Any input content that resides outside of the contextDir will be ignored by the build.”

Das heißt, ../pom.xml – also das pom.xml im Stammverzeichnis, würde in unserem Fall nicht gefunden werden, da es sich außerhalb des angegebenen contextDir befindet.

Aus diesem Grund bietet unser benutzerdefiniertes Build-Image eine zusätzliche Umgebungsvariable BUILDER_CONTEXT_DIR, die die oben genannte Einschränkung nicht aufweist. Kurz gesagt, wir werden unseren s2i-builder-maven dazu verwenden, um Wildfly/Quickstart mit dem Tag 18.0.0 final zu erstellen und es als binary-artefact bezeichnen. Unsere Anwendungs-pom.xml befindet sich in helloworld/pom.xml. Daher verwenden wir die Umgebungsvariable BUILDER_CONTEXT_DIR, um unser Kontext-Verzeichnis entsprechend festzulegen. Zusätzlich setzen wir BUILDER_MVN_OPTIONS so, dass das in der pom.xml angegebene OpenShift-Profil verwendet wird.

 

oc new-build s2i-builder-maven~https://github.com/wildfly/quickstart#18.0.0.Final \
     --name=binary-artefact  \
     --env=BUILDER_CONTEXT_DIR=helloworld \
     --env=BUILDER_MVN_OPTIONS="-P openshift"

 

Sie haben sich eine Pause verdient, ohne Optimierung dauert dies ungefähr 10 Minuten, abhängig von Ihrer Internetverbindung.

Hinweis: Mit BUILDER_MVN_OPTIONS können Sie jede maven-Option bereitstellen, die folgendermaßen bereitgestellt werden kann:

mvn clean package ${BUILDER_MVN_OPTIONS}

4) Artefakt mit Laufzeitumgebung kombinieren

Jetzt haben wir ein Image mit dem Namen binary-artefact, das unser Anwendungsartefakt in /deployments/helloworld/target/ROOT.war und unseren Quellcode enthält. Um unser Artefakt mit einer Laufzeitumgebung zu kombinieren, kopieren wir nur das Artefakt aus dem binary-artefact Image in unser neues Laufzeit-Image namens runtime, welches wir über ein inline angegebenes Dockerfile erstellen.

 

oc new-build --name=runtime --docker-image=jboss/wildfly \
     --source-image=binary-artefact \
     --source-image-path=/deployments/target/ROOT.war:. \
     --dockerfile=$'FROM jboss/wildfly \nCOPY ROOT.war
/opt/jboss/wildfly/standalone/deployments/ROOT.war'

 

Kurz gesagt, wir haben ein Image mit unserem Artefakt (ROOT.war), ein Image mit Wildfly und mit dem oben angegebenen Build definieren wir ein inline-Dockerfile, welches das Artefakt aus unserem Source-Image in unser neues Laufzeit-Image kopiert.

Sie haben sich eine Pause verdient, ohne Optimierung dauert dies ungefähr 10 Minuten, abhängig von Ihrer Internetverbindung.

Hinweis: Mit BUILDER_MVN_OPTIONS können Sie jede maven-Option bereitstellen, die folgendermaßen bereitgestellt werden kann:

5) Deployen der Anwendung

Der aufwendigste Teil ist nun erledigt. Wir haben unser endgültiges Image namens runtime erstellt. Als Nächstes wollen wir dieses unter dem Namen hello-world bereitstellen.

oc new-app runtime --name=hello-world

6) Anwendung freigeben

Um außerhalb des Clusters auf unsere Anwendung zugreifen zu können, müssen wir unseren hello-world Service über eine Route verfügbar machen.

oc expose svc/hello-world

7) Aufrufen der Anwendung

Sie können jetzt über Ihren Browser auf Ihre Anwendung zugreifen, indem Sie die von folgendem Befehl ausgegebene URL in Ihrem Browser öffnen.

oc describe route/hello-world | grep ``Requested Host:``

Hinweis: Alle in Implementierungs-Szenario 1 ausgeführten Befehle können durch Ausführen von: usecase.sh ausgeführt werden

Implementierung 2:
Erfüllung der Bonusziele

Um unsere Bonusziele zu erreichen, wollen wir unsere Anwendung schneller bauen und bereitstellen. Maven Builds beziehen ihre Abhängigkeiten normalerweise von Remote-Repositories wie Maven Central. Das Abrufen dieser Abhängigkeiten über das Internet mit jedem Build ist nicht performant. Wir können den Prozess beschleunigen, indem wir die erforderlichen Remote-Repositories in unserer Infrastruktur spiegeln und diesen lokalen Repository mirror verwenden.

Hierzu benötigen wir einen Nexus Repository Manager oder einen anderen Maven Mirror.

Um unseren Artefakt Build zu beschleunigen, können wir unseren eigenen Maven Mirror über eine dedizierte Umgebungsvariable festlegen. Kurz gesagt, wir ersetzen Schritt 3 aus Implementierung 1 durch

 

oc new-build s2i-builder-maven~https://github.com/wildfly/quickstart#18.0.0.Final \
     --name=binary-artefact \
     --env=BUILDER_MVN_MIRROR="* |
https://my-maven-mirror/path/to/maven-public/" \
     --env=BUILDER_MVN_MIRROR_ALLOW_FALLBACK=true

 

Hinweis: Sie können mehrere Maven Mirrors über die Variable BUILDER_MVN_MIRROR angeben. Weitere Informationen dazu finden Sie im Abschnitt “Verfügbare Umgebungsvariablen in S2I Maven Builder”.

Hinweis: Wir haben eine zusätzliche Umgebungsvariable BUILDER_MVN_MIRROR_ALLOW_FALLBACK eingeführt, die auf true oder false gesetzt werden kann. Bei false schlägt der Build fehl, wenn der angegebene mirror nicht verfügbar ist. true hingegen greift auf die in pom.xml angegebenen Repository zurück und baut stattdessen langsam.

Verfügbare Umgebungsvariablen in S2I Maven Builder

BUILDER_MVN_OPTIONS kann verwendet werden, um der Ausführung von Maven zusätzliche Parameter hinzuzufügen.

BUILDER_MVN_OPTIONS = ``-DskipTests``

BUILDER_CONTEXT_DIR kann verwendet werden, um den Speicherort der pom.xml im Git-Repository zu definieren, wenn die gesamte Ordnerstruktur des Repository erforderlich ist. Andernfalls können Sie contextDir in Ihrer buildConfig verwenden. Um beispielsweise helloworld/pom.xml zu verwenden kann folgende Umgebungsvariable gesetzt werden:

BUILDER_CONTEXT_DIR=helloworld

BUILDER_MVN_MIRROR kann verwendet werden, um Maven Mirror-Repositories anzugeben.

Ein Maven-Repository, das alle erforderlichen Abhängigkeiten spiegelt, kann wie folgt angegeben werden:

* | http://my-mirror.com/Pfad/zu/Repo

Es können mehrere Mirror angegeben werden, so kann beispielsweise mirror-A für central und mirror-B für JBoss verwendet wird:

central | http://mirror-A/Pfad/zu/Repo; jboss | http://mirror-B/Pfad/zu/Repo

BUILDER_MVN_MIRROR_ALLOW_FALLBACK kann verwendet werden um bei nicht verfügbaren mirrors auf die in pom.xml definierten Maven Repositories zurückzugreifen. Erlaubte Werte hierfür sind true / false mit false als Standardwert.

false … Fail, wenn der mirror nicht verfügbar ist
true … greift auf die in pom.xml angegebenen Maven-Repositories zurück, wenn der mirror nicht verfügbar ist

// Autor

Clemens