DEV Community

Uhltak Therestismysecret
Uhltak Therestismysecret

Posted on

CI/CD ohne YAML-Hölle: Dagger.io erklärt Pipelines als Code für DevOps

Hook – Warum ich mir die Hände verbrenne, wenn ich wieder YAML schreibe

Ich erinnere mich noch an den Moment, als ich in einem Teammeeting das Wort "YAML" hörte und fast sofort das Gefühl bekam, in einen Klimazwerg verwandelt zu werden – nur weil ich die nächste Stunde damit verbringen musste, ein verschachteltes Build‑File zu debuggen. Drei Ebenen steps:script:env: und das ganze Ergebnis kam trotzdem nie ganz ohne Überraschungen heraus. Während das heute noch ein Schlepptau für viele ist, hat mich Dagger.io aus dem Labyrinth geführt. In diesem Artikel zeige ich, warum das Paradigma Pipelines als Code ein Game‑Changer ist, wie Dagger das ganze Konzept ohne YAML realisiert und welche Stolperfallen Sie vermeiden sollten.


1. Von YAML zu echter Programmiersprache – das Kernkonzept von Dagger

Erklärung

Dagger ist ein Open‑Source‑Framework, das CI/CD‑Pipelines als erstklassige Code‑Komponenten behandelt. Anstatt Declaratives YAML zu schreiben, definiert man den Build‑ und Deploy‑Flow in einer echten Programmiersprache – derzeit Go, Python und TypeScript. Das bringt drei sofortige Vorteile:

  1. Typ‑Safety – Der Compiler prüft bereits beim Schreiben, ob ein Befehl existiert, ob Parameter passen und ob Rückgaben korrekt behandelt werden.
  2. IDE‑Unterstützung – Autocomplete, Refactoring und Linting funktionieren wie bei jedem anderen Code‑Projekt.
  3. Wiederverwendbare Funktionen – Logik kann in Bibliotheken ausgelagert und über Paket‑Managern wiederverwendet werden.

Beispiel 1 – Minimaler Dagger‑Workflow in Go

package main

import (
    "context"
    "dagger.io/dagger"
)

func main() {
    // 1️⃣ Dagger Client initialisieren – das ist das Pendant zu "docker login"
    client, err := dagger.Connect(context.Background())
    if err != nil { panic(err) }
    defer client.Close()

    // 2️⃣ Basis-Image auswählen
    node := client.Container().From("node:20-alpine")

    // 3️⃣ Quellcode einbinden
    src := client.Host().Directory("./my-app")
    node = node.WithDirectory("/src", src).WithWorkdir("/src")

    // 4️⃣ Build‑Schritt ausführen
    node = node.WithExec([]string{"npm", "install"}).WithExec([]string{"npm", "run", "build"})

    // 5️⃣ Artefakt exportieren
    _, err = node.Export(context.Background(), ".out")
    if err != nil { panic(err) }
}
Enter fullscreen mode Exit fullscreen mode

Hier sehen Sie, dass jede Zeile ein echter Go‑Befehl ist – keine versteckten Schlüsselwörter, keine -‑Syntax, die plötzlich einen Syntaxfehler wirft, weil ein Leerzeichen fehlt. Der Compiler weist Sie sofort darauf hin, wenn ein Befehl nicht existiert.

Einschätzung

Der psychologische Gewinn ist enorm: Entwickler fühlen sich nicht mehr wie „YAML‑Jäger“, sondern wie reguläre Software‑Ingenieure. Das reduziert die Lernkurve und erhöht die Code‑Qualität – weil das gleiche Werkzeug, das Sie zum Schreiben von Applikationslogik verwenden, jetzt auch Ihre Deploy‑Logik schreibt.


2. Praktische Dagger‑Features, die YAML nie bieten konnte

Erklärung

Dagger liefert ein container‑basiertes Abstraktionsmodell, das automatisch Caching, Parallelisierung und Secret‑Management integriert. Statt in jedem Schritt eigene Skripte zu schreiben, nutzen Sie die integrierten Methoden:

  • WithExec führt Befehle im Container aus.
  • CacheVolume definiert persistente Volumes für Zwischenergebnisse.
  • Secret importiert sensible Daten ohne sie im Git zu speichern.

Beispiel 2 – Caching von Node‑Modules mit Dagger

cache := client.CacheVolume("node-modules")
node = node.WithMountedCache("/src/node_modules", cache)
node = node.WithExec([]string{"npm", "install"}) // nutzt das Cache‑Volume
Enter fullscreen mode Exit fullscreen mode

Durch das explizite Caching wird das zweite Durchlaufen des Pipelines signifikant schneller – und das ohne separate actions/cache‑Step‑Definition, wie sie in GitHub Actions üblich ist.

Beispiel 3 – Secrets sicher einbinden

mySecret := client.Secret().File("./secrets/api_key.txt")
node = node.WithSecretVariable("API_KEY", mySecret)
node = node.WithExec([]string{"bash", "-c", "curl -H 'Authorization: Bearer $API_KEY' https://api.example.com"})
Enter fullscreen mode Exit fullscreen mode

Die Secret‑API sorgt dafür, dass das Geheimnis nie im Klartext ins Image gelangt, sondern zur Laufzeit im Container injiziert wird – analog zu Docker‑Secrets, jedoch komplett code‑seitig.

Einschätzung

Durch diese integrierten Bausteine wird das gesamte CI‑Setup modularer und wartbarer. Keine separate YAML‑Datei für Caching, keine extra‑Tool‑Wrapper – alles wird über die Dagger‑API orchestriert.


3. Wie Sie Dagger in Ihre bestehende Git‑Workflow‑Umgebung einbinden

Erklärung

Die meisten Teams nutzen bereits Git‑Hooks oder Plattform‑CI (GitHub Actions, GitLab CI). Dagger lässt sich nahtlos dort einbinden, indem Sie ein kleines Wrapper‑Script benutzen, das Ihren Dagger‑Code ausführt. So bleibt die bestehende Infrastruktur bestehen, ohne dass Sie komplett umziehen.

Beispiel 4 – GitHub Action, die Dagger ausführt

name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Dagger CLI
        run: |
          curl -L https://dl.dagger.io/dagger/install.sh | sudo sh
      - name: Run Dagger pipeline
        run: |
          dagger run go run ./ci/pipeline.go
Enter fullscreen mode Exit fullscreen mode

Der Aufruf dagger run go run ./ci/pipeline.go startet Ihren Go‑basierten Dagger‑Workflow. Der Rest des CI‑Systems bleibt unverändert.

Beispiel 5 – Lokale Entwicklung mit dagger develop

# Startet einen interaktiven Container, in dem Sie Befehle testen
dagger develop
Enter fullscreen mode Exit fullscreen mode

dagger develop spinnt einen temporären Container auf, in dem Sie Ihre Pipeline Schritt für Schritt ausprobieren – ähnlich wie docker run -it. Das reduziert die „It‑Works‑On‑My‑Machine“-Problematik drastisch.

Einschätzung

Die Integration ist leichtgewichtig und erfordert keine komplette Migration. Sie können Dagger zunächst in einem einzelnen Projekt testen und dann nach und nach weitere Pipelines migrieren.


4. Häufige Fehler – Was schiefgeht, wenn man Dagger ohne Vorbereitung einsetzt

Fehler Ursache Korrektur
Unvollständige go.mod‑Datei Beim Erstellen des Dagger‑Clients fehlt das Modul‑Setup, weshalb dagger.Connect fehlschlägt. go mod init example.com/my-pipeline && go get dagger.io/dagger ausführen.
Forgotten client.Close() Ressourcenlecks im CI‑Runner, weil der Dagger‑Client nicht geschlossen wird. Immer defer client.Close() nach dagger.Connect einbauen.
Caching‑Volume nicht gemountet Build‑Schritt wird jedes Mal neu ausgeführt, weil das Cache‑Volume fehlt. node = node.WithMountedCache("/src/node_modules", cache) hinzufügen.
Secrets im Repository gesichert Ein Entwickler checkt api_key.txt ein, weil das Secret‑API nicht richtig benutzt wurde. Verwenden Sie client.Secret().File(...) und .gitignore das Datei‑Path.
Verwechslung von Kontext und client WithExec wird auf einem falschen Container‑Instanz‑Objekt aufgerufen. Jede Transformation (From, WithDirectory etc.) gibt ein neues Container‑Objekt zurück – stets weiterreichen.

Warum diese Fehler häufig sind

Der Hauptgrund ist, dass Entwickler aus der YAML‑Welt kommen und plötzlich vollwertigen Code schreiben müssen. Das bedeutet, dass sie plötzlich an Dinge denken müssen, die in YAML implizit sind (z. B. Scope‑Management, Variable‑Lebensdauer). Ein kurzer Code‑Review und das Einführen von Linters (z. B. golangci-lint) helfen enorm dabei, diese Stolperfallen früh zu erkennen.


5. Fazit – Der nächste Schritt zu einer YAML‑freien CI/CD‑Strategie

Dagger.io zeigt, dass Pipelines nicht länger als deklarative Text‑Dateien existieren müssen. Sie werden zu erstklassigem Code, der von den gleichen Werkzeugen geprüft, getestet und versioniert wird wie Ihre Anwendung. Das Ergebnis: weniger Fehler, schnellere Builds und ein nachhaltigeres DevOps‑Ökosystem.

Mein konkreter Handlungsaufruf:

  1. Installieren Sie lokal dagger (curl -L https://dl.dagger.io/dagger/install.sh | sudo sh).
  2. Erstellen Sie ein neues Verzeichnis ci/ und legen Sie dort eine pipeline.go mit dem Minimalbeispiel aus Abschnitt 1 an.
  3. Fügen Sie einen GitHub‑Action‑Step ein, der dagger run go run ./ci/pipeline.go ausführt.
  4. Beobachten Sie das Caching‑Verhalten – passen Sie CacheVolume nach Bedarf an.
  5. Sobald das funktioniert, refaktorieren Sie Ihre bestehenden YAML‑Jobs Stück für Stück in Dagger‑Funktionen.

Damit haben Sie den ersten, entscheidenden Stein für ein YAML‑freies, Code‑zentrisches CI/CD‑System gelegt. Die Zukunft ist da – nutzen Sie sie, bevor Ihre Konkurrenz es tut.

Top comments (0)