Portainer GitHub Runner
Ein selbst-gehosteter GitHub-Actions-Runner als einzelner Portainer-Stack: zwei Dienste, ein Variablenblock, kein Build-Schritt. Verlagert den einen langen CI-Job auf eigene Hardware — mit den zwei Vorkehrungen, die solche Aufbauten sonst vermissen lassen.
Worum es geht
GitHub rechnet die Minuten eigener Runner nicht ab. Wer einen langen CI-Job auf eine eigene Maschine verlagert, nimmt ihn damit aus der Rechnung. Dieses Projekt packt das in einen Portainer-Stack: Repository eintragen, fünf Variablen füllen, deployen.
Die Rechnung geht allerdings nur bei langen Jobs auf. Einen Typecheck von zwei Minuten zu verlagern spart nichts und kostet eine Maschine, die gepflegt werden muss.
Wozu
Ein Beispiel aus der Praxis: ein Repository, dessen Push 60 abgerechnete Minuten kostete — 52 davon ein einzelner Browser-Test-Job. Diesen einen Job zu verlagern hat 87 % der Rechnung entfernt. Die vier schnellen Jobs blieben, wo sie waren, damit ein Push auch dann ein Urteil bekommt, wenn der eigene Server aus ist.
Die zwei Regeln, die der Stack festhält
Beide existieren, weil ihr Fehlen Störungen erzeugt, die ihre Ursache nicht benennen.
Identische Pfade. Der Runner nutzt den Docker-Daemon des Hosts, also löst der jeden übergebenen Pfad host-seitig auf. Liegt das Arbeitsverzeichnis nicht unter seinem eigenen Host-Pfad, legt Docker stillschweigend leere Ordner an und mountet die. Es erscheint keine Fehlermeldung — nur ein Container, der startet und nichts ausliefert, oder ein Build, der Dateien nicht findet, die offensichtlich da sind.
Ein gefiltertes Pruning. Ein dauerhafter Runner sammelt pro Branch-Build ein
Image und jeden Container, den ein Job nicht abbaut. Nichts auf einem Docker-Host
räumt das weg. Der Filter ist dabei so wichtig wie das Pruning selbst: ein
ungefiltertes docker image prune -af löscht genau die Images, die der nächste
Job braucht, und hängt deren Download an jeden Lauf.
Highlights
- Ein Stack, zwei Dienste — Runner und Pruning, ein Variablenblock
- Arbeitsverzeichnis, durch das Bind-Mounts tatsächlich hindurchgreifen
- Stündliches Pruning von Images, Build-Cache und gestoppten Containern, gefiltert auf älter als 24 Stunden
- Volumes bleiben unangetastet, sofern nicht ausdrücklich erlaubt — auf einem geteilten Host zerstört ein Volume-Pruning die Daten anderer Stacks
cleanup.shzum Zurücksetzen eines Hosts: berichtet zuerst und ändert nichts, bis ein Bereich benannt wird- Opt-out am Auto-Updater — ein Watchtower, der den Runner mitten im Job neu startet, verliert den Job und meldet ihn als abgebrochen
- Dokumentiert, was es nicht kann: Zustand in einem wiederverwendeten Volume, etwa eine geseedete Testdatenbank, sieht kein Pruning — das gehört in den Workflow, vor und nach dem Job
Das Risiko, ehrlich benannt
Der Runner bindet den Docker-Socket des Hosts ein, und das ist Root auf diesem Host. Jeder Workflow, den er ausführt, kann einen privilegierten Container starten und alles auf der Maschine lesen und schreiben. Es gibt keine Sandbox, und es ist auch keine beabsichtigt: der Aufbau setzt voraus, dass man jedem Workflow im Zielrepository vertraut.
Für ein privates Repository mit vertrauenswürdigen Autoren auf einer Maschine ohne Wert ist das ein vertretbarer Handel. Für ein öffentliches Repository oder eines, das Pull Requests aus Forks annimmt, ist es keiner — ein fremder Pull Request liefe dann als Root. Wer Isolation statt Vertrauen braucht, wird im README auf actions-runner-controller verwiesen.
Status
In aktiver Entwicklung — in Produktion noch nicht bewiesen. Das Tool ist in Benutzung und wird davon geformt, aber es ist noch kein Job vollständig darauf gelaufen: der erste echte Deploy lief im Kreis und hat drei Konfigurationsfehler im Repository zutage gebracht, die inzwischen behoben sind.
Ausdrücklich unbewiesen, das Wahrscheinlichste zuerst: Actions, die Werkzeuge installieren (sie sind für GitHubs Images geschrieben und scheitern im Container meist ohne Begründung), ein kompletter Durchlauf, und ob ein 24-Stunden-Fenster fürs Pruning über Wochen der richtige Handel ist.
Wer sich keine Überraschung leisten kann, sollte einen Commit festnageln statt
main zu folgen. Issues und PRs sind willkommen —
github.com/NoiXdev/portainer-github-runner.