# Infrastructure qpismont.fr Infrastructure self-hosted complète hébergée sur serveur dédié, fournissant une plateforme DevOps avec Git, CI/CD, reverse proxy, VPN et observabilité. **Domaine :** qpismont.fr **Stack :** Docker Swarm **Repository :** https://gitea.qpismont.fr/qpismont/infra --- ## Vue d'ensemble Cette infrastructure héberge l'ensemble des services nécessaires pour le développement et le déploiement de projets personnels : - **Hébergement Git** avec interface web (Gitea) - **CI/CD automatisé** pour builds et déploiements (Woodpecker) - **Agent IA de review** de code sur webhooks Gitea (Herald) - **Reverse proxy** avec HTTPS automatique (Caddy) - **Accès VPN sécurisé** pour l'administration (WireGuard + CoreDNS) - **Observabilité** : métriques, traces et dashboards (Prometheus, Tempo, Perses) - **Alerting** temps réel avec notifications push (Alertmanager + ntfy) --- ## Services déployés ### Stack `core` | Service | Image | Description | Exposition | |---------|-------|-------------|------------| | **reverse_caddy** | `dhi.io/caddy:2.11` | Reverse proxy, HTTPS auto, HTTP/3 (non-root, ports internes 8080/8443) | `80→8080`, `443→8443/tcp`, `443→8443/udp` | | **wireguard_server** | `linuxserver/wireguard:latest` | VPN d'accès distant + CoreDNS | `51820/udp` | ### Stack `git` | Service | Image | Description | URL | |---------|-------|-------------|-----| | **gitea** | `docker.gitea.com/gitea:28.2` | Hébergement Git | https://gitea.qpismont.fr · https://git.qpismont.fr | | **woodpecker_server** | `woodpeckerci/woodpecker-server:v3.19-alpine` | Serveur CI/CD | https://woodpecker.qpismont.fr | | **woodpecker_agent** | `woodpeckerci/woodpecker-agent:v3.19-alpine` | Agent d'exécution (Docker socket) | (interne) | | **herald** | `tintounn/herald:1.2` | Agent IA de review sur webhooks Gitea (Docker socket) | (interne, métriques `:9100`) | ### Stack `observability` | Service | Image | Description | Exposition | |---------|-------|-------------|------------| | **prometheus** | `prom/prometheus:v3.13.3` | TSDB + remote-write receiver | (interne) | | **otel-collector** | `otel/opentelemetry-collector-contrib:0.164.0` | Collecte métriques + traces | (interne) | | **docker-proxy-otel** | `tecnativa/docker-socket-proxy:0.4.0` | Proxy socket Docker (lecture seule) | (interne) | | **tempo** | `grafana/tempo:3.0.3` | Stockage de traces (OTLP) | (interne) | | **perses** | `persesdev/perses:latest` | Dashboards | (via VPN) | | **alertmanager** | `prom/alertmanager:v0.34.1` | Routage des alertes | (interne) | | **blackbox_exporter** | `prom/blackbox-exporter:v0.28.0` | Probes HTTP externes | (interne) | | **ntfy** | `binwiederhier/ntfy:latest` | Notifications push | https://ntfy.qpismont.fr | ### Configuration - **Timezone** : Europe/Paris - **OAuth** : Gitea ↔ Woodpecker intégré - **Admin** : qpismont - **Scrape interval** : 10 secondes - **Rétention Tempo** : 48 h - **Rétention Prometheus** : par défaut (15 j), volume persistant --- ## Architecture ``` Internet │ ┌────────────┼────────────┐ │ │ │ Port 80/443 Port 443/udp Port 51820/udp │ │ │ ▼ ▼ ▼ [Caddy Proxy] [HTTP/3] [WireGuard VPN] │ │ ┌───────────┼───────────┬──────────┐ │ │ │ │ │ │ gitea:3000 woodpecker:8000 │ ntfy:80│ │ │ www.qpismont.fr [CoreDNS] (portfolio) │ [Perses] [Prometheus] [Tempo] ┌──────────────────────────────────────────────────────────────┐ │ Stack git (CI/CD) │ │ │ │ [Gitea] ←──── OAuth ────→ [Woodpecker Server] │ │ │ │ │ │ │ webhook ▼ │ │ ▼ [Woodpecker Agent] │ │ [Herald] │ │ │ (review IA) ▼ │ │ [Docker-in-Docker] │ │ (build containers) │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ Stack observability │ │ │ │ [OTel Collector] ──┬─ hostmetrics (CPU/RAM/disk/net) │ │ │ ├─ docker_stats (métriques containers) │ │ │ ├─ prometheus scrape: │ │ │ │ reverse_caddy:2019 │ │ │ │ herald:9100 │ │ │ │ alertmanager:9093 │ │ │ │ blackbox_exporter:9115 │ │ │ └─ OTLP (4318) │ │ │ │ │ ├── remote_write ──→ [Prometheus] ──→ [Perses] │ │ └── OTLP ──────────→ [Tempo] │ │ │ │ │ [Prometheus] ── alertes ──→ [Alertmanager] ──→ [ntfy] │ └──────────────────────────────────────────────────────────────┘ ``` ### Stacks Docker Swarm | Stack | Fichier compose | Services | |-------|-----------------|----------| | `core` | `docker-compose.core.yml` | Caddy, WireGuard | | `git` | `docker-compose.git.yml` | Gitea, Woodpecker (server + agent), Herald | | `observability` | `docker-compose.observability.yml` | Prometheus, Perses, Tempo, Alertmanager, Blackbox, OTel Collector, proxy socket, ntfy | ### Réseaux Docker Swarm | Réseau | Type | Services | |--------|------|----------| | `reverse_network` | overlay | Caddy, Gitea, Woodpecker (server + agent), Herald, ntfy | | `wireguard_network` | overlay | WireGuard, Prometheus, Perses, Tempo | | `prometheus_network` | overlay | Caddy, Herald, Prometheus, Perses, Tempo, Alertmanager, Blackbox, OTel Collector, docker-proxy-otel, ntfy | --- ## Stack technique ### Orchestration - **Docker Engine** avec Docker Swarm mode - **Docker Compose** v3 (Stack deployment) - **Docker Secrets** pour credentials sensibles - **Docker Configs** pour configurations versionnées ### Infrastructure as Code - Configuration : `docker-compose.core.yml` (Caddy, WireGuard), `docker-compose.git.yml` (Gitea, Woodpecker, Herald), `docker-compose.observability.yml` (Prometheus, Perses, Tempo, ntfy…) - Déploiement : `./deploy.sh` (génère timestamp pour versioning) - Contexte Docker distant : `prod` ### Networking - **3 réseaux overlay** isolés - **Reverse proxy** : Caddy avec Let's Encrypt automatique - **HTTP/3** (QUIC) activé sur port 443/udp - **VPN** : WireGuard pour services internes ### Storage - **Volumes Docker** : `reversecaddydata`, `reversecaddyconfig` (core, propriété UID 65532) · `giteadata`, `woodpeckerdata` (git) · `prometheusdata`, `tempodata`, `persesdata`, `alertmanagerdata`, `ntfydata` (observability) - **Host mounts** : `/home/user/wireguard-config`, `/lib/modules` (WireGuard) · `/var/run/docker.sock` (Woodpecker agent, Herald, `docker-proxy-otel`) · `/` en lecture seule (OTel Collector) ### Monitoring - **Collecte** : OTel Collector (hostmetrics + scrape Prometheus + OTLP) - **Stockage métriques** : Prometheus (remote-write receiver activé) - **Stockage traces** : Tempo (OTLP HTTP `:4318`, rétention 48 h) - **Dashboards** : Perses (accessible via VPN) - **Container metrics** : receiver `docker_stats` de l'OTel Collector (labels Swarm `service_name` / `task_name`) - **Proxy metrics** : Caddy admin API (`:2019`) - **Probes externes** : blackbox_exporter (`http_2xx` sur les URLs publiques) - **Alerting** : Prometheus → Alertmanager → ntfy (`http://ntfy:80/alerts`) ### Backups - ⚠️ **Aucun service de backup n'est déployé actuellement.** L'ancien service `docker-volume-backup` a été retiré (commit `041eb51`). Les sauvegardes sont à refaire manuellement ou à réintroduire. --- ## Fonctionnalités ### Gitea - Hébergement de repositories Git illimité - Issues, Pull Requests, Wikis - OAuth provider pour Woodpecker - Interface web complète - API REST ### Woodpecker CI - Pipelines CI/CD automatisés - Intégration native avec Gitea - Agent Docker-in-Docker - Limite : 1 workflow concurrent, CPU core 1 - Support multi-architectures ### Herald - Agent IA de review de code, déclenché par webhooks Gitea - Modèle via OpenRouter (`openai/gpt-5.6-luna-pro`) - Sandbox d'exécution limitée à 32 itérations - 5 jobs concurrents max, timeout Gitea 60 s / OpenRouter 600 s - Métriques Prometheus exposées sur `:9100` ### Caddy - HTTPS automatique avec Let's Encrypt - HTTP/3 (QUIC) support - Renouvellement automatique des certificats - Métriques Prometheus exposées - Configuration simple (Caddyfile) - Image hardened `dhi.io/caddy:2.11` : non-root (UID 65532), sans shell - Écoute interne sur `8080`/`8443`, mappée depuis `80`/`443` (ports privilégiés) - `Alt-Svc` forcé sur `:443` pour conserver HTTP/3 vu de l'extérieur ### WireGuard - VPN moderne et rapide - Configuration : 1 peer (extensible) - Accès sécurisé aux services internes - CoreDNS embarqué pour résoudre `perses.internal`, `prometheus.internal`, `tempo.internal` - Faible overhead ### Monitoring - **OTel Collector** : hostmetrics (CPU, RAM, disque, réseau, load, process) + `docker_stats` (métriques containers) + scrape Prometheus + réception OTLP - **Prometheus** : stockage des métriques, remote-write receiver - **Tempo** : stockage des traces OTLP - **Perses** : visualisation moderne (alternative Grafana) - **blackbox_exporter** : probes HTTP sur les URLs publiques - **Alertmanager** : routage des alertes vers ntfy - Scrape automatique toutes les 10 secondes ### Alerting - Règles : `ServiceDown` (probe blackbox), `OtelCollectorDown`, `CaddyDown` - Seuil : 1 minute - Inhibition : `CaddyDown` supprime les `ServiceDown` (évite le bruit) - Notification : webhook ntfy avec template `alertmanager`, `send_resolved: true` --- ## URLs et accès ### Services publics - **Gitea** : https://gitea.qpismont.fr (alias https://git.qpismont.fr) - **Woodpecker** : https://woodpecker.qpismont.fr - **Portfolio** : https://www.qpismont.fr (`qpismont.fr` redirige vers `www`) - **ntfy** : https://ntfy.qpismont.fr ### Services internes (VPN requis) - **Perses** : http://perses.internal:8080 (via WireGuard/CoreDNS) - **Prometheus** : http://prometheus.internal:9090 - **Tempo** : http://tempo.internal:3200 - **Caddy Admin** : http://reverse_caddy:2019 (métriques) ### VPN - **Protocole** : WireGuard - **Port** : 51820/udp - **Sous-réseau** : `10.13.13.0/24` - **Config** : `/home/user/wireguard-config/peer1/` --- ## Déploiement Le déploiement se fait via un contexte Docker distant vers le serveur de production : ```bash ./deploy.sh ``` Le script génère un timestamp pour versionner les configs et secrets, puis déploie les trois stacks (`core`, `git`, `observability`) sur le contexte `prod`. **Prérequis :** - Contexte Docker `prod` configuré - Réseaux overlay créés (`reverse_network`, `wireguard_network`, `prometheus_network`) - Secrets configurés dans `./secrets/` (dossier ignoré par Git) --- ## Configuration ### Secrets Docker | Secret | Fichier source | Usage | |--------|----------------|-------| | `woodpecker_agent_secret` | `./secrets/woodpecker_agent_secret` | Communication server/agent Woodpecker | | `woodpecker_gitea_client` | `./secrets/woodpecker_gitea_client` | OAuth client ID Gitea | | `woodpecker_gitea_secret` | `./secrets/woodpecker_gitea_secret` | OAuth client secret Gitea | | `herald_gitea_token` | `./secrets/herald/herald_gitea_token` | Token API Gitea pour Herald | | `herald_openrouter_token` | `./secrets/herald/herald_openrouter_token` | Clé API OpenRouter | | `herald_gitea_header_secret` | `./secrets/herald/herald_gitea_header_secret` | Vérification signature webhook Gitea | | `herald_sentry_dsn` | `./secrets/herald/herald_sentry_dsn` | DSN Sentry | ### Configs Docker | Config | Fichier source | Stack | |--------|----------------|-------| | `caddy_config` | `./Caddyfile` | core | | `caddy_portfolio_html` | `./portfolio/index.html` | core | | `wireguard_coredns_config` | `./wireguard/CoreDns` | core | | `prometheus_config` | `./prometheus/prometheus.yml` | observability | | `prometheus_rules_config` | `./prometheus/alert.rules.yml` | observability | | `alertmanager_config` | `./prometheus/alertmanager/alertmanager.yml` | observability | | `blackbox_config` | `./blackbox/blackbox.yml` | observability | | `otel_collector_config` | `./otel-collector/config.yaml` | observability | | `tempo_config` | `./tempo/tempo.yml` | observability | Les configs et secrets sont versionnés avec `${DATETIME}` pour forcer les mises à jour lors du déploiement. Ils s'accumulent donc à chaque déploiement : voir la section [Nettoyage](#nettoyage). ---