# Semaphore CI Stack - Lokale Entwicklung

## Übersicht

Selbst-gehostete Semaphore CI/CD-Plattform für lokale Entwicklung, die es ermöglicht, CI/CD-Pipelines und Ansible-Playbooks lokal zu testen und auszuführen, ohne Abhängigkeit von externen CI-Services.

**Features**:
- **Selbst-gehostet**: Läuft vollständig lokal auf dem Entwicklungsrechner
- **Lokal isoliert**: NUR über localhost (127.0.0.1) erreichbar - KEIN externer Zugriff aus Sicherheitsgründen!
- **MySQL-Backend**: Persistente Datenbank für Projekte, Tasks und Templates
- **Web-UI**: Intuitive Benutzeroberfläche für Pipeline-Management (nur lokal)
- **Ansible-Integration**: Native Unterstützung für Ansible-Playbooks
- **Docker-basiert**: Einfaches Setup und Wartung

**Einsatzzweck**:
- Lokales Testen von CI/CD-Pipelines
- Entwicklung und Test von Ansible-Playbooks
- Experimentieren mit Deployment-Workflows
- Keine Abhängigkeit von externen CI-Services

## Services

- **mysql** - MySQL 8.0 Datenbank für Semaphore-Daten
- **semaphore** - Semaphore CI/CD Web-UI und API

## Voraussetzungen

- Docker und Docker Compose installiert
- Port 9300 auf localhost frei verfügbar (3000 wird von Gitea verwendet)
- Ausreichend Speicherplatz für Docker Volumes (~500MB initial)

## Verzeichnisstruktur

```
semaphore/
├── docker-compose.yml          # Service-Definitionen
├── env.example                 # Environment-Variablen Template
├── .env                        # Environment-Konfiguration (aus env.example erstellen)
└── README.md                   # Diese Datei
```

## Quick Start

### 1. Environment-Datei erstellen

```bash
cd deployment/stacks/semaphore
cp env.example .env
```

### 2. Konfiguration anpassen (Optional)

Bearbeite `.env` und passe die Werte an:

```bash
nano .env
```

**Wichtig**: Generiere einen sicheren Encryption Key:
```bash
# Linux/WSL
head -c32 /dev/urandom | base64

# Windows PowerShell
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | % {[char]$_}) | ConvertTo-Base64
```

Aktualisiere `SEMAPHORE_ACCESS_KEY_ENCRYPTION` in der `.env`-Datei.

### 3. Stack starten

```bash
docker compose up -d
```

### 4. Semaphore Web-UI öffnen

Öffne im Browser: **http://localhost:9300**

⚠️ **WICHTIG**: Semaphore ist NUR lokal zugänglich (127.0.0.1). Es gibt KEINEN externen Zugriff aus Sicherheitsgründen.

**Standard-Login**:
- **Username**: `admin` (oder Wert aus `SEMAPHORE_ADMIN`)
- **Password**: `admin` (oder Wert aus `SEMAPHORE_ADMIN_PASSWORD`)

### 5. Erste Schritte in Semaphore

1. **Projekt erstellen**: Klicke auf "New Project" und erstelle ein neues Projekt
2. **Inventory anlegen**: Erstelle ein Inventory mit lokalen Hosts oder Docker-Containern
3. **Template erstellen**: Erstelle ein Template mit einem Ansible-Playbook
4. **Task ausführen**: Starte einen Task und beobachte die Ausführung

## Konfiguration

### Environment-Variablen (.env)

#### MySQL-Datenbank

```env
MYSQL_ROOT_PASSWORD=semaphore_root
MYSQL_DATABASE=semaphore
MYSQL_USER=semaphore
MYSQL_PASSWORD=semaphore
```

#### Semaphore-Konfiguration

```env
# Port-Binding (Standard: 9300)
SEMAPHORE_PORT=9300

# Admin-Benutzer
SEMAPHORE_ADMIN=admin
SEMAPHORE_ADMIN_NAME=Administrator
SEMAPHORE_ADMIN_EMAIL=admin@localhost
SEMAPHORE_ADMIN_PASSWORD=admin

# Playbook-Pfad (im Container)
SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore

# Encryption Key (WICHTIG: Für Produktion ändern!)
SEMAPHORE_ACCESS_KEY_ENCRYPTION=change-me-in-production
```

#### Optionale Konfiguration

**LDAP-Integration** (Standard: deaktiviert):
```env
SEMAPHORE_LDAP_ENABLED=true
SEMAPHORE_LDAP_HOST=ldap.example.com
SEMAPHORE_LDAP_PORT=389
SEMAPHORE_LDAP_DN=cn=admin,dc=example,dc=com
SEMAPHORE_LDAP_PASSWORD=ldap_password
SEMAPHORE_LDAP_BASE_DN=dc=example,dc=com
SEMAPHORE_LDAP_USER_FILTER=(uid=%s)
```

**Webhook-Integration**:
```env
SEMAPHORE_WEBHOOK_URL=http://localhost:8080/webhook
```

## Verwendung

### Stack starten

```bash
# Services im Hintergrund starten
docker compose up -d

# Logs anzeigen
docker compose logs -f

# Nur Semaphore-Logs
docker compose logs -f semaphore

# Nur MySQL-Logs
docker compose logs -f mysql
```

### Stack stoppen

```bash
docker compose down
```

### Stack neu starten

```bash
docker compose restart
```

### Status prüfen

```bash
# Container-Status anzeigen
docker compose ps

# Health-Check-Status
docker compose ps --format "table {{.Name}}\t{{.Status}}"

# Semaphore-Health-Check manuell
docker compose exec semaphore wget --no-verbose --spider http://localhost:3000/api/health
```

### Datenbank-Backup

```bash
# MySQL-Dump erstellen
docker compose exec mysql mysqldump -u semaphore -psemaphore semaphore > semaphore-backup-$(date +%Y%m%d).sql

# Backup wiederherstellen
docker compose exec -T mysql mysql -u semaphore -psemaphore semaphore < semaphore-backup-YYYYMMDD.sql
```

### Daten löschen und neu starten

```bash
# ⚠️ WARNUNG: Löscht alle Daten!
docker compose down -v
docker compose up -d
```

## Erste Schritte mit Semaphore

### 1. Projekt erstellen

1. Öffne http://localhost:9300 im Browser
2. Melde dich mit Admin-Credentials an
3. Klicke auf "New Project"
4. Gib einen Projektnamen ein (z.B. "My Project")
5. Klicke auf "Create"

### 2. Inventory anlegen

Ein Inventory definiert die Hosts, auf denen Playbooks ausgeführt werden sollen.

**Option A: Lokaler Host**
1. Gehe zu Projekt → Inventories → New Inventory
2. Name: "Local Hosts"
3. Hinzufügen von Host:
   - Name: `localhost`
   - Address: `127.0.0.1`
   - SSH Username: `your-username`
   - SSH Key: Füge deinen privaten SSH-Key hinzu

**Option B: Docker-Container**
1. Erstelle ein Inventory mit Docker-Hosts
2. Für Docker-in-Docker Support benötigst du zusätzliche Konfiguration

### 3. Template erstellen

Templates definieren welche Playbooks ausgeführt werden sollen.

1. Gehe zu Projekt → Templates → New Template
2. Template-Name: "Hello World"
3. Inventory: Wähle dein Inventory
4. Playbook: Erstelle ein einfaches Playbook:

```yaml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: Print hello world
      debug:
        msg: "Hello from Semaphore CI!"
```

5. Speichere das Template

### 4. Task ausführen

1. Gehe zu Templates
2. Klicke auf dein Template
3. Klicke auf "Run"
4. Beobachte die Ausführung in Echtzeit

## Integration mit bestehenden Stacks

### Verwendung mit lokaler Docker-Registry

Semaphore kann Docker-Images aus der lokalen Registry verwenden:

**In Ansible-Playbooks**:
```yaml
- name: Pull image from local registry
  docker_image:
    name: registry.michaelschiemer.de/framework:latest
    source: pull
  register: image_result
```

**Voraussetzung**: Der Semaphore-Container muss Zugriff auf den Docker-Socket oder die Registry haben.

### Verwendung mit bestehenden Ansible-Playbooks

1. Mounte deine Playbooks als Volume:
   ```yaml
   volumes:
     - /path/to/your/playbooks:/tmp/semaphore/playbooks:ro
   ```

2. Oder kopiere Playbooks in den Container:
   ```bash
   docker compose exec semaphore mkdir -p /tmp/semaphore/my-playbook
   docker cp my-playbook.yml semaphore:/tmp/semaphore/my-playbook/playbook.yml
   ```

3. Verweise im Template auf den Playbook-Pfad

## Troubleshooting

### Port-Konflikt (Port 3000 vs 9300)

**Problem**: Port 3000 ist standardmäßig von Gitea belegt, daher verwendet Semaphore Port 9300.

**Lösung**: Wenn du einen anderen Port verwenden möchtest, setze `SEMAPHORE_PORT` in der `.env` Datei:
```env
SEMAPHORE_PORT=8080  # Oder ein anderer freier Port
```

**Wichtig**: Der interne Container-Port bleibt immer 3000 - nur der externe Host-Port ändert sich.

### Semaphore startet nicht

**Prüfe Logs**:
```bash
docker compose logs semaphore
```

**Häufige Ursachen**:
- MySQL ist noch nicht bereit (warte auf Health-Check)
- Port 9300 ist bereits belegt: `netstat -tuln | grep 9300` (oder auf Windows: `netstat -ano | findstr :9300`)
- Falsche Datenbank-Credentials

**Lösung**:
```bash
# Prüfe MySQL-Status
docker compose ps mysql

# Prüfe Semaphore-Logs für DB-Verbindungsfehler
docker compose logs semaphore | grep -i database

# Restart wenn nötig
docker compose restart semaphore
```

### MySQL startet nicht

**Prüfe MySQL-Logs**:
```bash
docker compose logs mysql
```

**Häufige Ursachen**:
- Volume-Permissions-Probleme
- Port-Konflikte (unwahrscheinlich, da kein Port-Mapping)

**Lösung**:
```bash
# Prüfe Volume
docker volume inspect semaphore-mysql-data

# Cleanup und Neu-Start (⚠️ Datenverlust!)
docker compose down -v
docker compose up -d
```

### Login funktioniert nicht

**Standard-Credentials**:
- Username: `admin` (oder `SEMAPHORE_ADMIN` Wert)
- Password: `admin` (oder `SEMAPHORE_ADMIN_PASSWORD` Wert)

**Admin-Passwort zurücksetzen**:
1. Stoppe Semaphore: `docker compose stop semaphore`
2. Setze `SEMAPHORE_ADMIN_PASSWORD` in `.env` auf neues Passwort
3. Starte Semaphore: `docker compose up -d`

### Playbooks werden nicht gefunden

**Prüfe Playbook-Pfad**:
```bash
docker compose exec semaphore ls -la /tmp/semaphore
```

**Lösung**:
- Stelle sicher, dass `SEMAPHORE_PLAYBOOK_PATH` korrekt gesetzt ist
- Prüfe, ob Playbooks im richtigen Pfad liegen
- Stelle sicher, dass Datei-Berechtigungen korrekt sind

### Health-Check schlägt fehl

**Prüfe Health-Check**:
```bash
docker compose exec semaphore wget --no-verbose --spider http://localhost:3000/api/health
```

**Lösung**:
- Warte auf vollständigen Start (kann 1-2 Minuten dauern)
- Prüfe Logs: `docker compose logs semaphore`
- Restart wenn nötig: `docker compose restart semaphore`

## Sicherheit

### 🔒 Lokaler Zugriff (Nur localhost)

Semaphore ist absichtlich NUR lokal zugänglich aus Sicherheitsgründen:

- ✅ **Nur localhost-Zugriff**: Port 9300 gebunden an 127.0.0.1 (nicht 0.0.0.0)
- ✅ **Keine externen Netzwerke**: Kein externer Zugriff möglich
- ✅ **Keine Traefik-Integration**: Keine öffentliche Route konfiguriert
- ✅ **Isoliertes Netzwerk**: Nur internes Docker-Netzwerk
- ⚠️ **Standard-Passwörter**: Nur für lokale Entwicklung - ändern bei Bedarf

### ⚠️ KEINE Produktions-Nutzung über Internet!

**Semaphore sollte NICHT öffentlich zugänglich gemacht werden!**

Gründe:
1. **Sicherheitsrisiko**: Semaphore hat Zugriff auf SSH-Keys und Deployment-Credentials
2. **Privilegierter Zugriff**: Kann auf Produktions-Server zugreifen
3. **Keine Multi-Factor-Authentication**: Standardmäßig keine 2FA
4. **Exploits**: Selbst-gehostete Software kann Sicherheitslücken haben

### Für lokale Entwicklung (Empfohlen)

Wenn du Semaphore lokal nutzen willst:

1. **Starke Passwörter**: Ändere alle Passwörter in `.env`:
   ```env
   SEMAPHORE_ADMIN_PASSWORD=<starkes-passwort>
   MYSQL_PASSWORD=<starkes-passwort>
   MYSQL_ROOT_PASSWORD=<starkes-passwort>
   ```
2. **Encryption Key**: Generiere einen sicheren Key:
   ```bash
   head -c32 /dev/urandom | base64
   ```
   Setze in `.env`: `SEMAPHORE_ACCESS_KEY_ENCRYPTION=<generierter-key>`
3. **SSH-Keys**: Stelle sicher, dass SSH-Keys sicher gespeichert sind
4. **Backup-Strategie**: Regelmäßige MySQL-Backups einrichten
5. **Resource Limits**: Füge Memory/CPU-Limits hinzu (optional)

## Wartung

### Regelmäßige Aufgaben

**Wöchentlich**:
- Logs auf Fehler prüfen: `docker compose logs --tail=100`
- Disk-Space prüfen: `docker system df`
- Backup erstellen (wenn wichtige Daten vorhanden)

**Monatlich**:
- Images aktualisieren: `docker compose pull && docker compose up -d`
- Alte Tasks in Semaphore aufräumen (über Web-UI)

### Updates

```bash
# Aktuelle Images herunterladen
docker compose pull

# Mit neuen Images neu starten
docker compose up -d

# Logs prüfen
docker compose logs -f
```

### Daten bereinigen

```bash
# Alte Docker-Images löschen
docker image prune -a

# Alte Volumes prüfen
docker volume ls

# ⚠️ Vorsicht: Löscht alle Semaphore-Daten!
docker compose down -v
```

## Backup und Wiederherstellung

### Backup erstellen

```bash
# MySQL-Dump
docker compose exec mysql mysqldump \
  -u semaphore -psemaphore semaphore \
  > semaphore-backup-$(date +%Y%m%d-%H%M%S).sql

# Volume-Backup (komplett)
docker run --rm \
  -v semaphore-mysql-data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/semaphore-mysql-backup-$(date +%Y%m%d).tar.gz /data
```

### Wiederherstellung

```bash
# MySQL-Dump wiederherstellen
docker compose exec -T mysql mysql \
  -u semaphore -psemaphore semaphore \
  < semaphore-backup-YYYYMMDD.sql

# Volume wiederherstellen (⚠️ stoppt Container)
docker compose down
docker run --rm \
  -v semaphore-mysql-data:/data \
  -v $(pwd):/backup \
  alpine sh -c "cd /data && tar xzf /backup/semaphore-mysql-backup-YYYYMMDD.tar.gz"
docker compose up -d
```

## Performance-Optimierung

### MySQL-Optimierung

Für bessere Performance kannst du MySQL-Konfiguration anpassen:

1. Erstelle `mysql/conf.d/my.cnf`:
```ini
[mysqld]
innodb_buffer_pool_size = 256M
max_connections = 100
```

2. Mounte in `docker-compose.yml`:
```yaml
volumes:
  - ./mysql/conf.d:/etc/mysql/conf.d:ro
```

### Resource Limits

Füge Limits in `docker-compose.yml` hinzu:

```yaml
deploy:
  resources:
    limits:
      memory: 1G
      cpus: '0.5'
```

## Unterstützung

### Dokumentation

- **Semaphore CI Docs**: https://docs.semaphoreui.com/
- **Semaphore GitHub**: https://github.com/semaphoreui/semaphore

### Logs

```bash
# Alle Logs
docker compose logs -f

# Semaphore-Logs
docker compose logs -f semaphore

# MySQL-Logs
docker compose logs -f mysql

# Letzte 100 Zeilen
docker compose logs --tail=100
```

### Health-Checks

```bash
# Container-Status
docker compose ps

# Semaphore-Health
curl http://localhost:9300/api/health

# MySQL-Health
docker compose exec mysql mysqladmin ping -h localhost -u root -psemaphore_root
```

---

**Setup-Status**: ✅ Bereit für lokale Entwicklung

**Nächste Schritte**:
1. `cp env.example .env` ausführen
2. `docker compose up -d` starten
3. http://localhost:9300 öffnen
4. Mit Admin-Credentials anmelden
5. Erstes Projekt und Template erstellen

