© zffoto/Shutterstock.com
Migration nach AWS – Teil 6

Optimierung bei Containern


In den vorigen fünf Teilen dieser Serie haben wir uns mit der schrittweisen Optimierung einer Three-Tier-Java-Anwendung hin zu einer Serverless-Architektur und der Verschlüsselung von Daten beschäftigt. In diesem Teil der Serie werfen wir einen Blick auf mögliche Optimierungen für Container mit dem Ziel, deren Startzeit und Größe zu verringern.

Nicht alle Runtimes sind gleich. Eine wichtige Frage für Entwicklerteams ist: Besitzt die Anwendung eine Coldstart-Phase, die einen ersten Resource Burst verursacht? Es existieren einige Java Frameworks mit Dependency Injection und Annotation Scanning, die eine sehr intensive Coldstart-Phase verursachen. Aufgrund des dynamischen Wirings werden gerade in der Coldstart-Phase viele Ressourcen benötigt, die aber erst einmal komplett hochgefahren werden müssen. Gerade für Microservices ist es sinnvoll, die Paketgröße so klein wie möglich zu halten. Einige AWS SDKs wie das Java SDK in der Version 2.0 [1] sind modular aufgebaut. Damit können genau die Komponenten ausgewählt werden, die für die Anwendung benötigt werden, wie beispielsweise Module für Amazon DynamoDB und Amazon Kinesis Data Streams. Dies ermöglicht kleinere Paketgrößen und beschleunigt somit die Coldstart-Phase.

Optimierung von Containern

Ein weiterer Weg, um die Größe der Container zu reduzieren, besteht in der Nutzung von Multistage Builds und jlink. Mehrstufige Builds sind eine relativ neue Funktion, die Docker 17.05 oder höher erfordern. Dieses Pattern ist nützlich, um Dockerfiles zu optimieren und sie gleichzeitig leicht lesbar und pflegbar zu halten. Bei mehrstufigen Builds können sich mehrere FROM-Anweisungen im Dockerfile befinden und jede FROM-Anweisung kann ein anderes Basis-Image verwenden. Jede dieser Anweisungen beginnt eine neue Phase des Builds. Artefakte des Builds können selektiv zwischen Stufen kopiert werden, damit die Anzahl der zu kopierenden Dateien begrenzt werden kann.

Vor der Einführung von Multistage Builds war die Situation deutlich komplizierter. Eine der anspruchsvollsten Aufgaben beim Bauen von Docker Images ist es, sie so klein wie möglich zu halten, denn jede Instruktion im Dockerfile fügt einen neuen Layer hinzu. Ein übliches Pattern war es, ein Dockerfile für die Test-Stage zu nutzen und für die Produktionsumgebung ein reduziertes, das nur die Anwendung enthielt. Dieses Pattern wird das Docker Builder Pattern genannt. Das Problem bei diesem Pattern ist offensichtlich: Zwei Dockerfiles zu pflegen ist nicht ideal, zudem gibt es Unterschiede zwischen den Stages, was den initialen Ansatz, in jeder Stage das identische Docker Image zu verwenden, ad absurdum führt.

Schauen wir uns in Listing 1 ein ganz konkretes Beispiel an. In der ersten Stage des Builds erzeugen wir zunächst ein Builder Image für eine sehr kleine JDK-Distribution mit einem sehr limitierten Set an Modulen und Dependencies. Unser Builder Image basiert auf debian:9-slim. In den ersten RUN-Statements aktualisieren wir alle Debian-Pakete, laden die OpenJDK-Distribution Amazon Corretto herunter und entpacken das Paket. In einem der folgenden RUN-Statements nutzen wir jlink, um eine JDK-Distribution mit einem limitierten Set an Modulen zu bauen. jlink ist ein Kommandozeilenwerkzeug, das es erlaubt, Module und deren transitive Abhängigkeiten zu verbinden, um ein Laufzeit-Image zu bauen. Der vollständige Build-Prozess ist self-contained aufgesetzt, findet also in einem Docker-Container statt, und es werden keine externen Abhängigkeiten zu Maven oder einer Java Runtime auf dem Build-Server benötigt.

Listing 1

FROM debian:9-slim AS builder LABEL maintainer="Sascha Möllering <smoell@amazon.de>" # First step: build java runtime module RUN apt-get update RUN apt-get install wget -y RUN mkdir -p /usr/share/man/man1 RUN apt-get install openjdk-8-jdk-headless -y RUN wget http://mirrors.koehn.com/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz -P /tmp RUN tar xf /tmp/apache-maven-3.6.3-bin.tar.gz -C /opt RUN ln -s /opt/apache-maven-3.6.3 /opt/maven ENV M2_HOME=/opt/maven ENV MAVEN_HOME=/opt/maven ENV PATH=${M2_HOME}/bin:${PATH} COPY ./pom.xml ./pom.xml COPY src ./src/ ENV MAVEN_OPTS='-Xmx6g' RUN mvn -Dmaven.test.skip=true clean package RUN set -ex && \ apt-g...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang