Feldnotizen


3 Min. Lesezeit

Selbst gehostete GitHub-Runner in Kubernetes

Die von GitHub gehosteten Runner sind bequem, aber sie werden pro Minute abgerechnet, erreichen nichts in deinem Netzwerk und starten jedes Mal auf einer leeren Maschine. Wenn du schon einen Kubernetes-Cluster betreibst, kannst du die Jobs dort laufen lassen. Actions Runner Controller (ARC) startet für jeden Job einen frischen Runner-Pod und entfernt ihn danach wieder.

Diese Notiz verwendet das aktuelle Setup, das GitHub selbst pflegt: die Charts gha-runner-scale-set. Ältere Anleitungen nutzen den Community-Controller summerwind mit RunnerDeployment-Ressourcen. Misch die beiden nicht.

Was du brauchst

  • Einen Kubernetes-Cluster und Zugriff darauf mit kubectl. Ein Cluster auf Bare Metal funktioniert gut.
  • Helm 3.
  • Owner-Rechte in der GitHub-Organisation, die die Runner bekommen soll.

1. Eine GitHub App erstellen

ARC braucht Zugangsdaten, um Runner zu registrieren. Eine GitHub App ist besser als ein Personal Access Token: Sie hängt an keiner Person, und ihre Berechtigungen sind eng.

In den Einstellungen der Organisation unter Developer settings → GitHub Apps → New GitHub App:

  • Gib ihr einen Namen und eine beliebige Homepage-URL. Schalte den Webhook aus.
  • Setz unter Organization permissions den Punkt Self-hosted runners auf Read and write.
  • Erstelle die App und erzeuge dann einen Private Key. Eine .pem-Datei wird heruntergeladen.
  • Installier die App in deiner Organisation.

Notier dir drei Werte:

  • die App ID auf der Einstellungsseite der App,
  • die Installation ID, die Zahl am Ende der URL, wenn du die installierte App in den GitHub Apps-Einstellungen der Organisation öffnest,
  • den Pfad zur Datei mit dem Private Key.

Die Schlüsseldatei gehört in kein Repository.

2. Den Controller installieren

Der Controller bekommt einen eigenen Namespace:

helm install arc \
  --namespace arc-systems \
  --create-namespace \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

Ohne --version installiert Helm das neueste Chart. Fixier eine Version, sobald es läuft, damit ein Upgrade dann passiert, wenn du es entscheidest.

3. Die Zugangsdaten der App ablegen

Die Runner bekommen einen zweiten Namespace. Das Secret muss im selben Namespace liegen wie das Runner Scale Set:

kubectl create namespace arc-runners

kubectl create secret generic arc-github-app \
  --namespace arc-runners \
  --from-literal=github_app_id=<APP_ID> \
  --from-literal=github_app_installation_id=<INSTALLATION_ID> \
  --from-file=github_app_private_key=<PFAD_ZUM_PRIVATE_KEY.pem>

Die Schlüsselnamen (github_app_id, github_app_installation_id, github_app_private_key) sind fest. ARC sucht genau nach diesen.

4. Ein Runner Scale Set installieren

Ein Runner Scale Set ist ein Pool von Runnern für eine Organisation. Sein Installationsname wird zum Label, das du in Workflows verwendest:

helm install arc-runner-set \
  --namespace arc-runners \
  --set githubConfigUrl="https://github.com/<ORGANISATION>" \
  --set githubConfigSecret=arc-github-app \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

5. Prüfen, ob es läuft

helm list -A
kubectl get pods -n arc-systems

Der Controller und ein Listener-Pod sollten Running sein. Das Scale Set erscheint ausserdem in den Einstellungen der Organisation unter Actions → Runners.

Runner-Pods gibt es noch keine. Sie starten, wenn ein Job ankommt.

6. In einem Workflow verwenden

Setz runs-on auf den Installationsnamen:

name: Runner test
on:
  workflow_dispatch:

jobs:
  hello:
    runs-on: arc-runner-set
    steps:
      - run: echo "Running on $(hostname)"

Starte den Workflow im Tab Actions und beobachte die Pods:

kubectl get pods -n arc-runners -w

Ein Runner-Pod erscheint, führt den Job aus und verschwindet wieder.

Gut zu wissen

  • Jeder Job bekommt einen frischen Pod. Zwischen den Jobs bleibt nichts erhalten. Das ist gut für die Sicherheit und schlecht für Caches. Nutze actions/cache oder einen Registry-Cache für Build-Layer.
  • Das Standard-Runner-Image hat keinen Docker-Daemon. Jobs, die Images bauen, brauchen beim Scale Set containerMode auf dind (Docker in Docker) oder kubernetes. Lies die values.yaml des Charts, bevor du wählst.
  • Selbst gehostete Runner und öffentliche Repositories passen nicht zusammen. Ein Pull Request aus einem Fork könnte Code auf deinem Cluster ausführen. Nutze sie nur für private Repositories.

Mehr in Feldnotizen

Teilen