Die Fünf-Minuten-IDE: Deskriptive Entwicklungsumgebungen

Mit deskriptiven Darstellungen lassen sich Standardaufgaben wie das Einrichten einer IDE automatisieren.

In Pocket speichern vorlesen Druckansicht 32 Kommentare lesen
Märchen, Bücher, Buch

(Bild: tomertu/Shutterstock.com)

Lesezeit: 11 Min.
Von
  • Alexander Serowy
Inhaltsverzeichnis

Ein Entwickler bekommt einen neuen Laptop, eine neue Mitarbeiterin kommt an Bord oder das Team hebt Projekte auf die jüngste LTS-Version. Damit geht die übliche Prozedur wieder los: Node.js installieren, Datenbanken aufsetzen, Netzwerk konfigurieren, nvm einrichten, lokale Projekteinstellungen umbauen und hoffen, dass am Ende alles läuft.

Wünschenswert wäre, mit einem Befehl alles einzurichten, um direkt loszulegen. Die Datenbanken sollten vorkonfiguriert und provisioniert sein, sobald man das Repository ausgecheckt hat und die Umgebung startet. Deskriptive Darstellungen der Projektumgebungen ermöglichen dieses Vorgehen.

Sie sorgen für reproduzierbare Umgebungen und lassen sich per Codeverwaltung versionierbar ablegen. Außerdem reduziert der beschreibende Ansatz die Fehleranfälligkeit und homogenisiert die Umgebungen über alle Projektbeteiligten. Separate Tools wie pyenv oder nvm, die mehrere Versionen einer Laufzeitumgebung ermöglichen, sind damit überflüssig. Schließlich lassen sich Arbeitsprozesse über mehrere Sprachen vereinheitlichen.

Im Folgenden dienen zwei konkrete Anwendungsfälle als Beispiele dafür, Entwicklungsumgebungen deskriptiv zu beschreiben und im Team zu verteilen.

Visual Studio Code lässt sich mit einer eigenen Definition in Docker-Containern verwenden. Docker fungiert dabei als Abstraktionsschicht zwischen Laufzeitumgebung und Betriebssystem. Mit einer deskriptiven Beschreibung in einem Dockerfile können Teams spezifische Versionen von Node.js und andere Abhängigkeiten reproduzierbar festlegen und durch Sourcecodeverwaltung im Projekt verteilen. Beim Erstellen eines Containers werden die Vorgaben ausschließlich darin installiert und haben keine Berührungspunkte mit dem Betriebssystem.

Durch die Reproduzierbarkeit und Entkopplung ist Schluss mit der "Works on my machine"-Mentalität, da externe Abhängigkeiten die Laufzeitumgebung nicht beeinflussen. VS Code bringt dazu sowohl eine Erweiterung als auch vorbereitete Container mit, die Teams dank des zwiebelartigen Aufbaus von Docker leicht erweitern und den eigenen Bedürfnissen anpassen können. Konfigurationen existieren für alle gängigen Sprachen und Umgebungen von Node.js über Python bis Rust. Darüber hinaus gibt es Images für spezielle Anforderungen wie Docker-in-Docker.

In den Containern muss der eigene Workspace als Mount eingebunden sein, damit die interne Laufzeitumgebung auf den Code zugreifen kann. Darüber hinaus bringen die Container den VS-Code-Server mit, über den Visual Studio Code auf dem Arbeitsplatzrechner eine Verbindung zum Container herstellt. Beispielsweise kann VS Code darüber bei der Fehlersuche auf Debug-Informationen zugreifen und Haltepunkte im Code an den Server kommunizieren.

Devcontainer in Windows 10 mit WSL 2

(Bild: Inhaltliche Vorlage: Thomas Maurer)

Die Konfiguration der Devcontainer findet in zwei Dateien statt. devcontainer.json enthält alle wichtigen Informationen zum Betrieb des Containers wie Erweiterungen innerhalb des Containers, welcher Nutzer im Container angemeldet sein soll und welcher Ordner als Workspace eingebunden wird. Im Folgenden ist die Standardumgebung für Rust beschrieben. Neben den Einstellungen für den Language Server rust-analyzer legt die Definition Erweiterungen für VS Code fest, die Entwicklerinnen und Entwickler projektspezifisch nutzen können.

{
  "name": "Rust",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "runArgs": ["--cap-add=SYS_PTRACE", 
              "--security-opt", "seccomp=unconfined"],

  // Set *default* container specific
  // settings.json values on container create.
  "settings": {
    "lldb.executable": "/usr/bin/lldb",
    // VS Code doesn't watch files under ./target
    "files.watcherExclude": {
      "**/target/**": true
    },
    "rust-analyzer.checkOnSave.command": "clippy"
  },

  // Add the IDs of extensions you want installed
  // when the container is created.

  "extensions": [
    "vadimcn.vscode-lldb",
    "mutantdino.resourcemonitor",
    "matklad.rust-analyzer",
    "tamasfe.even-better-toml",
    "serayuzgur.crates"
  ],

  // Use 'forwardPorts' to make a list of ports 
  // inside the container available locally.
  // "forwardPorts": [],

  // Use 'postCreateCommand' to run commands 
  // after the container is created.
  // "postCreateCommand": "rustc --version",

  // Comment out connect as root instead. More info: 
  // https://aka.ms/vscode-remote/containers/non-root.
  "remoteUser": "vscode"
}

Die Definitionen im Dockerfile legen fest, wie der Container aufgebaut ist und welche Abhängigkeiten der Laufzeitumgebung beim Erstellen zu laden sind.

# [Optional] Uncomment this section to install 
# additional packages.
# RUN apt-get update && export 
# DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install 
# --no-install-recommends <your-package-list-here>

Docker setzt beim Erstellen des Devcontainers alle wichtigen Konfigurationen. Anpassungen an den Dateien sind nur für spezielle Anforderungen beziehungsweise zum Definieren von Erweiterungen erforderlich. Ein Blick für ein tieferes Verständnis lohnt sich dennoch.

Devcontainer lassen sich zudem über docker-compose konfigurieren. Auf die Weise bieten sie ausreichend Flexibilität, um ganze Entwicklungsumgebungen zu konfigurieren. Von der Datenbank über den Message-Server bis zur Firewall ist alles flexibel, und Docker verhindert standardmäßig sogar im eigenen Netzwerk den Zugriff von außen. Über Konfigurationen lässt sich festlegen, dass beispielsweise bestimmte Ports für Webserver erreichbar sind. Viele typische Container-Definitionen sind über Docker-Hub verfügbar.