Forgejo Pipelines · Workshop Lektion 7 / services

services – Postgres im Integrationstest

Eine echte Datenbank neben dem Job – für Tests, die mehr prüfen als Funktionen.

Warum das zählt
Unsere Tasks-API spricht mit einer Datenbank. Unit-Tests reichen da nicht – wir wollen gegen ein echtes Postgres testen. services: startet dafür einen Begleit-Container, der nur für die Dauer des Jobs lebt. Das ist der Standardweg für Integrationstests in CI.

Ein Service ist ein Sidecar-Container

.forgejo/workflows/ci.yaml (Auszug)
  test:
    runs-on: docker
    container: python:3.12-slim
    services:
      db:                       # Name = Hostname im Netzwerk
        image: postgres:16
        env:
          POSTGRES_PASSWORD: secret
          POSTGRES_DB: tasks
    env:
      DATABASE_URL: postgres://postgres:secret@db:5432/tasks
    steps:
      - uses: actions/checkout@v4
      - run: pip install -r requirements.txt
      - run: pytest -q tests/integration
Der Service-Name ist der Hostname Im Beispiel heisst der Service db – darum lautet der Host in DATABASE_URL ebenfalls db, nicht localhost. Job-Container und Service-Container teilen sich ein Docker-Netzwerk; sie erreichen sich über den Service-Namen. Das ist ein häufiger Stolperstein für alle, die localhost erwarten.

Auf „bereit" warten

Ein gestarteter Postgres-Container ist nicht sofort verbindungsbereit. Robuste Pipelines warten per Healthcheck oder kurzer Retry-Schleife, bevor die Tests loslegen:

      - run: |
          until pg_isready -h db -U postgres; do sleep 1; done
„Connection refused" am Anfang Schlagen Tests sporadisch direkt nach dem Start fehl, fehlt fast immer das Warten auf die DB. Healthcheck/Retry löst das zuverlässig.

Parallelen: GitLab CI ↔ Forgejo Actions

GitLab CI

services: als Liste von Images

Host = Image-/Alias-Name

Job läuft im image:

Forgejo Actions

services: als benannte Map

Host = Service-Name (Schlüssel)

Job läuft im container:

Übungen für Teilnehmende

Übung 1 · Fehler finden

Frage: Die DATABASE_URL zeigt auf postgres://…@localhost:5432/…, der Service heisst db. Warum scheitert die Verbindung?

Lösung anzeigen

Der Host muss db sein (der Service-Name), nicht localhost. Service- und Job-Container sind getrennte Hosts im selben Netzwerk.

Übung 2 · Selbst schreiben

Aufgabe: Füge dem Test-Job einen zweiten Service cache mit redis:7 hinzu und gib eine passende REDIS_URL an.

Lösung anzeigen
    services:
      db:    { image: postgres:16, env: { POSTGRES_PASSWORD: secret } }
      cache: { image: redis:7 }
    env:
      REDIS_URL: redis://cache:6379/0

Host = cache (der Service-Name). Mehrere Services laufen parallel.

Kurz prüfen (aus dem Kopf)

Unter welchem Host erreichst du den Service db?

Wie lange lebt ein Service-Container?

Primärquelle zum Lesen Forgejo Docs – Reference, Abschnitt services (image, env, options, Healthcheck).
Ich bin dein Teacher. Verbindungsfehler oder Healthcheck unklar? Schick mir deine services-Definition – wir bringen die DB-Verbindung zum Laufen.
← Lektion 6 · Actions verwenden Lektion 8 · Container Registry →