Beliebte Suchanfragen
//

Development Container, Update.

17.6.2024 | 8 Minuten Lesezeit

Auf den Schultern von Titanen

Rainer Vehns Die Zukunft der IDEs – aus Sicht eines „Java-EE-Entwicklers“ und Jonas Hecht Development Containers & GitHub Codespaces kill the “works on my machine” problem haben Dev Container 2022 schon mal sehr gut sortiert und durchleuchtet: Rainer etwas allgemeiner mit remote (Cloud) IDE und Jonas mit einem Deep Dive Development Container (Dev Container).

Obwohl thematisch eng verwandt, lassen wir Remote IDE in diesem Post aussen vor und fokussieren auf Dev Container.

Kein RFC - Aber Quasi-Standard mit Open Source Spezifikation

Microsoft hat die VS Code Dev Container - Specification als Open Source 2021 offengelegt und pflegt die Spezifikation. IntelliJ implementiert diese Spezifikation ebenfalls, d.h. die Dev Container der beiden aktuellen Schwergewichte unter den IDE’s basieren auf dieser einen Spezifikation. Viele der Remote IDE’s implementieren im Backend entweder VS Code oder IntelliJ Engines. Auf diese Weise ist die VS Code Dev Container Spezifikation eigentlich so etwas wie ein Quasi-Standard.

VS Code - Flexibler Umgang

VS Code pflegt inzwischen einen sehr flexiblen Umgang mit Dev Containern: Man kann in bestehende Container “hooken” und ihn so zu einem Dev Container machen, VS Code erzeugt und verwaltet dann für jeden Dev Container eine Definition in einer eigenen devcontainer.json. Oder man verwendet eine eigene vorgegebene Dev Container Definition mit devcontainer.json, die VS Code direkt starten/öffnen kann.

IntelliJ - Am Start, aber noch nicht am Ziel

Dev Container sind für IntelliJ als Dev Containers Plugin (241.15989.20) verfügbar. IntelliJ setzt zwingend eine initiale bzw. minimale Dev Container Definition mit einer devcontainer.json voraus. Das macht man entweder händisch oder sucht sich ein passendes Template unter https://github.com/devcontainers/templates, die überwiegend auf Images von mcr.microsoft.com/devcontainers basieren. Kann man diskutieren, ob Vor- oder Nachteil: Wenn keine speziellen Images notwendig sind, spart das einiges an Zeit & Mühe.

Mit der richtigen devcontainer.json kann IntelliJ auch eine eigene, spezielle Umgebung als Dev Container nutzen. Allerdings erfordert das etwas Denk- und Handarbeit, weil IntelliJ keine Setup-Scripts direkt nutzen oder auf bestehende Container zugreifen kann, sondern ausschließlich devcontainer.json basiert arbeitet:

  • Setup-Aktionen wie z.B. eine DB in einem anderen Nicht-Dev Container inital befüllen oder einen Cache in einen bestimmten Zustand bringen etc. müssen entweder vollständig im Kontext von docker-compose ausgeführt werden oder im Kontext von devcontainer.json als Dev Container Lifecycle Scripts im Container realisiert werden werden. Bedeutet für 
    • docker-compose - ggf. ENTRYPOINT im docker-compose.yml (via command) überschreiben und/oder ggf. Service Spezifikationen im docker-compose.yml für Aktionen “missbrauchen”
    • devcontainer.json - Setup Aktionen als Dev Container Lifecycle Scripts ausführen bedeutet aber letztlich eine engere Verzahnung von Dev Container und containerisierter Umgebung. Als eigenständige Referenzumgebung z.B. im Issue nur noch bedingt bzw. nicht ohne Dev Container IDE nutzbar.
  • Das Dockerfile, alle Ressourcen, die vom Dockerfile für den Build benötigt werden (Scripts, Config-Files etc.), docker-compose.yml und devcontainer.json müssen im Zugriffspfad der IDE IntelliJ liegen.

Und Eclipse?

Die Grau- bzw. Weißhaarigen unter uns erinnern sich vielleicht vage an diese IDE. Aktuell aber scheint Eclipse nicht über die Funktionalität des Remote System Explorer hinausgekommen zu sein oder sich in Richtung Dev Container zu entwickeln.

Recap - Warum der Aufwand? Welche Use-Case?

Isolation - Die Entwicklungsumgebung nicht “vollmüllen” mit Installation unterschiedlicher Tools und Versionen aus unterschiedlichen Projekten (z.B. Python 2 und 3 Projekte oder der ganze Node.js Module-Zoo etc.). Das ist aber kein neues Thema: Vor dem Aufkommen der Container war diese Isolation durch Auslagerung der Projekte in Virtuelle Maschinen auf den Entwicklungsrechnern üblich. Oder noch früher unter Unix/Linux für Command Line Junkies mit chroot.

100% reproduzierbares Setup - Windows, Unix/Linux und MacOS kompatibel. Kein mühsames Fixen seltsamer Seiteneffekte auf dem Entwicklungsrechner mehr, weil irgendein längst vergessenes Tool in einer Version mit inkompatiblen Dependencies installiert ist - “Works-on-My-Machine” heisst: Läuft auch bei allen anderen. Ein gepflegtes Dockerfile und docker oder docker-compose reduzieren die Setup- und Update-Aufwände auf Entwicklungsumgebungen drastisch.

Debugging - Eine lokale, isolierte und debugbare Entwicklungsumgebung zur Fehlersuche aufzusetzen ist leider selten trivial. Neben bestimmten Versionen der Komponenten und dem Code für das Debuggen müssen oft auch bestimmte Zustände (Daten) hergestellt werden. Das lässt sich, isoliert im Container, einfacher/strukturierter/gezielter realisieren und vor allem gut automatisieren (Script).

In The Wild: Drupal debuggen - Den Affen im Sack entlausen.

Nach all der grauen Theorie haben wir natürlich auch für alle, die gerade ein angenehmes Zucken in den Fingerspitzen verspüren, ein ganz praktisches Beispiel für den hilfreichen Dev Container Einsatz aus den (Un)Tiefen des Alltags geborgen.

Das Problem

Drupal ist ein OpenSource Content Management System (CMS), in PHP mit Symfony geschrieben. Drupal lebt von und mit unzähligen Erweiterungen (Module), die Drupal funktional erweitern.

Im konkreten Fall wurde das Modul az_blob_fs hinzugefügt, um Ressourcen wie Bilder oder Videos direkt auf einem Azure Blob Storage abzulegen und von dort auch wieder lesen zu können.

Mit der Erweiterung konnten Bilder vom Azure Blob Storage unter bestimmten, reproduzierbaren Bedingungen nicht mehr angezeigt werden, siehe auch https://www.drupal.org/project/az_blob_fs/issues/3444134

Debuggen oder Loggen?

Mit statischer Codeanalyse und Logging (\Drupal::logger() und serialize()) konnte in diesem Fall das Problem auf eine fehlerhafte Berechtigungsprüfung eingegrenzt werden.

Berechtigungen werden in Drupal von Hooks geprüft, die auf verschiedenen Ebenen u.a. auch auf Modul Ebene implementiert werden. Der Call-Stack dieser Hooks ist zur Laufzeit dynamisch und lässt sich deshalb mit statischer Codeanalyse und Logging zur Laufzeit nur bedingt analysieren.

Also lieber: Mit der IDE klassisch debuggen - und das in genau einem der laufenden Container der Analyse Umgebung.

VS Code für Dev Container Vorbereiten

Es wurde VS Code mit dem Dev Container Plugin. verwendet. Docker bzw. Docker-Desktop oder Rancher-Desktop wird als gegeben angenommen.

Step 1: Start der Drupal Analyse-Umgebung

Für die Analyse wurde ein minimales Drupal Environment zusammengestellt und in einem GitLab Repository abgelegt. Das muss lokal geklont werden, anschließend wird die Umgebung mit einem Bash Script gestartet (unter Windows besser im WSL):

git clone https://github.com/codecentric/drupal-bug-reproduction.git
cd drupal-bug-reproduction
./quick-local-setup.sh

Wenn alles funktioniert hat, dann sollten folgende Container zu sehen sein:

docker ps -a

CONTAINER ID   IMAGE                                            COMMAND                  CREATED          STATUS          PORTS                                           NAMES
6f871b072ea1   quick-local-setup/nginx:latest                   "/docker-entrypoint.…"   37 minutes ago   Up 37 minutes   0.0.0.0:80->8080/tcp, 0.0.0.0:30007->8080/tcp   drupal-min-setup-debug-nginx-1
66ea06fbf210   mcr.microsoft.com/azure-storage/azurite:3.28.0   "docker-entrypoint.s…"   37 minutes ago   Up 37 minutes   0.0.0.0:10000-10002->10000-10002/tcp            drupal-min-setup-debug-azure_storage-1
013a1612db8a   quick-local-setup:latest                         "docker-php-entrypoi…"   37 minutes ago   Up 37 minutes   9000/tcp                                        drupal-min-setup-debug-php-1
59d1c1a58a54   bitnami/mariadb                                  "/opt/bitnami/script…"   37 minutes ago   Up 37 minutes   0.0.0.0:3306->3306/tcp                          drupal-min-setup-debug-db-1

Nach einer Drupal Installation liegt der komplette Drupal PHP Code im Image quick-local-setup:latest im Verzeichnis /opt/drupal/. Um den Code nicht nur für VS Code innerhalb des Containers sondern auch ausserhalb des Images verfügbar zu machen, wird ein temporärer Container mit diesem Image gestartet und der Drupal PHP Code aus dem Container heraus in ein lokales Verzeichnis ./container-src/opt/drupal kopiert. Zum Schluss wird der eigentliche Drupal Container gestartet und das Volume mit der Kopie des PHP Codes unter /opt/drupal gemountet. Damit wird nicht nur der Code, sondern auch die spätere VS Code spezifische Projekt-Konfiguration unabhängig vom Lifecycle des Containers persistiert.

Step 2: Mit VS Code in den PHP Container hooken

VS Code kann sich mit einem laufenden Container verbinden und alle Nötige installieren (remote VS Code Engine, Plugins etc.) um diesen Container als Dev Container zu verwenden. VS Code merkt sich in einer devcontainer.json, welche Komponenten installiert wurden.

Auf das Remote Explorer Symbol klicken (Icon in der linken Leiste unterhalb "Extensions") und dann mit dem Container drupal-min-setup-debug-php-1 verbinden (Pfeil-Icon rechts):

Um später die Verbindung zum Dev Container wieder zu beenden, auf die blaue "Remote Connection" Status Bar links unten klicken und "Disconnect Remote" aus dem Drop-Down Menü wählen:

Step 3: VS Code Plugin Im Container Installieren: PHP Debug (Xdebug)

VS Code arbeitet jetzt innerhalb des Dev Containers drupal-min-setup-debug-php-1. Im Container ist bereits der PHP Debugger Xdebug installiert (via Dockerfile). Um jetzt PHP Code mit VS Code in der IDE grafisch debuggen zu können, muss für VS Code im Dev Container remote noch das Plugin PHP Debug Xdebug installiert werden. Das macht man wie gewohnt in der IDE. VS Code merkt sich diese remote Installation und stellt beim erneuten Verbinden zu dem Container falls nötig die remote Installation wieder her.

Step 4: Debug/Launch Konfiguration: .vscode/launch.json

VS Code braucht diese Konfiguration, um Projekte zu starten oder zu debuggen.

Damit diese Debug Konfiguration im richtigen Ordner angelegt wird, muss in VS Code innerhalb des Dev Containers zuallererst in den Ordner /opt/drupal gewechselt werden (der wie in Step 1 beschrieben auf ein externes Volume gemountet ist). Die Projekt- bzw. VS Code spezifische Debug/Launch Konfiguration wird dort in .vscode/launch.json wie folgt erweitert (oder ggf. neu angelegt):

1{
2    "version": "0.2.0",
3    "configurations": [
4      
5        ...
6      
7        {
8            "name": "Debug Drupal",
9            "type": "php",
10            "request": "launch",
11            "runtimeArgs": [
12                "-dxdebug.mode=debug",
13                "-dxdebug.start_with_request=yes",
14                "-S",
15                "localhost:8000"
16            ],
17            "program": "",
18            "cwd": "/opt/drupal/web",
19            "port": 9003,
20            "serverReadyAction": {
21                "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
22                "uriFormat": "http://localhost:%s",
23                "action": "openExternally"
24            }
25        },
26      
27        ...
28
29    ]
30}

Step 5: Debuggen

Aus dem Hauptmenü "File" -> "Open ..." im Verzeichnis /opt/drupal/web die Datei index.php öffnen:

Dort in z.B. in Zeile 16 $kernel = new DrupalKernel('prod', $autoloader); eine Breakpoint setzen und das Debugging starten mit Konfiguration Debug Drupal.

Der Debugger startet einen internen Webserver an http://localhost:8000, öffnet einen neuen Tab im Standard-Browser mit dieser URL und stoppt dann sofort am Breakpoint.

Unter /opt/drupal liegt nun der komplette Drupal PHP Code und man kann mit dem Debugger und Breakpoints in die Tiefen der Eingeweide von Drupal und der Erweiterung az_blob_fs abtauchen.

Fazit

Auf den ersten Blick gerät man in Versuchung, sich irgendwie an Wackersteine im Rucksack zu erinnern: Wozu der extra Aufwand? Richtig, durchaus berechtigte Frage für Einzelkämpfer im lang laufenden Projekt.

Alle anderen profitieren von schnellem und funktionierendem Ramp Up und Update im Team sowie reproduzierbaren, portablen und flexiblen Environments, die man lokal einfacher debuggen kann.

VS Code zeigt den effektiven und flexiblen Umgang mit Dev Containern, IntelliJ ist noch nicht ganz am Ziel und Eclipse nimmt das Thema irgendwie nicht richtig wahr. Ein bisschen mehr Qual der Wahl wäre nicht schlecht.

Beitrag teilen

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.