Files
michaelschiemer/deployment/ansible
Michael Schiemer c3bec296fc
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Has been cancelled
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been cancelled
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been cancelled
🚀 Build & Deploy Image / Build Docker Image (push) Has been cancelled
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been cancelled
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been cancelled
Security Vulnerability Scan / Check for Dependency Changes (push) Successful in 33s
System Maintenance / Run Ansible System Maintenance (push) Failing after 15s
Security Vulnerability Scan / Composer Security Audit (push) Failing after 2m18s
fix: replace insecure Traefik API calls with CLI commands
- Replace http://localhost:8080/api/http/services with traefik show providers docker
- Replace http://localhost:8080/api/http/routers with traefik show providers docker
- Update debug messages to reference CLI command instead of HTTP API
- Fixes false 'NOT_FOUND' errors since api.insecure: false is set in traefik.yml

The Traefik CLI (traefik show providers docker) works without credentials
and is the recommended method for Traefik v3. It shows all Docker providers
including services, routers, and middlewares, so Gitea will be visible if
registered correctly.
2025-11-09 02:19:27 +01:00
..
2025-11-01 19:02:09 +01:00

Ansible Deployment Configuration

This directory contains Ansible playbooks and configuration for deploying the Custom PHP Framework to production.

Directory Structure

deployment/ansible/
├── ansible.cfg                   # Ansible configuration
├── inventory/
│   ├── production.yml         # Production server inventory
│   └── local.yml              # Local testing inventory
├── playbooks/
│   ├── deploy-update.yml            # Deploy application updates
│   ├── system-maintenance.yml       # Betriebssystem-Updates & Wartung
│   ├── rollback.yml                 # Rollback deployments
│   ├── setup-infrastructure.yml     # Provision core stacks
│   ├── setup-production-secrets.yml # Deploy secrets
│   ├── setup-wireguard.yml          # Setup WireGuard VPN server
│   ├── add-wireguard-client.yml     # Add WireGuard client
│   ├── sync-code.yml                # Git-based code sync
│   └── README-WIREGUARD.md          # WireGuard documentation
├── scripts/                     # Helper scripts for secrets & credentials
├── roles/                       # Reusable roles (e.g. application stack)
├── secrets/
│   ├── .gitignore                   # Prevent committing secrets
│   └── production.vault.yml.example # Example vault file
└── templates/
    ├── application.env.j2          # Application stack environment
    ├── gitea-app.ini.j2            # Gitea configuration
    ├── minio.env.j2                # MinIO environment
    ├── monitoring.env.j2           # Monitoring stack environment
    ├── wireguard-client.conf.j2    # WireGuard client config
    └── wireguard-server.conf.j2    # WireGuard server config

## Roles

Stack-spezifische Aufgaben liegen in `roles/` (z.B. `application`, `traefik`, `registry`). Playbooks wie `setup-infrastructure.yml` importieren diese Rollen direkt. Die Application-Rolle kann mit Variablen wie `application_sync_files=false` oder `application_compose_recreate="always"` konfiguriert werden (siehe `playbooks/deploy-update.yml` als Beispiel). Die neue `system`-Rolle hält Betriebssystem-Pakete aktuell und konfiguriert optionale Unattended-Upgrades bevor Docker-Stacks neu gestartet werden.

## Prerequisites

1. **Ansible Installed**:
   ```bash
   pip install ansible
  1. SSH Access:

    • SSH key configured at ~/.ssh/production
    • Key added to production server's authorized_keys for deploy user
  2. Ansible Vault Password:

    • Create .vault_pass file in secrets/ directory
    • Add vault password to this file (one line)
    • File is gitignored for security
    • 📖 Detaillierte Dokumentation: docs/guides/vault-password.md

Setup Instructions

1. Create Production Secrets

cd deployment/ansible/secrets

# Copy example file
cp production.vault.yml.example production.vault.yml

# Edit with your actual secrets
nano production.vault.yml

# Encrypt the file
ansible-vault encrypt production.vault.yml
# Enter vault password when prompted

2. Store Vault Password

# Create vault password file
echo "your-vault-password-here" > secrets/.vault_pass

# Secure the file
chmod 600 secrets/.vault_pass

📖 Für detaillierte Informationen: Siehe docs/guides/vault-password.md

3. Configure SSH Key

# Generate SSH key if needed
ssh-keygen -t ed25519 -f ~/.ssh/production -C "ansible-deploy"

# Copy public key to production server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151

Running Playbooks

Deploy Production Secrets

First-time setup - Deploy secrets to production server:

ansible-playbook playbooks/setup-production-secrets.yml \
  --vault-password-file secrets/.vault_pass

Deploy Application Update

Automated via Gitea Actions - Or run manually:

ansible-playbook playbooks/deploy-update.yml \
  -e "image_tag=sha-abc123" \
  -e "git_commit_sha=abc123" \
  -e "deployment_timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -e "docker_registry_username=gitea-user" \
  -e "docker_registry_password=your-registry-password"

Rollback Deployment

Rollback to previous version:

ansible-playbook playbooks/rollback.yml

Rollback to specific version:

ansible-playbook playbooks/rollback.yml \
  -e "rollback_to_version=2025-01-28T15-30-00"

Setup WireGuard VPN

First-time setup - Install WireGuard VPN server:

ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml

Add a client:

ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
  -e "client_name=myclient"

Siehe playbooks/README-WIREGUARD.md für detaillierte Anleitung.

System Maintenance ausführen

Führt die system-Rolle aus, aktualisiert Paketquellen, führt OS-Upgrades durch und aktiviert optional Unattended-Upgrades.

ansible-playbook -i inventory/production.yml \
  playbooks/system-maintenance.yml

Tipp: Mit --check lässt sich zunächst ein Dry-Run starten, um anstehende Updates zu prüfen.

Ansible Vault Operations

View Encrypted File

ansible-vault view secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

Edit Encrypted File

ansible-vault edit secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

Change Vault Password

ansible-vault rekey secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

Decrypt File (Temporarily)

ansible-vault decrypt secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

# DO NOT COMMIT DECRYPTED FILE!

# Re-encrypt when done
ansible-vault encrypt secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

Testing Playbooks

Test with Check Mode (Dry Run)

ansible-playbook playbooks/deploy-update.yml \
  --check \
  -e "image_tag=test"

Test Connection

ansible production -m ping

Verify Inventory

ansible-inventory --list -y

System Maintenance Dry Run

ansible-playbook -i inventory/production.yml \
  playbooks/system-maintenance.yml \
  --check

Security Best Practices

  1. Never commit unencrypted secrets

    • production.vault.yml must be encrypted
    • .vault_pass is gitignored
    • Use .example files for documentation
  2. Rotate secrets regularly

    • Update vault file
    • Re-run setup-production-secrets.yml
    • Restart affected services
  3. Limit SSH key access

    • Use separate SSH key for Ansible
    • Limit key to deploy user only
    • Consider IP restrictions
  4. Vault password security

    • Store vault password in secure password manager
    • Don't share via insecure channels
    • Use different passwords for dev/staging/prod

Troubleshooting

Vault Decryption Failed

Error: Decryption failed (no vault secrets were found)

Solution:

# Verify vault password is correct
ansible-vault view secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

# If password is wrong, you'll need the original password to decrypt

SSH Connection Failed

Error: Failed to connect to the host

Solutions:

# Test SSH connection manually
ssh -i ~/.ssh/production deploy@94.16.110.151

# Check SSH key permissions
chmod 600 ~/.ssh/production
chmod 644 ~/.ssh/production.pub

# Verify SSH key is added to server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151

Docker Registry Authentication Failed

Error: unauthorized: authentication required

Solution:

# Verify registry credentials in vault file
ansible-vault view secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

# Test registry login manually on production server
docker login registry.michaelschiemer.de

Service Not Starting

Check service logs:

# SSH to production server
ssh -i ~/.ssh/production deploy@94.16.110.151

# Check Docker Compose service logs
docker compose -f {{ app_stack_path }}/docker-compose.yml logs app
docker compose -f {{ app_stack_path }}/docker-compose.yml logs nginx

# Check stack status
docker compose -f {{ app_stack_path }}/docker-compose.yml ps

CI/CD Integration

These playbooks are automatically executed by Gitea Actions workflows:

  • .gitea/workflows/production-deploy.yml - Calls deploy-update.yml on push to main
  • .gitea/workflows/update-production-secrets.yml - Calls setup-production-secrets.yml on manual trigger
  • .gitea/workflows/system-maintenance.yml - Führt system-maintenance.yml geplant oder manuell aus, um Pakete aktuell zu halten

Vault password is stored as Gitea Actions secret: ANSIBLE_VAULT_PASSWORD

Inventory Variables

All zentralen Variablen werden in group_vars/production.yml gepflegt und können bei Bedarf im Inventory überschrieben werden. Häufig verwendete Werte:

Variable Beschreibung Standardwert
deploy_user_home Home-Verzeichnis des Deploy-Users /home/deploy
stacks_base_path Basispfad für Docker Compose Stacks /home/deploy/deployment/stacks
app_stack_path Pfad zum Application Stack /home/deploy/deployment/stacks/application
backups_path Ablageort für Deployment-Backups /home/deploy/deployment/backups
docker_registry Interner Registry-Endpunkt (lokal) localhost:5000
docker_registry_external Externer Registry-Endpunkt registry.michaelschiemer.de
app_domain Produktions-Domain michaelschiemer.de
health_check_url Health-Check Endpoint https://michaelschiemer.de/health
max_rollback_versions Anzahl vorgehaltener Backups 5
system_update_packages Aktiviert OS-Paketupdates via system-Rolle true
system_apt_upgrade Wert für apt upgrade (z.B. dist) dist
system_enable_unattended_upgrades Aktiviert unattended-upgrades true
system_enable_unattended_reboot Steuert automatische Reboots nach Updates false
system_unattended_reboot_time Reboot-Zeitfenster (wenn aktiviert) 02:00
system_enable_unattended_timer Aktiviert Systemd-Timer für apt true
system_enable_docker_prune Führt nach Updates docker system prune aus false

Backup Management

Backups are automatically created before each deployment:

  • Location: /home/deploy/backups/
  • Retention: Last 5 versions kept
  • Contents:
    • current_image.txt - Previously deployed image
    • stack_status.txt - Stack status before deployment
    • deployment_metadata.txt - Deployment details

List Available Backups

ssh -i ~/.ssh/production deploy@94.16.110.151 \
  "ls -lh /home/deploy/backups/"

Manual Backup

ansible-playbook playbooks/deploy-update.yml \
  --tags backup \
  -e "image_tag=current"

Support

For issues with:

  • Playbooks: Check this README and playbook comments
  • Vault: See Ansible Vault documentation
  • Deployment: Review Gitea Actions logs
  • Production: SSH to server and check Docker logs