Labs: Container aus einer Compose-Datei

Manche Lektionen brauchen mehr als eine Datei: eine Datenbank zum Abfragen, eine API davor, einen Cache daneben. Ein Lab ist dieser Satz von Diensten, beschrieben in einer Compose-Datei, die auf der Seite des Labs geschrieben oder in Ihrem eigenen GitHub-Repository gespeichert ist.

Richten Sie eines ein, prüfen Sie, was jede Person bekommt, veröffentlichen Sie es und probieren Sie Ihre eigene Kopie aus. Danach weisen Sie es zu wie jede Aufgabe. Alle Lernenden starten ihre eigene Kopie von der Seite des Labs aus, sie bleibt zwischen Sitzungen erhalten, und am Ende geben sie sie zum Bewerten ab.

Um selbst eines auszuprobieren, zunächst ohne Modul, öffnen Sie „Entwickeln“ in der Leiste und wählen „Einen Container ausführen“. Es steht auch Besuchenden zur Verfügung: Ohne Konto können alle das Git-Lab oder das SQL-Lab starten, und jedes andere Beispiel braucht ein kostenloses Konto, das jeden Monat genug kostenloses Guthaben mitbringt, um den meisten Lektionen zu folgen. Eine Karte „Beispiel-Labs“ listet sechs auf: eine Datenbank mit Web-API, MySQL mit den Daten eines Shops und phpMyAdmin davor, Git mit Gitea sowie die Sicherheits-Labs Juice Shop, DVWA und WebGoat. Jedes hat eine eigene Seite in diesem Handbuch und ein „Starten“, das es mit einem Klick startet, oder „Zuerst die Datei bearbeiten“, um es vor dem Start zu ändern. Auf dieser Seite führt „Starten“ aus, was auf dem Bildschirm steht, und behält die Volumes, das Öffnen schreibt nichts, die Datei wird bei einem Konto eine Sekunde nach dem letzten Tippen gespeichert und bei Besuchenden nur geprüft, und „Rückgängig“ nimmt ein geladenes Beispiel zurück.

Ein Lab kann auch eine Ubuntu-VM sein: Wählen Sie „Ubuntu-VM“ als Art des Labs, und jede Person bekommt eine eigene Maschine, die ein von Ihnen geschriebenes Skript einrichtet. Siehe „Ein VM-Lab einrichten“ weiter unten.

Einrichten

Fügen Sie unter „Lab“ eine Aufgabe mit der Karte „Container-Lab“ hinzu. Ein neues Lab beginnt mit „Hier geschrieben“ und einem bereits enthaltenen Beispiel: eine Datenbank und davor eine Web-API, die das Terminal mit psql und curl erreichen kann. Veröffentlichen Sie es unverändert, um ein laufendes Lab zu sehen, oder ändern Sie es vorher. Die Datei wird eine Sekunde nach dem letzten Tippen gespeichert und beim Speichern geprüft. Eine hier geschriebene Datei kann nur Images verwenden, da es kein Repository gibt, aus dem ein Image gebaut oder eine env_file gelesen werden könnte.

Für etwas Größeres wählen Sie „In einem GitHub-Repository“, das den Verlauf des Labs bewahrt und mit docker compose auf Ihrem eigenen Rechner genauso läuft. „Das Beispiel verwenden“ verweist das Lab auf mitsurutech/lab-sample, dessen README eine begleitende Lektion ist; machen Sie einen Fork, um es zu Ihrem eigenen zu machen. Ein zweites, mitsurutech/lab-mysql-phpmyadmin, ist MySQL mit einer Lerndatenbank und phpMyAdmin, mit SQL-Übungen in seinem README; geben Sie seine Adresse an, um es zu verwenden. Ein drittes, mitsurutech/lab-git-gitea, ist ein Git-Server, Gitea, mit einem Repository zum Klonen, zum Committen und Pushen aus dem Terminal, das git hat. Andernfalls geben Sie die Adresse des Repositorys, den Branch und den Ort der Compose-Datei ab der obersten Ebene des Repositorys an. Bei einem privaten Repository haken Sie „Privates Repository“ an und fügen ein Fine-grained Personal Access Token mit reinem Lesezugriff auf dieses eine Repository ein. Es wird verschlüsselt gespeichert und nie wieder angezeigt.

Das Formular speichert sich selbst. Bei jedem Speichern wird die Datei im neuesten Commit des Branches gelesen und gezeigt, was jede Person bekäme: jeden Dienst, das Image, aus dem er kommt, seinen Arbeitsspeicher, seine Ports und welche Volumes erhalten bleiben.

Die Karte „Beispiel-Labs“ auf der Seite „Container“, die jedes fertige Lab mit der Schaltfläche „Starten“, der Schaltfläche „Zuerst die Datei bearbeiten“ und einem Link zu seinem Code auf GitHub auflistet
Die Karte „Beispiel-Labs“: Jedes Lab startet mit einem Klick, ohne etwas einzurichten; das Git- und das SQL-Lab brauchen kein Konto, die anderen ein kostenloses.

Was eine Compose-Datei enthalten darf

Ein Lab läuft auf Maschinen, die sich die ganze Klasse teilt, deshalb darf eine Datei nur verlangen, was sich dreißig Lernenden gleichzeitig gefahrlos geben lässt. Images kommen von Docker Hub oder aus der Registry von GitHub (ghcr.io) oder werden aus einem Ordner eines öffentlichen Repositorys gebaut: „Veröffentlichen“ baut diese zuerst auf GitHub, und das entstehende Image ist öffentlich, legen Sie Passwörter also in einer env_file ab und nicht im Dockerfile. Ein Lab darf bis zu 6 Dienste und 2048 MB Arbeitsspeicher für die Kopie einer Person haben; ein Dienst ohne eigenes Limit zählt als 512 MB.

Alles, was die Maschine selbst erreichen würde, wird abgelehnt: privilegierte Container, eingebundene Ordner der Maschine, der Docker-Socket, das Netzwerk des Hosts, auf der Maschine veröffentlichte Ports und Werte, die mit ${...} aus ihrer Umgebung eingesetzt werden. Eine Datei, die eines davon verlangt, wird Zeile für Zeile abgelehnt, alle Probleme auf einmal und jedes mit seiner Zeile, sodass Sie sie in einem Push beheben können. Ein Dollarzeichen, das der Container sehen soll, wird $$ geschrieben, wie es docker compose vorsieht, sodass ein Befehl weiterhin $$HOME oder "$$@" sagen kann.

Die eigene Kopie ausprobieren

Sobald ein Commit veröffentlicht ist, klicken Sie auf der Seite des Labs auf „Starten“. Ihre Dienste starten in der Reihenfolge, die das depends_on der Datei vorgibt, jeder von den anderen über seinen Dienstnamen erreichbar, mit einem Terminal daneben: „Terminal öffnen“, wenn es läuft. „Beenden und speichern“ behält jedes benannte Volume und den Home-Ordner des Terminals, und „Erneut starten“ bringt sie zurück.

Das Terminal hat docker für die Dienste des Labs, über ihre Namen in der Datei: docker ps, um sie und ihren Zustand zu sehen, docker logs api, um zu lesen, was einer ausgegeben hat, docker exec -it db sh, um eine Shell in einem zu öffnen, docker restart, stop und start, docker inspect, top, stats und cp. Es lässt sich nichts erstellen, herunterladen, bauen oder entfernen, und nur die Dienste dieser Kopie sind erreichbar. In ein Image ohne Shell lässt sich nicht hineingehen, genau wie auf einem echten Server.

Ein Dienst lässt sich für den Browser öffnen, indem man ihm in der Datei ein Label gibt, lab.open: "8080", für einen Port, den er unter ports oder expose aufführt. Solange die Kopie läuft, hat ihre Karte einen Link zu jedem geöffneten Port, unter einer eigenen Adresse. Nur die Person, der die Kopie gehört, und die Mitarbeitenden des Moduls können ihn öffnen. Um eine API aus Postman, curl oder einem Programm aufzurufen, senden Sie das Token der Kopie, das auf der Karte steht, im Header X-Lab-Token; „Neues Token“ ersetzt es, falls es nach außen gelangt. Ports transportieren Webverkehr: Ein Datenbank-Client auf Ihrem eigenen Rechner kann sich nicht verbinden, nutzen Sie dafür also das Terminal des Labs. Wenn Sie einen Port öffnen, der üblicherweise zu einer Datenbank oder einem anderen Nicht-Web-Dienst gehört (5432, 3306, 6379, 27017 und so weiter), sagt die Seite das und nennt, wo man ihn stattdessen erreicht; es ist ein Hinweis und nie eine Ablehnung.

Zusammen dürfen sie 200 MB belegen. Die Seite warnt Sie, wenn sich eine Kopie dem nähert, und eine Kopie darüber behält beim Stoppen stattdessen den vorherigen Speicherstand.

Zuweisen

Haken Sie das Lab auf dem Tab „Aufgaben“ des Moduls an und klicken Sie auf „Zuweisen“, wie bei jeder Aufgabe: Die Lernenden erhalten die übliche Benachrichtigung und E-Mail. Ihr Link öffnet die eigene Seite des Labs, mit Ihren Anweisungen, einer Schaltfläche „Starten“ für die eigene Kopie und der Compose-Datei, die sie ausführt.

Die Lernenden behalten den Commit, mit dem sie begonnen haben. Wenn Sie eine Korrektur veröffentlichen, sagt die Seite des Labs, wie viele Kopien zurückliegen, und „Alle aktualisieren“ stellt sie auf diese um, sobald die jeweilige Person ihre Kopie das nächste Mal startet, wobei das Gespeicherte erhalten bleibt.

Eine Lektion, die in einem Lab durchgeführt wird

Eine Lektion kann von einem Lab getragen werden, einem Container-Lab oder einem Ubuntu-VM-Lab, sodass die Klasse am selben Ort liest und tippt. Wählen Sie im Editor der Lektion oben unter „Lab“ das Lab; angeboten werden nur die Labs des Moduls selbst. Mehrere Lektionen können sich eines teilen, etwa Git Teil 1 und Git Teil 2, und jede Person hat dann eine Kopie und ein Terminal für alle.

Fügen Sie für jeden Schritt einen Befehlsblock hinzu, einen Shell-Befehl von bis zu 20 Zeilen. In der Lektion sieht jede lesende Person oben die Leiste des Labs: „Lab starten“ und „Lab beenden“, einen Link zu jedem geöffneten Port und „Terminal öffnen“, das das vollständige Terminal in einem neuen Tab öffnet. „Ausführen“ bei einem Befehlsblock tippt diesen Befehl in das eigene Terminal, sodass, was aus der Lektion lief, in dessen Verlauf steht, und was er ausgegeben hat, erscheint unter dem Block. Ein Befehl, der nach einigen Sekunden noch läuft, etwa weil er auf ein Passwort wartet, läuft im Terminal weiter. „Ausführen“ lässt sich anklicken, wenn das Lab läuft, und startet das Lab nie selbst. Das Terminal eines VM-Labs ist die Ubuntu-Maschine, „Ausführen“ tippt also in die Maschine, sobald sie gestartet ist und ihr Einrichtungsskript fertig ist; vorher sagt es, dass die Maschine noch startet, und tippt nichts.

Auch Mitarbeitende haben eine eigene Kopie, ein am Projektor ausgeführter Befehl läuft also in Ihrer. Auch Lernende, deren Lab-Arbeit abgegeben oder bewertet ist, können es nicht aus der Lektion starten. Ein Befehlsblock in einer Lektion ohne Lab wird in der Liste dessen, was nicht fertig ist, im Editor markiert.

Beobachten und übernehmen

Die Seite des Labs listet die Klasse auf: wer eine Kopie gestartet hat, wer gerade das Terminal geöffnet hat, und zu jeder Person zwei Schaltflächen. „Beobachten“ öffnet das Terminal schreibgeschützt, sodass Sie sehen, was sie tippt, während sie es tippt. „Übernehmen“ lässt Sie darin tippen: Die Seite der Lernenden sagt „Ihre Lehrkraft tippt in Ihrem Terminal“, ihr Zugriffsprotokoll hält es fest, und „Zurückgeben“ beendet es. Wenn Sie eines von beiden zum ersten Mal öffnen, wird Ihnen ein Code per E-Mail gesendet, wie bei jedem Terminal.

Für Lernende, die nicht da sind, startet „Gespeicherte Kopie öffnen“, was zuletzt gespeichert wurde, in einer eigenen Sitzung von Ihnen mit eigenem Terminal. Nichts, was Sie dort ändern, wird in ihrer Kopie gespeichert. Es wird abgelehnt, solange die eigene Kopie dieser Person läuft: Beobachten Sie sie stattdessen.

Abgeben und bewerten

Die Lernenden stoppen ihr Lab und klicken auf „Abgeben“. Was ihre Kopie gespeichert hat, bleibt so erhalten, wie es in diesem Moment war, und die Kopie wird geschlossen: Sie können sie erst wieder starten, wenn Sie sie zurückgeben. Ein Lab, das noch läuft, lässt sich nicht abgeben, sodass das, was Sie bewerten, genau das Gespeicherte ist.

Die Klassenliste auf der Seite des Labs zeigt, wer abgegeben hat und wer bewertet ist, mit „Bewerten“ neben jeder Person. Es öffnet das Lab so, wie die Person es hat: wann sie es abgegeben hat, was sie auf einer VM geändert hat, das Gespräch mit ihr sowie Bewertung und Rückmeldung. „Gespeicherte Kopie öffnen“ aus der Klassenliste startet das Abgegebene unverändert in einer eigenen Sitzung von Ihnen.

Das Bewerten benachrichtigt die Lernenden und hält das Lab geschlossen. „Zur Überarbeitung zurückgeben“ öffnet es wieder, mit einem Grund, wenn Sie einen nennen, den sie in der Benachrichtigung und im Gespräch lesen. Was sie zuvor abgegeben haben, bleibt bestehen, bis sie erneut abgeben.

Veröffentlichen

„Veröffentlichen“ legt den Commit fest, den Sie gerade geprüft haben. Die Kopien der Lernenden starten von diesem Commit, und ein Push auf den Branch ändert für sie nichts, bis Sie erneut veröffentlichen.

Nur benannte Volumes werden für jede Person zwischen den Sitzungen gespeichert, zusammen mit dem Home-Ordner ihres Terminals. Alles andere, was sie in einem Container ändern, ist weg, wenn das Lab stoppt, so wie sich Container in der Produktion verhalten. Ein neues Image ändert keine Daten, die ein Volume bereits enthält: Startdaten und Startskripte laufen nur auf einem leeren Volume.

Jeder Dienst läuft als der eigene Benutzer seines Images, so wie docker compose ihn auf Ihrem Rechner ausführt, es sei denn, die Datei nennt einen Benutzer. Eine Kopie, deren Container mehr als 2 GB außerhalb der Volumes schreiben, wird gestoppt, mit gespeicherten Volumes, damit ein außer Kontrolle geratenes Protokoll nicht den Datenträger füllen kann, den sich die Klasse teilt.

Ein VM-Lab einrichten

Fügen Sie unter „Lab“ eine Aufgabe mit der Karte „Ubuntu-VM-Lab“ hinzu. Die Seite enthält statt einer Compose-Datei eine kurze Beschreibung der Maschine: cpus, memory in MB, disk in GB, open für die Ports, die sich im Browser öffnen, und setup, ein Skript, das als root läuft, wenn die VM jeder Person zum ersten Mal startet. Es beginnt mit einem Beispiel, das einen Webserver installiert, sodass „Veröffentlichen“ und „Starten“ sofort einen zeigen.

Die VM jeder Person bleibt zwischen den Sitzungen erhalten, alles auf ihrem Datenträger, und Sie können das Terminal einer Person beobachten oder übernehmen wie in jedem Lab. Das Skript setup läuft einmal pro Person, und seine Ausgabe steht in /var/log/mitsurutech-setup.log auf der VM. Die VM erreicht die Pakete von Ubuntu, PyPI, npm und GitHub und sonst nichts.

Um eine abgegebene VM zu bewerten, ohne sie selbst zu lesen, fügen Sie check hinzu, ein Skript in derselben Form wie setup. „Prüfung ausführen“ in der Zeile einer Person in der Klassenliste startet eine Kopie ihrer gespeicherten Maschine, die nicht zurückgespeichert wird, führt Ihr Skript als root aus und zeigt, ob es bestanden wurde (es endet mit 0), was es ausgegeben hat und wann. Die Lernenden erfahren das Ergebnis nicht, und nichts, was das Skript tut, ändert ihre Maschine.

Lernende in einer Einrichtung erhalten die Größe, die Sie angeben, bis zu 4 CPUs, 4096 MB Arbeitsspeicher und 20 GB Datenträger. Was Sie weglassen, ist der Standard der Einrichtung, 2 CPUs, 2 GB und 10 GB. Wer außerhalb einer Einrichtung das Lab öffnet, wird stattdessen auf die Größe der eigenen Stufe begrenzt.

Wenn eine Person ihre VM stoppt, zeigt die Klassenliste, was sie geändert hat: die installierten Pakete, die Dateien, die sie außerhalb der Systemordner geschrieben hat, und den Verlauf von /etc. Sie können es lesen, um die Arbeit zu bewerten, ohne ihre VM zu starten.

Netzwerk-Labs in einer einzigen VM

Ein Router braucht eine eigene Maschine und Lernende haben eine VM, deshalb bauen die Beispiel-Netzwerk-Labs die ganze Topologie in ihr auf: Jeder Router und jeder Host ist ein Netzwerk-Namespace, jede Verbindung ein Paar virtueller Kabel, und jeder Router führt sein eigenes FRRouting aus, mit der vtysh-Konsole, der Lernende an echten Geräten begegnen. Die Seite eines VM-Labs bietet drei zum Einstieg: statisches Routing, OSPF und BGP zwischen zwei autonomen Systemen. Die Verwendung eines ersetzt den Text, und „Rückgängig“ bringt Ihren zurück.

Alles wird vom eigenen Skript setup des Labs installiert, aus dem Paketarchiv von Ubuntu, beim ersten Start der VM, in etwa zehn Sekunden. Es installiert einen Befehl netlab mit: netlab status listet die Maschinen auf, netlab shell r1 öffnet die Konsole eines Routers und netlab shell h1 die eines Hosts, und netlab reset startet das Lab von vorn. Die echten Befehle funktionieren ebenfalls, und das ist der Sinn: sudo ip netns exec h1 ping 10.0.3.2, sudo ip netns exec r1 tcpdump -ni to-r2, sudo vtysh -N r1. Jedes Lab legt im Home-Ordner der Lernenden ein README ab, das sagt, was zu tun ist, und keine Lösungen verrät.

Jedes Beispiel hat eine Prüfung. Sie läuft nach einem frischen Start auf einer Kopie der gespeicherten Maschine, sieht also nur, was die Lernenden mit write memory gespeichert haben (auch das Verlassen von netlab shell speichert), und besteht nur, wenn der tatsächliche Zustand stimmt: Die Routen stehen in den Routingtabellen, die OSPF-Nachbarn sind Full oder die BGP-Sitzung ist Established, h1 erreicht h2 auf dem Weg über die Router, und das private Netzwerk bleibt unerreichbar. Eine verschobene Adresse, eine Firewall-Regel oder eine mit ip route getippte und nie gespeicherte Route besteht sie nicht, und die erste Zeile dessen, was sie ausgegeben hat, sagt, warum.

Um eine eigene Topologie zu erstellen, kopieren Sie ein Beispiel und ändern Sie die Topologie in dessen setup: eine Zeile für jeden Router, Host und jede Verbindung, und daemons ospfd oder daemons bgpd, um dieses Protokoll auf jedem Router zu starten. Das Werkzeug, eine Topologie und ein README passen bequem in die 16 KB von setup.

Häufige Fragen

Sollte ein Lab hier geschrieben oder in GitHub gespeichert werden?
„Hier geschrieben“ ist am schnellsten zum Ausprobieren und reicht für ein Lab aus fertigen Images. Speichern Sie es in GitHub, um Images aus einem Dockerfile zu bauen, seinen Verlauf zu bewahren, es mit docker compose auf Ihrem eigenen Rechner zu testen und mit Kolleginnen und Kollegen zu teilen. Der Wechsel zwischen beiden behält, was Sie für das andere eingestellt haben.
Kann ich ein Image verwenden, das ich selbst gebaut habe?
Ja. Pushen Sie es als öffentliches Image nach ghcr.io und nennen Sie es in der Datei, oder lassen Sie die Datei es aus einem Ordner im Repository bauen.
Können die Lernenden die Compose-Datei sehen?
Ja, schreibgeschützt, auf der Seite des Labs neben ihrer eigenen Kopie. So lernen sie, woraus das Lab besteht. Das Token wird dagegen niemandem angezeigt.