Zum Hauptinhalt springen

Architektur

Eine Semaphore-Installation besteht aus drei zwingenden Teilen: einem Serverprozess, einer Datenbank und einem Ort, an dem Tasks ausgeführt werden. Alles andere — Runner, Redis, ein Reverse Proxy, ein Identity Provider — ist optional und kommt hinzu, wenn ein konkreter Bedarf entsteht.

Die Bestandteile

Server

Eine einzelne Go-Binärdatei. Sie enthält die kompilierte Weboberfläche, sodass ein einziger Prozess die UI, die REST-API und einen WebSocket-Endpunkt unter /api/ws bereitstellt, der Task-Ausgaben an geöffnete Browser streamt. Standardmäßig lauscht sie auf Port 3000.

In diesem Prozess laufen mehrere Dinge gleichzeitig ab:

BestandteilAufgabe
HTTP-API und UIAlles, was Browser und API-Clients aufrufen.
Task-PoolDie Warteschlange der Tasks, ihre Parallelitätsgrenzen und ihr Zustand.
SchedulerStartet Templates gemäß ihren Cron-Zeitplänen.
Lokaler ExecutorFührt Tasks auf dem Server selbst aus, wenn kein Remote-Runner sie übernimmt.
NotifierVersendet Benachrichtigungen, wenn Tasks enden.

Datenbank

SQLite, MySQL oder PostgreSQL, ausgewählt über die Option dialect. Sie enthält Projekte, Templates, Inventories, Zeitpläne, Benutzer, Rollen, den Task-Verlauf und die verschlüsselten Inhalte des Key Store. Sie ist das Einzige, was gesichert werden muss: alles andere lässt sich neu aufbauen.

SQLite ist die Voreinstellung und eignet sich für einen einzelnen Server. Verwenden Sie PostgreSQL oder MySQL, sobald mehrere Personen auf den Dienst angewiesen sind, und immer dann, wenn Sie mehr als einen Knoten betreiben.

Dateicache

Das Verzeichnis unter tmp_path (standardmäßig /tmp/semaphore) enthält geklonte Repositories und das Arbeitsverzeichnis jedes Laufs. Es ist ein Cache, kein Speicher: Löschen kostet einen zusätzlichen Klon pro Projekt. Clear cache in den Projekteinstellungen macht genau das.

Der Cache liegt jeweils auf der Maschine, die einen Task ausführt — beim Server, wenn Tasks lokal laufen, und bei jedem Runner, wenn nicht.

Wo Tasks ausgeführt werden

Standardmäßig führt der Server Tasks selbst aus, in seinem eigenen Dateisystem und mit seinem eigenen Netzwerkzugriff. Das ist die einfachste Konfiguration und die richtige für ein kleines Team, das Hosts verwaltet, die der Server ohnehin erreicht.

Runner trennen beides. Ein Runner ist dieselbe Binärdatei, gestartet mit semaphore runner start. Er hält keine Datenbankverbindung und öffnet keinen eingehenden Port: Er fragt den Server über HTTPS mit einem Bearer-Token ab, erhält einen Job, klont das Repository, führt das Werkzeug aus und streamt die Ausgabe zurück. Mit Runnern können Sie

  • die Ausführung in ein Netzwerk verlegen, das der Server nicht erreicht,
  • Zugangsdaten für die Produktion auf einer Maschine halten, die keine Weboberfläche bereitstellt,
  • Last über mehrere Maschinen verteilen und
  • (in Pro) einen Task per Tags an einen bestimmten Runner leiten.

Jeder Runner legt über executor.type fest, wie er einen Job startet:

ExecutorWo der Job läuft
localAls Prozess auf dem Host des Runners, in tmp_path.
dockerIn einem Container, den der Runner für diesen Job startet und danach entfernt.
k8sIn einem Pod, den der Runner in Ihrem Cluster erzeugt und danach entfernt.

Ports und Richtungen

Jede Verbindung geht ausgehend von der Komponente aus, die sie aufbaut — genau das macht Runner über Netzwerkgrenzen hinweg nutzbar.

VonNachZweck
Browser, API-ClientServer :3000UI, REST-API, WebSocket.
ServerDatenbankSämtlicher persistenter Zustand.
Server, RunnerGit-RemotesKlonen von Repositories.
Server, RunnerVerwaltete Hosts, Cloud-APIsDie eigentliche Automatisierung.
RunnerServer :3000Abfrage von Jobs, Streaming der Ausgabe.
ServerLDAP, OIDC, SMTP, Chat-WebhooksAnmeldung und Benachrichtigungen.

Skalierung

Zwei Achsen skalieren unabhängig voneinander.

Mehr Ausführung bedeutet mehr Runner. Der Server bleibt ein einzelner Prozess, und Tasks werden auf die verbundenen Runner verteilt.

Mehr Verfügbarkeit bedeutet mehr Server. Mehrere Knoten arbeiten gegen eine PostgreSQL- oder MySQL-Datenbank mit Redis für verteilte Sperren, gemeinsamen Warteschlangenzustand und Pub/Sub, hinter einem Load Balancer, der WebSocket unterstützt. Das ist Hochverfügbarkeit, eine Enterprise-Funktion. SQLite kann dafür nicht verwendet werden.

Wie es weitergeht