Zum Inhalt springen

Aufgabe 09 - Vom lokalen Projekt zum Image

Zu Zen-Modus wechseln

Sie schreiben das Dockerfile für Ihr Jahresprojekt und bauen daraus ein Image, das ohne root läuft und klein genug ist, um es bei jedem Deployment zu übertragen (siehe Kapitel Auslieferung mit Containern). Der Unterschied zwischen einem Image von 1,2 GB und einem von 180 MB ist keine Kosmetik. Dieses Image entsteht ab jetzt bei jedem Push.

  • Kapitel Auslieferung mit Containern, Abschnitte Warum überhaupt Container und Das eigene Dockerfile.
  • Ihr Projekt aus Aufgabe 03 mit einer Anwendung, die lokal startet.
  • Docker auf Ihrem eigenen Rechner.
  • Sie unterscheiden Image, Container und Volume und begründen, warum ein Container jederzeit ersetzbar sein muss.
  • Sie schreiben ein mehrstufiges Dockerfile, das ohne root läuft und ein schlankes Laufzeit-Image erzeugt.
  • Sie erklären den Unterschied zwischen Bauzeit- und Laufzeitvariablen und ziehen die Folgen für Geheimnisse.
  • Reproduktion: die drei Begriffe unterscheiden und ein Dockerfile nach Vorlage schreiben (Teil A).
  • Reorganisation und Transfer: die mehrstufige Bauweise auf das eigene Projekt übertragen und das Ergebnis messen (Teil B).
  • Reflexion, Problemlösung und Urteilsbildung: Bauzeitpunkte einordnen und die Geheimnisfrage beurteilen (Teil C).

Die Übung ist auf etwa zwei Stunden ausgelegt.

  1. Starten Sie einen Container aus einem fertigen Image mit docker run -it --rm alpine sh. Legen Sie darin eine Datei an, beenden Sie den Container und starten Sie einen neuen aus demselben Image. Notieren Sie, ob die Datei noch da ist, und erklären Sie das Ergebnis in zwei Sätzen.

  2. Wiederholen Sie den Versuch, diesmal mit einem Volume. Legen Sie es mit docker volume create uebung an und hängen Sie es beim Start mit -v uebung:/daten ein. Legen Sie die Datei unter /daten an und notieren Sie den Unterschied zu Punkt 1.

  3. Erklären Sie in je zwei Sätzen, was ein Image ist, was ein Container und was ein Volume. Nennen Sie zu jedem der drei Begriffe einen Bestandteil Ihres eigenen Projekts, der später dorthin gehört.

  4. Nennen Sie drei Dinge aus Ihrem Projekt, die beim nächsten Deployment verschwinden würden, wenn Sie sie im Container ablegen statt in einem Volume. Hochgeladene Bilder, eine SQLite-Datei neben der Anwendung und ein von Hand nachinstalliertes Paket sind die üblichen Kandidaten.

  1. Schreiben Sie ein einstufiges Dockerfile für Ihr Projekt mit Basis-Image, Abhängigkeiten, Quellcode, Build und Startbefehl. Bauen Sie es und notieren Sie die Größe aus docker images.

  2. Schreiben Sie daraus ein mehrstufiges Dockerfile mit den drei Stufen deps, build und runtime. In die letzte Stufe kommt nur, was zur Laufzeit gebraucht wird.

  3. Bauen Sie erneut und notieren Sie die neue Größe. Stellen Sie beide Werte gegenüber und rechnen Sie die Ersparnis in Prozent aus.

  4. Ergänzen Sie einen eigenen Benutzer, sodass der Prozess im Container nicht als root läuft. Weisen Sie das Ergebnis mit docker run --rm <image> id nach und notieren Sie die Ausgabe.

  5. Legen Sie eine .dockerignore an. Prüfen Sie danach mit docker run --rm <image> ls -la, dass weder .git noch .env noch node_modules im Image gelandet sind, und notieren Sie das Ergebnis.

  6. Starten Sie das Image lokal mit docker run --rm -p 3000:3000 --env-file .env <image> und rufen Sie die Anwendung im Browser auf. Notieren Sie, welche Variablen gefehlt haben, bis es lief.

  7. Sehen Sie sich mit docker history <image> an, welche Schicht wie viel zur Größe beiträgt. Nennen Sie die drei größten Schichten und zu jeder einen Weg, sie zu verkleinern. Setzen Sie eine dieser Maßnahmen um und stellen Sie die Größe vorher und nachher gegenüber.

  1. Ändern Sie eine einzige Zeile in Ihrem Quellcode und bauen Sie neu. Notieren Sie die Bauzeit und welche Schichten aus dem Cache kamen.

  2. Verschieben Sie COPY . . versuchsweise vor die Installation der Abhängigkeiten. Bauen Sie zweimal, ändern Sie zwischendurch wieder eine Zeile Quellcode, und notieren Sie die Bauzeit erneut. Stellen Sie beide Zeiten gegenüber und erklären Sie den Unterschied in drei bis vier Sätzen mit dem Schichtencache. Stellen Sie danach die richtige Reihenfolge wieder her.

  3. Legen Sie eine Tabelle Ihrer Umgebungsvariablen an mit den Spalten Name, Zweck, Bauzeit oder Laufzeit, Geheimnis?. Tragen Sie jede Variable ein, die Ihr Projekt kennt.

  4. Belegen Sie den Unterschied praktisch. Setzen Sie eine Variable mit dem Präfix NEXT_PUBLIC_ und eine ohne, bauen Sie das Image und suchen Sie beide Werte im ausgelieferten JavaScript. Notieren Sie, welchen der beiden Sie finden.

  5. Beantworten Sie in drei bis vier Sätzen, warum ein Geheimnis niemals ein NEXT_PUBLIC_-Präfix bekommen darf. Erklären Sie dabei, warum ein noch so gut abgesicherter Server daran nichts ändert.

  6. Bauen Sie versuchsweise ein Image, in dem .env mitkopiert wird, also ohne den Eintrag in .dockerignore. Finden Sie den Wert im fertigen Image wieder, etwa mit docker run --rm <image> cat .env. Beschreiben Sie in vier bis fünf Sätzen, warum ein Image kein sicherer Ort für Zugangsdaten ist. Gehen Sie darauf ein, was mit einem Image geschieht, das in einer Registry liegt oder weitergegeben wird. Löschen Sie das Image danach.

  7. Vervollständigen Sie .env.example mit allen Namen aus Ihrer Tabelle, weiterhin ohne echte Werte.

  8. Schreiben Sie einen Eintrag in docs/entscheidungen.md zum gewählten Basis-Image und zur Laufzeitstufe, mit den vier Abschnitten Entscheidung, Alternativen, Begründung und Folgen.

  1. Was unterscheidet ein Image von einem Container, und was von einem Volume?
  2. Warum muss ein Container jederzeit ersetzbar sein, und welche Fehler folgen aus dem Gegenteil?
  3. Warum wird package.json vor dem übrigen Quellcode kopiert?
  4. Was bringt eine mehrstufige Bauweise, und was genau bleibt in der letzten Stufe zurück?
  5. Wann wird eine Variable mit NEXT_PUBLIC_-Präfix gelesen, wann eine ohne, und was folgt daraus für Geheimnisse?
  6. Wozu dient .dockerignore, und welcher Eintrag darin ist der wichtigste?
  7. Warum läuft der Prozess im Container unter einer eigenen Kennung und nicht als root?

Dockerfile und .dockerignore liegen im Repository, das Image baut lokal und läuft ohne root. .env.example ist vollständig, der Eintrag zum Basis-Image steht in docs/entscheidungen.md. In protokoll.md stehen die Größenvergleiche aus Teil B, die Bauzeitmessungen aus Teil C, die Variablentabelle und das Ergebnis des Versuchs mit der mitkopierten .env.