Start / Seminare / Spring Boot 4 Grundlagen
Modul
Deployment und Betrieb
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Deployment und Betrieb
0:00 Im letzten Modul bringen wir Radwerk dorthin, wo Anwendungen heute laufen: in einen Container. Der Weg dahin ist kürzer, als viele erwarten — Spring Boot baut das Abbild selbst, ohne dass Sie eine Zeile Dockerfile schreiben. Danach lassen wir Anwendung und Datenbank gemeinsam starten, mit der richtigen Reihenfolge und einem geordneten Halt.
0:19 Und zum Schluss werfen wir einen ehrlichen Blick auf native Abbilder mit GraalVM: ein spannendes Verfahren, das gerade deshalb eine nüchterne Einordnung verdient, weil es so verlockend klingt.
Deployment und Betrieb
0:31 Drei fachliche Kapitel und ein Rückblick. Zuerst das Abbild — gebaut vom Maven-Plugin, ohne eigenes Dockerfile. Dann der Verbund aus Anwendung und Datenbank mit Docker Compose, samt der Frage, wie ein Container weiß, dass der andere bereit ist. Danach GraalVM mit seinen Möglichkeiten und seinen Grenzen. Und am Ende schauen wir gemeinsam zurück auf den Weg, den Radwerk in diesen sieben Modulen genommen hat.
In ein Docker-Abbild verpacken
0:56 Fangen wir mit dem Verpacken an. Wer schon einmal ein gutes Dockerfile für eine Java-Anwendung geschrieben hat, weiß, dass daran mehr hängt als drei Zeilen. Die gute Nachricht: Sie müssen es nicht. Kurz die Begriffe, weil sie oft vermischt werden. Ein Container ist eine leichtgewichtige, isolierte Laufzeitumgebung — laufend, konkret, vergänglich.
1:17 Ein Abbild ist die Vorlage dafür: unveränderlich, gespeichert, aus der beliebig viele Container entstehen können. Verhält sich zueinander wie Klasse und Objekt. Um eine Anwendung in einem Container laufen zu lassen, brauchen Sie also zuerst ein Abbild. Üblicherweise beschreibt man dessen Aufbau in einem Dockerfile. Für Spring Boot ist das nicht nötig, und das ist mehr als Bequemlichkeit — dazu gleich mehr.
1:43 Ein Befehl, und am Ende liegt ein Abbild in Ihrer lokalen Registry. Die Option zum Überspringen der Tests ist hier nur der Kürze halber dabei; im Ernstfall wollen Sie die Tests laufen lassen. Wichtiger ist die Fußzeile, und das ist die typische Stolperstelle: In der aktuellen Fassung baut das Plugin ein Abbild mit einer älteren Java-Laufzeit, als unser Projekt verwendet.
2:05 Sie müssen die Version also ausdrücklich setzen. Wer das übersieht, bekommt ein Abbild, das läuft — bis eine Sprachfunktion gebraucht wird, die es dort nicht gibt. Ein sehr unangenehmes Fehlerbild. Der erste Punkt ist die ehrliche Einschätzung: Ein gutes Dockerfile zu schreiben ist schwerer, als es aussieht. Naiv gebaut landet die gesamte Anwendung samt Abhängigkeiten in einer einzigen Schicht — und wenn Sie danach eine Zeile Code ändern, wird alles neu übertragen.
2:32 Das Plugin nutzt Buildpacks, also vorgefertigte Bausteine, die das Abbild sinnvoll schichten: Abhängigkeiten unten, Ihr Code oben. Bei einer Codeänderung ändert sich dann nur die kleine obere Schicht. Und der letzte Punkt ist ein Wartungsargument: Sie pflegen keine zweite Beschreibung des Aufbaus neben der Projektdatei. Der erste Punkt ist die Java-Version von eben, und ich erwähne ihn erneut, weil er so leise scheitert.
2:56 Der zweite ist eine Selbsteinschätzungsfrage: Ein eigenes Dockerfile ist völlig legitim, aber dann sollten Sie Schichtung und Zwischenspeicherung wirklich beherrschen — sonst tauschen Sie eine erprobte Lösung gegen eine schlechtere. Und der dritte ist eine Gewohnheit, die sich einschleicht: Tests beim Abbildbau zu überspringen, ist für den schnellen Versuch in Ordnung.
3:16 Wenn es dauerhaft im Skript steht, bauen Sie irgendwann ein Abbild aus Code, der nie geprüft wurde.
Mit Docker Compose betreiben
3:22 Ein Container allein reicht selten. Radwerk braucht MongoDB, und beide müssen sich finden. Dafür gibt es Docker Compose — und dabei stellt sich eine Frage, die man leicht unterschätzt: Wer startet zuerst? Docker Compose beschreibt mehrere Dienste in einer einzigen Datei, startet sie gemeinsam und spannt ein virtuelles Netz auf.
3:43 Der entscheidende Teil ist der letzte: In diesem Netz finden die Container einander unter ihrem Dienstnamen. Sie müssen also keine Adressen kennen und keine Ports von außen durchreichen, damit die Anwendung ihre Datenbank erreicht. Wenn der Dienst mongo heißt, ist er unter dem Namen mongo erreichbar. Das klingt selbstverständlich, ist aber der Grund, warum sich so ein Verbund auf jedem Rechner gleich verhält — ohne Anpassung an die lokale Umgebung.
4:08 Zwei Dienste, und die interessanten Zeilen stehen beim zweiten. Die Umgebungsvariable enthält den Rechnernamen mongo — genau den Dienstnamen von oben. Das ist der Punkt, an dem die Namensauflösung des Compose-Netzes sichtbar wird. Und beachten Sie, dass diese Verbindungsangabe als Umgebungsvariable gesetzt wird: Damit sind wir wieder bei Modul zwei.
4:29 Die Anwendung liest sie über denselben Mechanismus wie jede andere Einstellung und weiß nicht, dass sie von Compose stammt. Beim ersten Dienst steht außerdem ein Gesundheitscheck — der wird gleich wichtig. Der Ablauf ist kurz, aber die Fußzeile enthält die eigentliche Lehre. Ohne die Abhängigkeit auf den Gesundheitscheck startet die Anwendung möglicherweise, bevor MongoDB bereit ist.
4:51 Und das ist ein besonders unangenehmer Fehler, weil er nicht zuverlässig auftritt: Auf einem schnellen Rechner geht es meistens gut, auf einem langsamen oder unter Last nicht. Solche sporadischen Fehler kosten viel Zeit, weil man sie nicht reproduzieren kann. Deshalb sagt man dem Verbund ausdrücklich, dass der eine Dienst auf die Bereitschaft des anderen wartet — nicht auf dessen Start, sondern auf dessen Bereitschaft.
5:15 Und noch ein Betriebsthema, das oft vergessen wird. Wenn ein Container gestoppt wird, endet der Prozess — mitten in dem, was er gerade tut. Eine Anfrage, die zu neunzig Prozent bearbeitet ist, bricht ab, und der Aufrufer bekommt einen Verbindungsfehler. Mit der Einstellung auf einen geordneten Halt ändert sich das: Die Anwendung nimmt keine neuen Anfragen mehr an, bringt die laufenden aber zu Ende.
5:38 In einer Umgebung, in der Container ständig ersetzt werden — bei jeder Auslieferung, bei jeder Skalierung —, ist das der Unterschied zwischen unsichtbaren und sichtbaren Ausrollvorgängen. Drei Dinge sollen gelingen: ein Befehl startet alles, eine Anmeldung funktioniert, und beim Stoppen brechen laufende Anfragen nicht ab.
5:57 Der Hinweis am Ende ist die lehrreichste Zusatzaufgabe dieses Moduls — entfernen Sie den Gesundheitscheck und beobachten Sie den ersten Start. Möglicherweise passiert nichts, weil Ihr Rechner schnell genug ist. Genau das ist die Botschaft: Der Fehler versteckt sich. Wenn er bei Ihnen auftritt, sehen Sie, wie eine Anwendung scheitert, weil sie eine halbe Sekunde zu früh war.
6:19 Der erste Punkt hebelt genau den Vorteil aus, den wir eben besprochen haben: Feste Adressen statt Dienstnamen einzutragen, macht den Verbund abhängig von der konkreten Umgebung. Der zweite ist das sporadische Problem von vorhin — und es lohnt sich, ihn zu wiederholen, weil solche Fehler oft als Zufall abgetan werden. Und der dritte ist der geordnete Halt: Ihn zu vergessen, kostet keine Funktion, aber es kostet Anfragen.
6:43 In einer Umgebung mit häufigen Auslieferungen summiert sich das zu einer Fehlerrate, die niemand einem Ausrollvorgang zuordnet.
Native Abbilder mit GraalVM
6:50 Zum Schluss ein Thema, bei dem ich um Zurückhaltung bitte. Native Abbilder klingen nach reinem Gewinn — schneller Start, weniger Speicher. Der Preis steht seltener im Prospekt, gehört aber zur Entscheidung dazu. Der Ausgangspunkt ist ein Cloud-Thema: Wenn je nach Last hoch- und heruntergefahren wird, zählen Startzeit und Speicherbedarf unmittelbar.
7:12 Eine Instanz, die dreißig Sekunden zum Starten braucht, taugt nicht für kurzfristige Lastspitzen. GraalVM ist eine Alternative zur Java Virtual Machine, die möglichst viel Arbeit vom Programmlauf in den Bau vorverlegt und den Code in Maschinencode übersetzt. Der Gedanke ist bestechend: Was beim Bauen schon feststeht, muss beim Start nicht mehr ermittelt werden.
7:32 Nur steht eben nicht alles beim Bauen fest, und daraus ergeben sich die Grenzen. Die Logik dieser Tabelle ist ein Tausch, und die rechte Spalte verdient mehr Aufmerksamkeit als die mittlere. Startzeit und Speicher gewinnen Sie, dafür dauert der Bau länger. Die dritte Zeile ist die folgenreichste: Reflection funktioniert nicht ohne Weiteres.
7:53 Viele Java-Bibliotheken bauen darauf, und das Problem zeigt sich häufig erst zur Laufzeit, nicht beim Bauen. Und die letzte Zeile ist die ehrlichste — die Quelle unseres Kursmaterials stuft das Verfahren als noch experimentell ein. Das heißt nicht, dass es nicht funktioniert. Es heißt, dass Sie Zeit einplanen müssen. Die ersten beiden Punkte beschreiben, wann sich die Rechnung trägt: viele Instanzen, die kurzfristig kommen und gehen, und Speicher, der unmittelbar Kosten verursacht.
8:21 Bei einer Anwendung, die dauerhaft mit drei Instanzen läuft, sparen Sie praktisch nichts. Der dritte Punkt ist die technische Voraussetzung, und die sollten Sie vorher prüfen, nicht hinterher. Und der letzte ist meine Empfehlung in einem Satz: Nicht als Gewohnheit für jede beliebige Anwendung. Wenn Sie den Nutzen nicht beziffern können, ist das eine gute Begründung, es zunächst zu lassen.
8:44 Der erste Punkt ist die Reihenfolge: erst prüfen, dann bauen. Der zweite ist die konkrete Ausprägung davon — Bibliotheken, die auf Reflection setzen, und ein Scheitern, das erst spät auffällt. Je später in der Kette, desto teurer. Und der dritte ist der, der mir am wichtigsten ist: Startzeit zu optimieren, wo niemand auf den Start wartet. Das ist ein klassisches Beispiel für Optimierung ohne Messung.
9:08 Wenn Ihre Anwendung dreimal im Monat neu startet, ist eine halbierte Startzeit keine Verbesserung, die irgendjemand bemerkt.
Rückblick auf das Seminar
9:16 Damit sind wir am Ende des fachlichen Teils. Bevor wir schließen, lohnt ein Blick zurück auf den Weg — und zwar entlang von Radwerk, weil sich daran am besten zeigt, was eigentlich passiert ist. Vor zwei Tagen war Radwerk ein leeres Projekt aus dem Initializr, das nichts konnte außer starten. Jetzt ist es ein Dienst, der Auskunft gibt, Anmeldungen annimmt, seine Daten in zwei verschiedenen Speichern hält, sich selbst prüft, über seinen Zustand Auskunft gibt und als Container samt Datenbank startet.
9:45 Und jetzt der bemerkenswerte Teil: Der Weg dorthin bestand fast ausschließlich aus Abhängigkeiten und wenigen Einstellungen. Wir haben erstaunlich wenig Infrastrukturcode geschrieben. Fast alles, was wir getippt haben, war entweder Fachlichkeit oder eine Beschreibung dessen, was wir wollten. Wenn Sie diese Tabelle von oben nach unten lesen, sehen Sie eine Bewegung von innen nach außen.
10:08 Die ersten Module bauen die Anwendung selbst — Gerüst, Konfiguration, Schnittstelle, Daten. Ab Modul fünf geht es nicht mehr darum, was die Anwendung kann, sondern darum, wie man sich auf sie verlassen kann: prüfen, beobachten, ausliefern. Diese zweite Hälfte wird in Projekten gern nach hinten geschoben, weil sie fachlich nichts hinzufügt.
10:27 Sie entscheidet aber darüber, ob die erste Hälfte im Betrieb überhaupt etwas wert ist. Wenn Sie aus diesen zwei Tagen nur vier Sätze behalten, dann bitte diese. Starter bündeln Abhängigkeiten, die Autokonfiguration richtet sie ein — zwei Schritte, nicht einer. Konfiguration kommt von außen, mit fester Rangfolge der Quellen; das ist Ihr Diagnosewerkzeug bei jedem unerwarteten Wert.
10:51 Repositories sind Schnittstellen, deren Implementierung zur Laufzeit entsteht, und deshalb sind Methodennamen dort Abfragen. Und die Anwendung ist ein eigenständiges Jar, das zum Container wird — jene Umkehrung aus Modul eins, die alles Weitere erklärt. Drei Haltungen zum Schluss, die im Weg stehen können. Die Vorgaben von Spring Boot für unveränderlich zu halten, führt zu Umwegen um Dinge herum, die sich mit einem Abhängigkeitstausch ändern ließen.
11:18 Autokonfiguration als Magie abzutun, ist bequem und verhindert das Verstehen — die Startmeldungen und die Endpunkte aus Modul sechs sagen Ihnen genau, was passiert ist. Und der dritte ist der ehrlichste: Nach einem Überblickskurs beim Überblick zu bleiben. Was Sie hier gesehen haben, sitzt erst, wenn Sie es einmal selbst gebaut haben.
Danke!
11:38 Damit sind wir am Ende. Radwerk läuft — als Container, mit Datenbank, mit Selbstauskunft. Die Materialien und der Beispielcode liegen in der Freigabe, damit Sie in Ruhe nachvollziehen können, was wir gebaut haben. Was sich als Nächstes lohnt: Sicherheit, also Authentifizierung und Autorisierung, dann Nachrichtenverarbeitung und schließlich die tieferen Themen rund um Spring Web MVC — dieses Seminar hat davon nur die Oberfläche berührt.
12:03 Vielen Dank für Ihre Aufmerksamkeit und die zwei Tage. Bei Fragen erreichen Sie uns unter der angegebenen Adresse.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →