Files
infra/README.md
T

301 lines
14 KiB
Markdown

# 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** | `caddy:2.11-alpine` | Reverse proxy, HTTPS auto, HTTP/3 | `80`, `443/tcp`, `443/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) · `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)
### 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).
---