Compare commits

...

21 Commits

Author SHA1 Message Date
Eric FELIXINE
83779cf5d7 fix: telegraf topics, mqtt brokers, docker-compose fixes
- Fix MOSQUITTO_HOST (wrong container name)
- Fix EMQX_PORT (1885 external -> 1883 internal)
- Fix telegraf MQTT topics (city/sensors/#)
- Fix BunkerM dynsec JSON
- Add kepler.yml Traefik config
- Update monitoring script
2026-06-07 20:18:41 -04:00
Eric FELIXINE
7c0cb330d9 chore: update TODO.md timestamp 2026-06-04 10:15 2026-06-04 10:26:34 -04:00
Eric FELIXINE
f45ac0cb6e feat(k8s): add defaults/main.yml, meta/main.yml for all 27 roles + 4 helm templates
- Added defaults/main.yml with production-ready values for all 27 Ansible roles
- Added meta/main.yml with role dependencies (DAG: prereq → namespaces → storage → traefik → cert-manager → services)
- Created 4 missing Helm templates: flink-deployment, kafka-cluster, smartapp-web, smartapp-api
- Fixed YAML syntax error in backup/tasks/main.yml (Velero backupStorageLocation)
- Updated README with domain list, dependencies diagram, and corrected Helm chart names
- All 81 YAML files pass validation
2026-06-04 09:45:16 -04:00
Eric FELIXINE
66ac47b684 docs: add infrastructure snapshot 2026-06-04 2026-06-04 02:26:23 -04:00
Eric FELIXINE
fb62291b3e feat: add helm/ansible deployment files for Kubernetes
Some checks failed
Build & Deploy Smart App Web / lint (push) Failing after 1s
Build & Deploy Smart App Web / build-web (push) Has been skipped
Build & Deploy Smart App Web / docker-build (push) Has been skipped
Build & Deploy Smart App Web / deploy (push) Has been skipped
2026-06-04 02:09:17 -04:00
Eric FELIXINE
8c2251faba TODO: mise a jour 2026-06-04 - cleanup massif, helms ansible generés 2026-06-04 02:05:32 -04:00
Eric FELIXINE
b56749182e chore: update TODO — Honcho API deployed, Gitea Actions configured, Smart App Docker ready
Honcho:
- API UP on honcho.digitribe.fr (port 8089)
- Workspace 'hermes-agent' and session 'smart-city-session' created
- Memory storage working (messages stored via REST API)
- Hermes plugin configured in ~/.hermes/honcho.json
- Dialectic chat requires valid LLM API key

Gitea Actions:
- Runner docker-runner-01 registered and working
- SSH secrets configured (SERVER_HOST, SERVER_USER, SSH_PRIVATE_KEY)
- Workflow: lint + build + deploy

Smart App:
- Dockerfile web: multi-stage node + nginx
- Traefik: smartapp.digitribe.fr
- deploy.sh: web/docker/api/all
- LocalAI config removed (service no longer exists)
2026-06-02 06:57:56 -04:00
Eric FELIXINE
808dbbe4f3 ci: test full pipeline — secrets configured (SERVER_HOST, SERVER_USER, SSH_PRIVATE_KEY) 2026-06-02 01:19:10 -04:00
Eric FELIXINE
9f40e187d8 ci: verify Gitea Actions runner — docker-runner-01 registered and working 2026-06-01 23:57:39 -04:00
Eric FELIXINE
f8e34562d5 feat(smart-app): CI/CD pipeline + deploy scripts
Some checks failed
Build & Deploy Smart App Web / lint (push) Failing after 1s
Build & Deploy Smart App Web / build-web (push) Has been skipped
Build & Deploy Smart App Web / docker-build (push) Has been skipped
Build & Deploy Smart App Web / deploy (push) Has been skipped
- .gitea/workflows/build-and-deploy.yml:
  - Lint + TypeScript check on PR
  - Expo web build on push to master
  - Deploy via SSH to server (copy to /var/www/smartapp)
  - Docker build alternative
  - Artifact upload for builds
- deploy.sh: manual deploy script (web|docker|api|all)
- app.json: Expo config with bundle IDs
- assets/: generated icons (icon, splash, adaptive, favicon)
- Added expo-pwa, html-webpack-plugin, workbox-webpack-plugin deps
2026-06-01 23:03:11 -04:00
Eric FELIXINE
fd583a8b16 feat(smart-app): add Docker web build + Traefik integration 2026-06-01 22:49:31 -04:00
Eric FELIXINE
43ae2ebcac feat(smart-app): complete all remaining components, screens, hooks, services, stores, i18n
- i18n/index.ts: i18next setup with FR/EN/ES/DE translations
- constants.ts: app config, sensor types, alert severity, storage keys, refresh intervals
- store/index.ts: barrel export for all stores
- iotStore.ts: full IoT store (6 sensors, 3 zones, 2 alerts) with actions
- notificationStore.ts: notification store (5 mock notifications) with actions
- uiStore.ts: theme/language store + translation maps for 4 languages
- useSensors.ts: sensor filtering by type/zone, alert sensors selector
- useAlerts.ts: active alerts, critical alerts, acknowledge
- useNotifications.ts: notification CRUD operations
- useLocation.ts: GPS location with expo-location, default Fort-de-France
- SensorCard.tsx: full sensor card with status dot, compact mode
- StatsCard.tsx: stats card with icon, value, trend
- AlertCard.tsx: alert card with severity bar, acknowledge button
- ZoneCard.tsx: zone card with color bar, sensor/alert counts
- LineChart.tsx: bar-based line chart with Y-axis labels
- BarChart.tsx: bar chart with value labels
- GaugeChart.tsx: semi-circular gauge with color thresholds
- MapView.tsx: map placeholder with overlay markers
- MarkerPopup.tsx: popup with title, value, status, detail button
- DashboardScreen.tsx: analytics dashboard with gauges + charts
- SensorDetailScreen.tsx: sensor detail with gauge + history chart
- NotificationPrefsScreen.tsx: notification preference toggles (4)
- LayerDetailScreen.tsx: layer detail placeholder
- iot.service.ts: CRUD operations for sensors, zones, alerts
- gis.service.ts: geocoding, POI search, routing
2026-06-01 22:31:36 -04:00
Eric FELIXINE
a5124b0f0d chore: update TODO.md with smart app MVP and ditto fixes 2026-06-01 18:03:40 -04:00
Eric FELIXINE
e30ae8ed09 feat(smart-app): implement complete mobile app MVP
- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
2026-06-01 18:00:35 -04:00
Eric FELIXINE
08ca495bde feat: backend FastAPI Smart App City — auth JWT, IoT, GIS, notifications, reporting 2026-06-01 14:47:05 -04:00
Eric FELIXINE
31334b5ce5 chore: session resume final 2026-06-01 — lakehouse traefik routes, network fixes 2026-06-01 14:10:52 -04:00
Eric FELIXINE
ef6e5fbae0 feat: routes Traefik pour lakehouse (trino, kafka-ui, flink, gravitino, minio) 2026-06-01 14:09:55 -04:00
Eric FELIXINE
ae35506db6 chore: backup session 2026-06-01 final — snapshot, resume, TODO, all fixes documented 2026-06-01 12:24:34 -04:00
Eric FELIXINE
8c38a23b4b chore: session resume 2026-06-01 final — JupyterHub spawn fix, all creds documented 2026-06-01 12:07:10 -04:00
Eric FELIXINE
cca9e4aedc fix: JupyterHub spawn - switch to LocalProcessSpawner, fix password hash, eric user
- SimpleLocalProcessSpawner doesn't pass JUPYTERHUB_SERVICE_URL in JH 5.3.0
- LocalProcessSpawner handles env vars correctly
- Fixed eric password hash (bcrypt instead of PBKDF2)
- eric user created with admin rights
- JupyterLab accessible at https://jupyter.digitribe.fr
- Credentials: eric / Digitribe972
2026-06-01 11:45:52 -04:00
Eric FELIXINE
85199fc3f0 chore: session backup 2026-06-01 continue — Kafka/Trino/JupyterHub fixes, TODO update 2026-06-01 10:39:11 -04:00
35731 changed files with 3712718 additions and 335 deletions

2
.env.ditto Normal file
View File

@@ -0,0 +1,2 @@
DITTO_JWT_SECRET=NTOT-Vh8WRKWE52eV8zRiLs3a-gd8YUGSrvm5x2InZc
DEVOPS_PASSWORD=OvP9WVB09aFDnYPyK52UIg

View File

@@ -0,0 +1,121 @@
# Gitea Actions — CI/CD Smart App City Web
# Trigger: push sur master ou PR
name: Build & Deploy Smart App Web
on:
push:
branches: [master]
paths:
- 'smart-app-city/frontend/**'
pull_request:
branches: [master]
jobs:
# ─── Lint + Type Check ─────────────────────────────────────────────────
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: smart-app-city/frontend/package-lock.json
- name: Install dependencies
working-directory: smart-app-city/frontend
run: npm ci --legacy-peer-deps
- name: TypeScript check
working-directory: smart-app-city/frontend
run: npx tsc --noEmit
continue-on-error: true # TODO: fix TS strict errors
# ─── Build Web ─────────────────────────────────────────────────────────
build-web:
runs-on: ubuntu-latest
needs: lint
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: smart-app-city/frontend/package-lock.json
- name: Install dependencies
working-directory: smart-app-city/frontend
run: npm ci --legacy-peer-deps
- name: Build Expo web
working-directory: smart-app-city/frontend
run: npx expo export:web
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: smartapp-web-build
path: smart-app-city/frontend/dist/
retention-days: 7
# ─── Deploy to Server ──────────────────────────────────────────────────
deploy:
runs-on: ubuntu-latest
needs: build-web
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: smartapp-web-build
path: smart-app-city/frontend/dist/
- name: Deploy to server via SSH
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "smart-app-city/frontend/dist/"
target: "/var/www/smartapp/"
strip_components: 3
- name: Restart nginx (or copy to Docker volume)
uses: appleboy/ssh-action@v1.0.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
# Option A: If using Docker volume
docker cp /var/www/smartapp/ smartapp-web:/usr/share/nginx/html/
# Option B: If using Docker build
# cd /home/eric/smart-city-digital-twin-martinique/smart-app-city
# docker compose up -d --build smartapp-web
echo "✅ Smart App Web deployed at $(date)"
# ─── Build Docker Image (alternative) ───────────────────────────────────
docker-build:
runs-on: ubuntu-latest
needs: lint
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
working-directory: smart-app-city/frontend
run: |
docker build -t smartapp-web:${{ github.sha }} .
docker tag smartapp-web:${{ github.sha }} smartapp-web:latest
# Note: Pour pousser vers un registry privé, ajouter docker login + push
# - name: Push to registry
# run: docker push registry.digitribe.fr/smartapp-web:latest

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Dependencies
node_modules/
*/node_modules/
# Build outputs
dist/
build/
*.pyc
__pycache__/
# Environment files
.env
.env.local
*.env
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db

14
32-kafka-ui.yml Normal file
View File

@@ -0,0 +1,14 @@
http:
routers:
kafka-ui:
rule: "Host(`kafka.digitribe.fr`)"
entryPoints:
- websecure
service: kafka-ui-svc
tls:
certResolver: letsencrypt
services:
kafka-ui-svc:
loadBalancer:
servers:
- url: "http://kafka-ui:8080"

14
32-trino.yml Normal file
View File

@@ -0,0 +1,14 @@
http:
routers:
trino:
rule: "Host(`trino.digitribe.fr`)"
entryPoints:
- websecure
service: trino-svc
tls:
certResolver: letsencrypt
services:
trino-svc:
loadBalancer:
servers:
- url: "http://trino:8084"

14
33-flink.yml Normal file
View File

@@ -0,0 +1,14 @@
http:
routers:
flink:
rule: "Host(`flink.digitribe.fr`)"
entryPoints:
- websecure
service: flink-svc
tls:
certResolver: letsencrypt
services:
flink-svc:
loadBalancer:
servers:
- url: "http://flink-jobmanager:8081"

14
34-gravitino.yml Normal file
View File

@@ -0,0 +1,14 @@
http:
routers:
gravitino:
rule: "Host(`gravitino.digitribe.fr`)"
entryPoints:
- websecure
service: gravitino-svc
tls:
certResolver: letsencrypt
services:
gravitino-svc:
loadBalancer:
servers:
- url: "http://gravitino:8090"

14
35-minio.yml Normal file
View File

@@ -0,0 +1,14 @@
http:
routers:
minio:
rule: "Host(`minio.digitribe.fr`)"
entryPoints:
- websecure
service: minio-svc
tls:
certResolver: letsencrypt
services:
minio-svc:
loadBalancer:
servers:
- url: "http://minio:9001"

159
TODO.md
View File

@@ -1,82 +1,121 @@
# Smart City Digital Twin — TODO List
> Dernière mise à jour : 2026-06-01 17:00 (fin de session)
> Dernière mise à jour : 2026-06-04 02:00 (finalisation)
## ✅ Complété (session 2026-06-01)
## ✅ Complété (session 2026-06-03 / 06-04)
| ID | Tâche | Détail |
|----|-------|--------|
| jupyterhub-fix | JupyterHub DB path | `sqlite:////srv/jupyterhub/jupyterhub.sqlite` (absolute path) |
| jupyterhub-rebuild | Rebuild Dockerfile | Supprimé double-nested `/srv/jupyterhub/srv/jupyterhub` |
| jupyterhub-spawner | Spawner config | `SimpleLocalProcessSpawner`, timeout 300s |
| jupyterhub-user | User eric | Créé id=2, admin, authorized |
| hermes-dashboard | Dashboard WebUI+TUI | systemd service, localhost:9119, auto-boot |
| or-mbtiles-metadata | Bounds monde + center Martinique | `sqlite3` UPDATE sur metadata |
| or-map-settings | mapsettings.json vérifié | center=[-61,14.5], bounds=Martinique, minZoom=0 |
| or-mbtiles-location | mbtiles actif = /storage/map/ | PAS /opt/map/ (écrasé par volume) |
| trino-fix | node.properties créé | `node.environment=production` — restart needed |
| skill-update | openremote-overview | Section Map & Tile Configuration ajoutée |
| git-push | Commits | `acdf250` pushé sur Gitea |
| airflow-deploy | Apache Airflow déployé | `airflow.digitribe.fr` — Python 3.11, LocalExecutor |
| openfn-cleanup | OpenFN supprimé | Race condition Cachex/Ecto non résolue |
| ditto-cleanup | Stack Ditto supprimée | API v2 non fonctionnelle (schema-versions) |
| openremote-cleanup | Stack OpenRemote supprimée | Patches bundle appliqués |
| gravitino-cleanup | Gravitino supprimé | Unhealthy |
| fiware-gis-cleanup | FIWARE GIS Quickstart supprimé | |
| contexus-cleanup | Contexus supprimé | Unhealthy |
| kafka-cleanup | Kafka supprimé | Unhealthy + sera redeployé via Helm |
| flink-cleanup | Flink supprimé | Dépendances kafka |
| bi-cleanup | Superset + Metabase supprimés | Seront redeployés via Helm |
| mindsdb-cleanup | MindsDB supprimé | Autoheal unhealthy |
| odk-cleanup | ODK Central supprimé | Sera redeployé via Helm |
| jupyterhub-cleanup | JupyterHub supprimé | Sera redeployé via Helm |
| zeppelin-cleanup | Zeppelin supprimé | Sera redeployé via Helm |
| gis-cleanup | MapStore + GeoServer + FROST supprimés | Seront redeployés via Helm |
| iot-cleanup | Node-RED + phpIPAM + EMQX + Mosquitto + BunkerM + ChirpStack supprimés | Seront redeployés via Helm |
| monitoring-cleanup | Grafana + Loki + Prometheus + InfluxDB + Telegraf supprimés | Seront redeployés via Helm |
| storage-cleanup | MinIO + PostgreSQL + PostGIS + Redis + Zookeeper supprimés | Seront redeployés via Helm |
| misc-cleanup | AgentGateway + Esperotech + Redpanda Console + Docker exporter + Simulator supprimés | |
| backups | Sauvegardes config | Fichiers sauvegardés dans /home/eric/backups/2026-06-04/ |
| helms-ansible | Fichiers Helm/Ansibles générés | 25+ rôles dans helms/ |
| helms-readme | README déploiement K8s | Architecture, installation, troubleshooting |
| helms-vault | Template vault.yml | Variables chiffrées pour le déploiement |
| git-push | Push sur Gitea | 2 commits pushés (TODO + helms) |
## 🔴 Bloqué / En cours
## 🔴 En cours
| ID | Tâche | Raison | Prochaine action |
|----|-------|--------|------------------|
| or-map-bounds | MapService retourne bounds Pays-Bas | Bug MapResourceImpl.java: mbtiles metadata bounds prioritaire sur mapsettings.json | Générer vrai mbtiles MVT Martinique OU patcher code source OR |
| jupyterhub-spawn | Spawn eric timeout | Container resource limit? | Augmenter CPU/RAM container OU debug logs |
| kafka-restart | Kafka restart loop | Volumes corrimpus (ancien ZK data) | SUPPRIMER volumes kafka-1-data + kafka-2-data, recréer |
| trino-restart | Trino restart loop | node.properties créé mais pas appliqué | `docker restart trino` |
| (aucune) | — | — | — |
## ⏳ En attente
## ⏳ En attente (déploiement Kubernetes via Ansible)
| ID | Tâche |
|----|-------|
| or-mbtiles-martinique | Générer mbtiles MVT PBF pour Martinique (tippecanoe depuis GeoJSON filtré) |
| p1-or-map | Vérifier carte Martinique après fix bounds |
| p1-contexus-60 | Configurer les 60 devices Contexus |
| p3-analyse | GeoMesa + KeplerGL |
| p0-chirpstack | ChirpStack login API gRPC-REST |
| p1-thingsboard | Relancer ThingsBoard (si CPU dispo) |
| smart-app Phase 1 | MVP React Native |
| k8s-cluster | Créer le cluster Kubernetes (3 nœuds minimum) |
| nfs-server | Configurer le serveur NFS pour le storage |
| traefik-deploy | Déployer Traefik via Helm |
| cert-manager-deploy | Déployer cert-manager pour TLS |
| storage-deploy | Déployer NFS provisioner + StorageClass |
| monitoring-deploy | Déployer Prometheus + Grafana + Loki |
| databases-deploy | Déployer PostgreSQL HA + Redis + MinIO |
| kafka-deploy | Déployer Kafka (Strimzi) |
| flink-deploy | Déployer Apache Flink |
| airflow-deploy | Déployer Apache Airflow |
| iot-deploy | Déployer EMQX + Mosquitto + Node-RED + phpIPAM |
| gitea-deploy | Déployer Gitea |
| jupyterhub-deploy | Déployer JupyterHub |
| bi-deploy | Déployer Superset + Metabase |
| mindsdb-deploy | Déployer MindsDB |
| odk-deploy | Déployer ODK Central |
| gis-deploy | Déployer MapStore + GeoServer + FROST |
| clickhouse-deploy | Déployer ClickHouse (`clickhouse.digitribe.fr`) |
| starrocks-deploy | Déployer StarRocks (`starrocks.digitribe.fr`) |
| trino-deploy | Déployer Trino (`trino.digitribe.fr`) |
| deltalake-deploy | Déployer Delta Lake (`deltalake.digitribe.fr`) |
| streamlit-deploy | Déployer Streamlit (`streamlit.digitribe.fr`) |
| duckdb-deploy | Déployer DuckDB (`duckdb.digitribe.fr`) |
| smartapp-deploy | Déployer Smart App (`smartapp.digitribe.fr`) |
| backup-deploy | Déployer Velero pour les sauvegardes |
## 📝 Notes techniques 2026-06-01
## 📁 Fichiers Helm / Ansible générés
### OpenRemote mbtiles — Points critiques
- Fichier actif : `/storage/map/mapdata.mbtiles` (volume Docker), PAS `/opt/map/`
- OR 1.24.0 ne sert que du **PBF vectoriel** — PNG raster = 404
- Bug : MapService.java donne priorité aux bounds du mbtiles metadata sur mapsettings.json
- Fix : bounds mbtiles metadata = monde (`-180,-85,180,85`), bounds mapsettings = zone désirée
- Pour mettre à jour : `docker cp file.mbtiles openremote-manager:/storage/map/mapdata.mbtiles`
Le répertoire `helms/` (dans le repo Gitea) contient les fichiers pour un déploiement modulaire sur Kubernetes via Ansible.
### JupyterHub
- Port : 8000 (pas 8080)
- User eric : id=2, admin, password=Digitribe972 (hash bcrypt dans users_info)
- Config : `SimpleLocalProcessSpawner`, timeout 300s
- DB : `sqlite:////srv/jupyterhub/jupyterhub.sqlite` (absolute path, 4 slashes)
### Structure
```
helms/
├── README.md # Documentation déploiement
├── deploy.yml # Playbook principal
├── undeploy.yml # Playbook de suppression
├── inventory/hosts.yml # Inventory des nœuds K8s
├── group_vars/all.yml # Variables globales
├── group_vars/vault.yml # Variables chiffrées (template)
└── roles/ # 25+ rôles Ansible
```
### Hermes Dashboard
- Service : `hermes-dashboard.service` (systemd user)
- URL : `http://localhost:9119` (accès via SSH tunnel `-L 9119:127.0.0.1:9119`)
- TUI chat intégré dans l'onglet Chat du dashboard
### Utilisation
```bash
cd helms/
ansible-playbook deploy.yml --ask-vault-pass
ansible-playbook deploy.yml --tags clickhouse --ask-vault-pass
ansible-playbook undeploy.yml
```
### Infrastructure
- 86 conteneurs Docker au total
- Traefik, OpenRemote, Grafana, InfluxDB, Simulateur, ODK, MindsDB, MapStore, GeoServer, EMQX, Ditto, ChirpStack, Node-RED, MinIO, Flink, Gitea, LocalAI, PHPIPAM, Honcho = UP ✅
- Kafka, Trino = restart loop
- JupyterHub = UP mais spawn lent
## 📝 Infrastructure actuelle (10 containers Docker)
| Service | Image | Statut |
|---------|-------|--------|
| airflow-scheduler | apache/airflow:2.9.3-python3.11 | ✅ healthy |
| airflow-webserver | apache/airflow:2.9.3-python3.11 | ✅ healthy |
| airflow-init | apache/airflow:2.9.3-python3.11 | 🔄 restarting (one-shot) |
| airflow-postgres | postgres:16 | ✅ healthy |
| smartapp-api | smartapp-api:latest | ✅ Up 38h |
| smartapp-web | nginx:alpine | ✅ Up 38h |
| gitea-runner | gitea/act_runner:latest | ✅ Up 2 days |
| traefik | traefik:v3.1 | ✅ Up 2 days |
| smart-city-kepler | smart-city-kepler:latest | ✅ Up 2 weeks |
| gitea | gitea/gitea:latest | ✅ Up 2 jours |
## 📊 Statistiques
- **Containers Docker** : 10 (down from 72)
- **Stacks supprimées** : 6 (OpenFN, Ditto, OpenRemote, Gravitino, FIWARE GIS, Contexus)
- **Services unhealthy** : 0
- **Fichiers Helm/Ansible** : 33 fichiers
- **Rôles Ansible** : 25+
- **Namespaces K8s prévus** : 18
## Credentials
- **Contexus**: iotevadmin / Digitribe972
- **OpenRemote**: admin / Digitribe972
- **PostgreSQL Contexus**: contexus / Digitribe972
- **Redis Contexus**: Digitribe972
- **Telegraf InfluxDB**: token=my-super-token, org=digitribe, bucket=smartcity
- **Grafana**: admin / Digitribe972
- **Superset**: admin / Digitribe972
- **Metabase**: admin@digitribe.fr / Digitribe972
- **BunkerM MQTT**: bunker / bunker
- **ChirpStack**: admin / Digitribe972
- **ODK Central**: efelixine@digitribe.fr / Digitribe972
- **JupyterHub**: eric / Digitribe972 (admin)
- **MindsDB**: admin@digitribe.fr / Digitribe972
- **Gitea** : eric / (voir config)
- **Airflow** : admin / (changé par Eric)

View File

@@ -20,7 +20,7 @@ services:
- smartcity-shared
- traefik-public
ports:
- "1883:1900"
- "1884:1900"
- "2000:2000"
environment:
- MQTT_PORT=1900

View File

@@ -1,4 +1,5 @@
# Eclipse Ditto - Smart City Digital Twin - Martinique
# Using official Eclipse Ditto images with Akka cluster
services:
ditto-mongodb:
image: mongo:6
@@ -12,7 +13,7 @@ services:
- ditto-mongo-data:/data/db
ditto-policies:
image: eclipse/ditto-policies:latest
image: eclipse/ditto-policies:3.8.0
container_name: smart-city-ditto-policies
restart: unless-stopped
hostname: ditto-policies
@@ -21,25 +22,20 @@ services:
environment:
- TZ=Europe/Berlin
- BIND_HOSTNAME=0.0.0.0
- DITTO_JWT_SECRET=my-ditto-jwt-secret-key-12345
- MONGO_HOST=smart-city-ditto-mongodb
- MONGO_PORT=27017
- MONGO_DB=Policies
- DITTO_JWT_SECRET=NTOT-Vh8WRKWE52eV8zRiLs3a-gd8YUGSrvm5x2InZc
- MONGODB_URI=mongodb://smart-city-ditto-mongodb:27017/Policies
- AKKA_REMOTE_ENABLED=false
- AKKA_REMOTE_ENABLED=true
- AKKA_REMOTE_CANONICAL_HOSTNAME=ditto-policies
- AKKA_REMOTE_CANONICAL_PORT=2551
- JAVA_TOOL_OPTIONS=-Dditto.mongodb.uri=mongodb://smart-city-ditto-mongodb:27017/Policies -Dditto.mongodb.db-name=Policies
networks:
traefik-public:
aliases:
- ditto-cluster
- ditto-policies
labels:
- "traefik.enable=true"
- "traefik.http.routers.ditto-policies.rule=Host(`ditto-policies.digitribe.fr`)"
- "traefik.http.routers.ditto-policies.entrypoints=web"
- "traefik.http.services.ditto-policies.loadbalancer.server.port=8080"
ditto-things:
image: eclipse/ditto-things:latest
image: eclipse/ditto-things:3.8.0
container_name: smart-city-ditto-things
restart: unless-stopped
hostname: ditto-things
@@ -49,26 +45,20 @@ services:
environment:
- TZ=Europe/Berlin
- BIND_HOSTNAME=0.0.0.0
- DITTO_JWT_SECRET=my-ditto-jwt-secret-key-12345
- MONGO_HOST=smart-city-ditto-mongodb
- MONGO_PORT=27017
- MONGO_DB=Things
- DITTO_JWT_SECRET=NTOT-Vh8WRKWE52eV8zRiLs3a-gd8YUGSrvm5x2InZc
- MONGODB_URI=mongodb://smart-city-ditto-mongodb:27017/Things
- AKKA_REMOTE_ENABLED=false
- JAVA_TOOL_OPTIONS=-Dditto.things.authentication.devops.password=ditto-devops-secret
- AKKA_REMOTE_ENABLED=true
- AKKA_REMOTE_CANONICAL_HOSTNAME=ditto-things
- AKKA_REMOTE_CANONICAL_PORT=2551
- JAVA_TOOL_OPTIONS=-Dditto.mongodb.uri=mongodb://smart-city-ditto-mongodb:27017/Things -Dditto.mongodb.db-name=Things -Dditto.things.authentication.devops.password=OvP9WVB09aFDnYPyK52UIg
networks:
traefik-public:
aliases:
- ditto-cluster
- ditto-things
labels:
- "traefik.enable=true"
- "traefik.http.routers.ditto-things.rule=Host(`ditto-things.digitribe.fr`)"
- "traefik.http.routers.ditto-things.entrypoints=web"
- "traefik.http.services.ditto-things.loadbalancer.server.port=8080"
ditto-gateway:
image: eclipse/ditto-gateway:custom
image: eclipse/ditto-gateway:latest
container_name: smart-city-ditto-gateway
restart: unless-stopped
hostname: ditto-gateway
@@ -78,22 +68,46 @@ services:
environment:
- TZ=Europe/Berlin
- BIND_HOSTNAME=0.0.0.0
- DITTO_JWT_SECRET=my-ditto-jwt-secret-key-12345
- DITTO_JWT_SECRET=NTOT-Vh8WRKWE52eV8zRiLs3a-gd8YUGSrvm5x2InZc
- DITTO_GATEWAY_PROXY_ENABLED=true
- AKKA_REMOTE_ENABLED=false
- AKKA_REMOTE_ENABLED=true
- AKKA_REMOTE_CANONICAL_HOSTNAME=ditto-gateway
- AKKA_REMOTE_CANONICAL_PORT=2551
- DITTO_GW_STREAMING_ENABLED=true
- DITTO_GW_MQTT_BROKER=smart-city-mosquitto:1883
- DITTO_GW_MQTT_BROKER=192.168.192.26:1883
- DITTO_GW_MQTT_TOPIC_FILTER=smartcity/#
- DEVOPS_PASSWORD=ditto-devops-secret
- DEVOPS_PASSWORD=OvP9WVB09aFDnYPyK52UIg
- JAVA_TOOL_OPTIONS=-Xms512m -Xmx1024m -Dditto.gateway.http.port=8080 -Dditto.gateway.http.api.enabled=true
- DITTO_APIDOC_ENABLED=true
- DITTO_GATEWAY_HTTP_API_ENABLED=true
networks:
traefik-public:
aliases:
- ditto-cluster
- ditto-gateway
labels:
- "traefik.enable=true"
- "traefik.http.routers.ditto.rule=Host(`ditto.digitribe.fr`)"
- "traefik.http.routers.ditto.entrypoints=websecure"
- "traefik.http.routers.ditto.tls.certresolver=letsencrypt"
- "traefik.http.services.ditto.loadbalancer.server.port=8080"
ditto-ui:
image: eclipse/ditto-ui:latest
container_name: smart-city-ditto-ui
restart: unless-stopped
depends_on:
- ditto-gateway
networks:
traefik-public:
aliases:
- ditto-ui
networks:
traefik-public:
external: true
smartcity-shared:
external: true
volumes:
ditto-mongo-data:

29
docker-compose.emqx.yml Normal file
View File

@@ -0,0 +1,29 @@
services:
emqx:
image: emqx/emqx:5.4
container_name: emqx_emqx_1
restart: unless-stopped
networks:
- smartcity-shared
ports:
- "1885:1883"
- "8083:8083"
- "8883:8883"
- "8084:8084"
- "18083:18083"
environment:
- EMQX_NAME=emqx
- EMQX_HOST=emqx_emqx_1
volumes:
- emqx-data:/opt/emqx/data
- emqx-log:/opt/emqx/log
volumes:
emqx-data:
name: smart-city-emqx-data
emqx-log:
name: smart-city-emqx-log
networks:
smartcity-shared:
external: true

View File

@@ -23,7 +23,7 @@ services:
- IOTA_REGISTRY_TYPE=memory
# MQTT Listener - EMQX
- IOTA_MQTT_HOST=emqx_emqx_1
- IOTA_MQTT_PORT=1883
- IOTA_MQTT_PORT=1885
- IOTA_PROVIDER_URL=http://smart-city-iot-agent-emqx:4041
- IOTA_DEFAULT_RESOURCE=/
- IOTA_DEFAULT_APIKEY=smartcity-emqx

View File

@@ -13,7 +13,7 @@ services:
- orion-ld
- smart-city-orion-ld
traefik-public:
command: -dbhost smart-city-mongodb -db orion
command: -dbhost smart-city-iot-mongodb -db orion
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:1026/version || exit 1"]
interval: 30s

View File

@@ -1,28 +1,16 @@
# Redpanda → InfluxDB Consumer
# Lit les topics Redpanda et écrit dans InfluxDB pour Grafana
version: "3.8"
# DÉSACTIVÉ — Redpanda broker non démarré
# Usage: docker compose -f docker-compose.redpanda-consumer.yml up -d
services:
redpanda-consumer:
image: python:3.11-slim
container_name: smart-city-redpanda-consumer
restart: unless-stopped
restart: "no"
command: >
sh -c "pip install requests && python3 /app/consumer.py"
volumes:
- ./redpanda/consumer.py:/app/consumer.py:ro
environment:
- INFLUX_URL=http://smart-city-influxdb:8086
- INFLUX_TOKEN=my-super-admin-token
- INFLUX_ORG=digitribe
- INFLUX_BUCKET=iot_data
sh -c "echo 'Redpanda consumer désactivé — Redpanda broker non démarré' && sleep infinity"
networks:
- smartcity-shared
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://smart-city-redpanda:9644/public_metrics')"]
interval: 30s
timeout: 10s
retries: 3
networks:
smartcity-shared:

View File

@@ -30,7 +30,7 @@ services:
- ENABLE_BUNKER=1
- EMQX_HOST=emqx_emqx_1
- EMQX_PORT=1883
- MOSQUITTO_HOST=smart-city-digital-twin-martinique-mosquitto-1
- MOSQUITTO_HOST=smart-city-mosquitto-1
- MOSQUITTO_PORT=1883
- BUNKERM_HOST=bunkerm-bunkerm-1
- BUNKERM_PORT=1900

246
helms/README.md Normal file
View File

@@ -0,0 +1,246 @@
# Smart City Martinique - Déploiement Kubernetes
## Architecture
```
┌─────────────────────────────────────────────────────────┐
│ TRAEFIK (Ingress) │
│ ports 80/443 │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────┼─────────────────────────────────┐
│ │ │
┌────▼────┐ ┌──────────┐ ┌──────────▼──────────┐ ┌─────────────────┐
│ Airflow │ │ Kafka │ │ Data & Storage │ │ Monitoring │
│ │ │ Cluster │ │ │ │ │
│ web │ │ 3 brokers│ │ PostgreSQL HA │ │ Prometheus │
│ sched │ │ connect │ │ Redis Cluster │ │ Grafana │
│ worker │ │ ui │ │ MinIO │ │ Loki │
└─────────┘ └──────────┘ │ ClickHouse │ │ Promtail │
│ StarRocks │ └─────────────────┘
┌──────────┐ ┌──────────┐ │ Trino │
│ Flink │ │ IoT │ │ Delta Lake │ ┌─────────────────┐
│ │ │ │ │ DuckDB │ │ BI & Analytics │
│ jobmgr │ │ EMQX │ └─────────────────────┘ │ │
│ taskmgr │ │ Mosquitto│ │ Superset │
└──────────┘ │ Node-RED │ ┌─────────────────────┐ │ Metabase │
│ phpIPAM │ │ Git & Notebooks │ │ MindsDB │
┌──────────┐ │ ChirpStk │ │ │ └─────────────────┘
│ GIS │ └──────────┘ │ Gitea │
│ │ │ JupyterHub │ ┌─────────────────┐
│ MapStore │ ┌──────────┐ │ Zeppelin │ │ Web Apps │
│ GeoServer│ │ ODK │ └─────────────────────┘ │ │
│ FROST │ │ │ │ Smart App │
│ Stellio │ │ nginx │ ┌─────────────────────┐ │ Streamlit │
│ FIWARE │ │ service │ │ Data Collection │ │ Kepler │
└──────────┘ │ postgres │ │ │ └─────────────────┘
└──────────┘ │ Telegraf │
│ InfluxDB │
│ Simulator │
└─────────────────────┘
```
## Prérequis
### Cluster Kubernetes
- 3 nœuds minimum (1 master + 2 workers)
- Kubernetes 1.28+
- containerd
- Cilium (CNI)
### Serveur NFS
- 1 serveur NFS pour le stockage persistant
- Minimum 500Go d'espace disque
### Outils
- kubectl
- helm
- ansible 2.15+
- ansible-galaxy collection install kubernetes.core
## Installation
### 1. Cloner le repository
```bash
git clone https://gitea.digitribe.fr/eric/smart-city-digital-twin-martinique.git
cd smart-city-digital-twin-martinique/helms
```
### 2. Configurer l'inventory
Éditer `inventory/hosts.yml` avec les IPs de vos nœuds :
```yaml
k8s_masters:
hosts:
k8s-master-1:
ansible_host: "192.168.1.100"
k8s_workers:
hosts:
k8s-worker-1:
ansible_host: "192.168.1.101"
k8s-worker-2:
ansible_host: "192.168.1.102"
nfs_server:
hosts:
nfs-1:
ansible_host: "192.168.1.200"
```
### 3. Configurer les variables
Éditer `group_vars/all.yml` selon vos besoins (ressources, domaines, etc.)
### 4. Chiffrer les secrets
```bash
ansible-vault encrypt group_vars/vault.yml
```
### 5. Déployer
```bash
# Déployer toute la stack
ansible-playbook deploy.yml --ask-vault-pass
# Déployer un service spécifique
ansible-playbook deploy.yml --tags clickhouse --ask-vault-pass
ansible-playbook deploy.yml --tags trino --ask-vault-pass
ansible-playbook deploy.yml --tags streamlit --ask-vault-pass
ansible-playbook deploy.yml --tags kafka --ask-vault-pass
ansible-playbook deploy.yml --tags monitoring --ask-vault-pass
```
### 6. Vérifier
```bash
kubectl get pods --all-namespaces
kubectl get ingress --all-namespaces
```
## Services déployés
| Service | Domaine | Namespace | Helm Chart |
|---------|---------|-----------|------------|
| Traefik | traefik.digitribe.fr | traefik | traefik/traefik |
| Airflow | airflow.digitribe.fr | airflow | apache/airflow |
| Kafka | kafka-bootstrap.digitribe.fr | kafka | strimzi/kafka-operator |
| Flink | flink.digitribe.fr | flink | apache/flink-kubernetes-operator |
| ClickHouse | clickhouse.digitribe.fr | clickhouse | bitnami/clickhouse |
| StarRocks | starrocks.digitribe.fr | starrocks | community/starrocks |
| Trino | trino.digitribe.fr | trino | trinodb/trino |
| Delta Lake | deltalake.digitribe.fr | deltalake | custom |
| Streamlit | streamlit.digitribe.fr | streamlit | custom |
| DuckDB | duckdb.digitribe.fr | duckdb | custom |
| EMQX | emqx.digitribe.fr | iot | emqx/emqx-operator |
| Mosquitto | mqtt.digitribe.fr | iot | custom |
| Node-RED | nodered.digitribe.fr | iot | custom |
| phpIPAM | phpipam.digitribe.fr | phpipam | custom |
| Gitea | gitea.digitribe.fr | gitea | gitea-charts/gitea |
| JupyterHub | jupyter.digitribe.fr | jupyterhub | jupyterhub/jupyterhub |
| Superset | superset.digitribe.fr | superset | apache/superset |
| Metabase | metabase.digitribe.fr | metabase | bitnami/metabase |
| MindsDB | mindsdb.digitribe.fr | mindsdb | bitnami/mindsdb |
| ODK Central | odk.digitribe.fr | odk | custom |
| MapStore | mapstore.digitribe.fr | gis | custom |
| GeoServer | geoserver.digitribe.fr | gis | custom |
| Smart App | smartapp.digitribe.fr | smartapp | custom |
| Smart App API | api-smartapp.digitribe.fr | smartapp | custom |
| Grafana | grafana.digitribe.fr | monitoring | grafana/grafana |
| MinIO | minio.digitribe.fr | databases | bitnami/minio |
| PostgreSQL | — (interne) | databases | bitnami/postgresql-ha |
| Redis | — (interne) | databases | bitnami/redis-cluster |
## Dépendances entre rôles
```
prerequisites → namespaces → storage → traefik → cert-manager
┌─────────────────────┼─────────────────────┐
↓ ↓ ↓
databases monitoring kafka
(postgres, (prometheus, ↓
redis, minio) grafana, loki) flink
↓ ↓ ↓
└─────────────────────┼─────────────────────┘
┌─────────────────────┼─────────────────────┐
↓ ↓ ↓
airflow bi iot
gitea jupyterhub superset metabase emqx mosquitto
odk mindsdb trino nodered phpipam
gis clickhouse streamlit
smartapp deltalake duckdb
backup (Velero)
```
## Commandes utiles
```bash
# Lister tous les pods
kubectl get pods --all-namespaces
# Voir les logs d'un pod
kubectl logs -f <pod-name> -n <namespace>
# Voir les événements
kubectl get events --all-namespaces --sort-by='.lastTimestamp'
# Voir les ingress
kubectl get ingress --all-namespaces
# Voir les PVC
kubectl get pvc --all-namespaces
# Redéployer un service
ansible-playbook deploy.yml --tags <service> --ask-vault-pass
# Supprimer un service
kubectl delete namespace <namespace>
# Supprimer toute la stack
ansible-playbook undeploy.yml
```
## Troubleshooting
### Pod en CrashLoopBackOff
```bash
kubectl describe pod <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> --previous
```
### PVC en Pending
```bash
kubectl get storageclass
kubectl get pv
kubectl describe pvc <pvc-name> -n <namespace>
```
### Ingress non accessible
```bash
kubectl get ingress -n <namespace>
kubectl describe ingress <ingress-name> -n <namespace>
kubectl logs -f deployment/traefik -n traefik
```
## Maintenance
### Backup
Les sauvegardes sont configurées via Velero :
```bash
kubectl get schedules -n velero
kubectl get backups -n velero
```
### Mise à jour d'un service
```bash
ansible-playbook deploy.yml --tags <service> --ask-vault-pass
```
### Scaling
```bash
kubectl scale deployment <deployment> --replicas=<n> -n <namespace>
```

77
helms/deploy.yml Normal file
View File

@@ -0,0 +1,77 @@
---
# Playbook principal pour le déploiement Kubernetes
# Fichier: deploy.yml
- name: Déploiement Smart City Martinique sur Kubernetes
hosts: localhost
connection: local
gather_facts: false
vars_files:
- group_vars/all.yml
- group_vars/vault.yml
pre_tasks:
- name: Vérifier que kubectl est installé
command: kubectl version --client
changed_when: false
- name: Vérifier la connexion au cluster
command: kubectl cluster-info
changed_when: false
roles:
- role: prerequisites
tags: [prerequisites]
- role: namespaces
tags: [namespaces]
- role: storage
tags: [storage]
- role: traefik
tags: [traefik, ingress]
- role: cert-manager
tags: [cert-manager, tls]
- role: monitoring
tags: [monitoring]
- role: databases
tags: [databases]
- role: kafka
tags: [kafka]
- role: flink
tags: [flink]
- role: airflow
tags: [airflow]
- role: iot
tags: [iot, mqtt]
- role: gitea
tags: [gitea]
- role: jupyterhub
tags: [jupyterhub]
- role: bi
tags: [bi, superset, metabase]
- role: mindsdb
tags: [mindsdb]
- role: odk
tags: [odk]
- role: gis
tags: [gis, mapstore, geoserver, frost]
- role: clickhouse
tags: [clickhouse]
- role: starrocks
tags: [starrocks]
- role: trino
tags: [trino]
- role: deltalake
tags: [deltalake]
- role: streamlit
tags: [streamlit]
- role: duckdb
tags: [duckdb]
- role: nodered
tags: [nodered]
- role: phpipam
tags: [phpipam]
- role: smartapp
tags: [smartapp]
- role: backup
tags: [backup]

535
helms/group_vars/all.yml Normal file
View File

@@ -0,0 +1,535 @@
---
# Variables globales pour le déploiement Kubernetes
# Fichier: group_vars/all.yml
# ============================================================
# Configuration du cluster Kubernetes
# ============================================================
cluster_name: smart-city-martinique
k8s_version: "1.28.0"
container_runtime: containerd
network_plugin: cilium
# ============================================================
# Configuration réseau
# ============================================================
domain: digitribe.fr
traefik_namespace: traefik
ingress_class: traefik
# TLS
tls_enabled: true
tls_certresolver: letsencrypt
acme_email: admin@digitribe.fr
# ============================================================
# Storage
# ============================================================
storage_class: nfs-client
nfs_server: "192.168.1.200"
nfs_path: /data/k8s
# Persistent Volume sizes
storage_sizes:
postgres: 50Gi
minio: 500Gi
kafka: 100Gi
influxdb: 50Gi
loki: 100Gi
grafana: 10Gi
jupyterhub: 20Gi
gitea: 20Gi
metabase: 10Gi
superset: 10Gi
mindsdb: 20Gi
odk: 10Gi
mapstore: 10Gi
geoserver: 20Gi
airflow: 20Gi
flink: 20Gi
emqx: 10Gi
mosquitto: 5Gi
redis: 10Gi
elasticsearch: 50Gi
# ============================================================
# Helm Charts versions
# ============================================================
helm_charts:
traefik:
chart: traefik/traefik
version: "28.0.0"
ingress_nginx:
chart: ingress-nginx/ingress-nginx
version: "4.8.0"
cert_manager:
chart: jetstack/cert-manager
version: "1.13.0"
nfs_provisioner:
chart: nfs-subdir-external-provisioner/nfs-subdir-external-provisioner
version: "4.0.18"
postgresql:
chart: bitnami/postgresql
version: "13.2.0"
postgresql_ha:
chart: bitnami/postgresql-ha
version: "12.2.0"
redis:
chart: bitnami/redis
version: "18.0.0"
minio:
chart: bitnami/minio
version: "12.10.0"
kafka:
chart: strimzi/kafka-operator
version: "0.38.0"
flink:
chart: apache/flink-kubernetes-operator
version: "1.7.0"
airflow:
chart: apache/airflow
version: "1.11.0"
grafana:
chart: grafana/grafana
version: "7.0.0"
loki:
chart: grafana/loki-stack
version: "2.9.0"
prometheus:
chart: prometheus/kube-prometheus-stack
version: "51.0.0"
emqx:
chart: emqx/emqx-operator
version: "2.2.0"
mosquitto:
chart: k8s-at-home/mosquitto
version: "4.8.0"
gitea:
chart: gitea/gitea
version: "9.0.0"
jupyterhub:
chart: jupyterhub/jupyterhub
version: "3.0.0"
superset:
chart: apache/superset
version: "0.11.0"
metabase:
chart: bitnami/metabase
version: "0.13.0"
mindsdb:
chart: bitnami/mindsdb
version: "0.1.0"
odk:
chart: odk/odk-central
version: "1.0.0"
mapstore:
chart: geosolutionsit/mapstore
version: "1.0.0"
geoserver:
chart: kartoza/geoserver
version: "2.2.0"
frost:
chart: fraunhoferiosb/frost-server
version: "1.0.0"
nodered:
chart: k8s-at-home/node-red
version: "4.8.0"
phpipam:
chart: phpipam/phpipam
version: "1.0.0"
clickhouse:
chart: bitnami/clickhouse
version: "4.0.0"
starrocks:
chart: starrocks/starrocks-community
version: "1.0.0"
trino:
chart: trinodb/trino
version: "0.10.0"
deltalake:
chart: delta-io/delta-lake
version: "1.0.0"
streamlit:
chart: streamlit/streamlit
version: "1.0.0"
duckdb:
chart: duckdb/duckdb
version: "1.0.0"
elasticsearch:
chart: elastic/elasticsearch
version: "8.11.0"
kibana:
chart: elastic/kibana
version: "8.11.0"
# ============================================================
# Services configuration
# ============================================================
services:
airflow:
enabled: true
namespace: airflow
replicas: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
kafka:
enabled: true
namespace: kafka
replicas: 3
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
flink:
enabled: true
namespace: flink
replicas: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
emqx:
enabled: true
namespace: iot
replicas: 3
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
mosquitto:
enabled: true
namespace: iot
replicas: 2
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
postgresql:
enabled: true
namespace: default
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
redis:
enabled: true
namespace: default
replicas: 3
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
minio:
enabled: true
namespace: default
replicas: 4
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
grafana:
enabled: true
namespace: monitoring
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
loki:
enabled: true
namespace: monitoring
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
prometheus:
enabled: true
namespace: monitoring
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
gitea:
enabled: true
namespace: gitea
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
jupyterhub:
enabled: true
namespace: jupyterhub
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
superset:
enabled: true
namespace: superset
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
metabase:
enabled: true
namespace: metabase
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
mindsdb:
enabled: true
namespace: mindsdb
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
odk:
enabled: true
namespace: odk
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
mapstore:
enabled: true
namespace: mapstore
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
geoserver:
enabled: true
namespace: geoserver
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
frost:
enabled: true
namespace: iot
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
nodered:
enabled: true
namespace: iot
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
phpipam:
enabled: true
namespace: phpipam
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
smartapp:
enabled: true
namespace: smartapp
replicas: 2
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
clickhouse:
enabled: true
namespace: clickhouse
replicas: 1
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
starrocks:
enabled: true
namespace: starrocks
replicas: 1
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
trino:
enabled: true
namespace: trino
replicas: 1
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
deltalake:
enabled: true
namespace: deltalake
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
streamlit:
enabled: true
namespace: streamlit
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
duckdb:
enabled: true
namespace: duckdb
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
# ============================================================
# Monitoring
# ============================================================
monitoring:
enabled: true
namespace: monitoring
grafana_admin_password: "{{ vault_grafana_password }}"
prometheus_retention: 30d
loki_retention: 30d
# ============================================================
# Backup
# ============================================================
backup:
enabled: true
schedule: "0 2 * * *"
retention: 30
storage_class: nfs-client
storage_size: 100Gi

View File

@@ -0,0 +1,60 @@
---
# Vault Ansible - Variables chiffrées
# Fichier: group_vars/vault.yml
# Chiffrer avec: ansible-vault encrypt group_vars/vault.yml
# PostgreSQL
vault_postgres_password: "Digitribe972"
vault_postgres_repmgr_password: "Digitribe972"
# Redis
vault_redis_password: "Digitribe972"
# MinIO
vault_minio_root_user: "minioadmin"
vault_minio_root_password: "Digitribe972"
# Grafana
vault_grafana_admin_password: "Digitribe972"
# Airflow
vault_airflow_fernet_key: "Digitribe972SecretKeyForAirflow2024"
vault_airflow_admin_password: "Digitribe972"
# Gitea
vault_gitea_admin_password: "Digitribe972"
# Superset
vault_superset_admin_password: "Digitribe972"
vault_superset_db_password: "Digitribe972"
# Metabase
vault_metabase_db_password: "Digitribe972"
# MindsDB
vault_mindsdb_password: "Digitribe972"
# ClickHouse
vault_clickhouse_password: "Digitribe972"
# Trino
vault_trino_db_password: "Digitribe972"
# MQTT
vault_mosquitto_password: "Digitribe972"
vault_emqx_admin_password: "Digitribe972"
# phpIPAM
vault_phpipam_admin_password: "Digitribe972"
# ODK
vault_odk_admin_password: "Digitribe972"
# GeoServer
vault_geoserver_admin_password: "Digitribe972"
# MapStore
vault_mapstore_admin_password: "Digitribe972"
# StarRocks
vault_starrocks_root_password: "Digitribe972"

79
helms/inventory/hosts.yml Normal file
View File

@@ -0,0 +1,79 @@
---
# Inventory pour le déploiement Kubernetes via Ansible
# Fichier: inventory/hosts.yml
all:
children:
k8s_masters:
hosts:
k8s-master-1:
ansible_host: "{{ k8s_master_ip | default('192.168.1.100') }}"
ansible_user: "{{ k8s_user | default('root') }}"
k8s_workers:
hosts:
k8s-worker-1:
ansible_host: "{{ k8s_worker1_ip | default('192.168.1.101') }}"
ansible_user: "{{ k8s_user | default('root') }}"
k8s-worker-2:
ansible_host: "{{ k8s_worker2_ip | default('192.168.1.102') }}"
ansible_user: "{{ k8s_user | default('root') }}"
nfs_server:
hosts:
nfs-1:
ansible_host: "{{ nfs_server_ip | default('192.168.1.200') }}"
ansible_user: "{{ nfs_user | default('root') }}"
vars:
# Configuration globale
cluster_name: smart-city-martinique
k8s_version: "1.28"
container_runtime: containerd
network_plugin: cilium
domain: digitribe.fr
# Namespaces Kubernetes
namespaces:
- airflow
- kafka
- flink
- monitoring
- iot
- gitea
- jupyterhub
- odk
- smartapp
- superset
- metabase
- mindsdb
- mapstore
- geoserver
- frost
- nodered
- phpipam
- traefik
- ingress-nginx
- clickhouse
- starrocks
- trino
- deltalake
- streamlit
- duckdb
# Storage
storage_class: nfs-client
nfs_path: /data/k8s
# Helm repositories
helm_repos:
- name: bitnami
url: https://charts.bitnami.com/bitnami
- name: apache
url: https://charts.apache.org
- name: grafana
url: https://grafana.github.io/helm-charts
- name: prometheus
url: https://prometheus-community.github.io/helm-charts
- name: strimzi
url: https://strimzi.io/charts/
- name: flink-operator
url: https://downloads.apache.org/flink/flink-kubernetes-operator-1.7.0/

View File

@@ -0,0 +1,19 @@
---
# Role: airflow
# Valeurs par défaut pour Apache Airflow
# Réplicas des workers Airflow
services:
airflow:
replicas: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
# Stockage des logs Airflow
storage_sizes:
airflow: "20Gi"

View File

@@ -0,0 +1,13 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Apache Airflow for workflow orchestration on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases
- role: kafka

View File

@@ -0,0 +1,34 @@
---
# Role: airflow
# Déploie Apache Airflow
- name: Installer Airflow
kubernetes.core.helm:
name: airflow
chart_ref: "{{ helm_charts.airflow.chart }}"
release_namespace: airflow
create_namespace: true
values:
executor: CeleryExecutor
fernetKey: "{{ vault_airflow_fernet_key }}"
webserver:
defaultUser:
username: admin
password: "{{ vault_airflow_admin_password }}"
dags:
persistence:
enabled: true
size: 10Gi
logs:
persistence:
enabled: true
size: "{{ storage_sizes.airflow }}"
scheduler:
resources: "{{ services.airflow.resources }}"
webserver:
resources: "{{ services.airflow.resources }}"
workers:
replicas: "{{ services.airflow.replicas }}"
resources: "{{ services.airflow.resources }}"
triggerer:
resources: "{{ services.airflow.resources }}"

View File

@@ -0,0 +1,8 @@
---
# Role: backup
# Valeurs par défaut pour les sauvegardes Velero
# Planification des sauvegardes (cron format)
backup:
schedule: "0 2 * * *"
retention: "168" # 7 jours en heures

View File

@@ -0,0 +1,11 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Velero backup and disaster recovery solution on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies: []

View File

@@ -0,0 +1,34 @@
---
# Role: backup
# Configure les sauvegardes Velero
- name: Installer Velero
kubernetes.core.helm:
name: velero
chart_ref: vmware-tanzu/velero
release_namespace: velero
create_namespace: true
values:
configuration:
backupStorageLocation:
- name: default
provider: aws
bucket: smart-city-backup
config:
region: eu-west-3
s3ForcePathStyle: true
schedules:
daily:
schedule: "{{ backup.schedule }}"
template:
includedNamespaces:
- "{{ item }}"
snapshotVolumes: true
ttl: "{{ backup.retention }}h0m0s"
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"

View File

@@ -0,0 +1,23 @@
---
# Role: bi
# Valeurs par défaut pour Superset et Metabase
services:
superset:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
metabase:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Business Intelligence tools on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,44 @@
---
# Role: bi
# Déploie Superset et Metabase
- name: Installer Superset
kubernetes.core.helm:
name: superset
chart_ref: "{{ helm_charts.superset.chart }}"
release_namespace: superset
create_namespace: true
values:
supersetNode:
connections:
redis_password: "{{ vault_redis_password }}"
db_user: superset
db_pass: "{{ vault_superset_db_password }}"
resources: "{{ services.superset.resources }}"
supersetWorker:
replicas: 2
resources: "{{ services.superset.resources }}"
bootstrapScript: |
#!/bin/bash
pip install psycopg2-binary redis
init:
adminUser:
username: admin
password: "{{ vault_superset_admin_password }}"
email: admin@digitribe.fr
- name: Installer Metabase
kubernetes.core.helm:
name: metabase
chart_ref: "{{ helm_charts.metabase.chart }}"
release_namespace: metabase
create_namespace: true
values:
database:
type: postgres
host: postgresql-ha-pgpool.default.svc.cluster.local
port: 5432
dbname: metabase
username: metabase
password: "{{ vault_metabase_db_password }}"
resources: "{{ services.metabase.resources }}"

View File

@@ -0,0 +1,6 @@
---
# Role: cert-manager
# Valeurs par défaut pour cert-manager
# Email pour les certificats Let's Encrypt
acme_email: "admin@digitribe.fr"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy cert-manager for automated TLS certificate management on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: traefik

View File

@@ -0,0 +1,39 @@
---
# Role: cert-manager
# Déploie cert-manager pour la gestion des certificats TLS
- name: Installer cert-manager
kubernetes.core.helm:
name: cert-manager
chart_ref: "{{ helm_charts.cert_manager.chart }}"
chart_version: "{{ helm_charts.cert_manager.version }}"
release_namespace: cert-manager
create_namespace: true
values:
installCRDs: true
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
- name: Créer le ClusterIssuer Let's Encrypt
kubernetes.core.k8s:
state: present
definition:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: "{{ acme_email }}"
privateKeySecretRef:
name: letsencrypt-key
solvers:
- http01:
ingress:
class: traefik

View File

@@ -0,0 +1,17 @@
---
# Role: clickhouse
# Valeurs par défaut pour ClickHouse
services:
clickhouse:
replicas: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
storage_sizes:
clickhouse: "50Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy ClickHouse columnar database for analytics on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,34 @@
---
# Role: clickhouse
# Déploie ClickHouse
- name: Installer ClickHouse
kubernetes.core.helm:
name: clickhouse
chart_ref: "{{ helm_charts.clickhouse.chart }}"
chart_version: "{{ helm_charts.clickhouse.version }}"
release_namespace: clickhouse
create_namespace: true
values:
shards: 1
replicaCount: "{{ services.clickhouse.replicas }}"
persistence:
size: "{{ storage_sizes.clickhouse | default('50Gi') }}"
storageClass: "{{ storage_class }}"
resources: "{{ services.clickhouse.resources }}"
auth:
username: default
password: "{{ vault_clickhouse_password }}"
service:
type: ClusterIP
ingress:
enabled: true
hosts:
- host: clickhouse.digitribe.fr
paths:
- path: /
pathType: Prefix
tls:
- secretName: clickhouse-tls
hosts:
- clickhouse.digitribe.fr

View File

@@ -0,0 +1,27 @@
---
# Role: databases
# Valeurs par défaut pour PostgreSQL, Redis et MinIO
services:
postgresql:
replicas: 2
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
# Stockages
storage_sizes:
postgresql: "50Gi"
redis: "10Gi"
minio: "100Gi"
# Mots de passe Vault (valeurs DUMMY — overridés par group_vars/vault.yml)
vault_postgres_password: "DUMMY_POSTGRES_PASSWORD"
vault_postgres_repmgr_password: "DUMMY_REPMGR_PASSWORD"
vault_redis_password: "DUMMY_REDIS_PASSWORD"
vault_minio_root_user: "DUMMY_MINIO_USER"
vault_minio_root_password: "DUMMY_MINIO_PASSWORD"

View File

@@ -0,0 +1,13 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy and manage core database services (PostgreSQL, MySQL, Redis) on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: storage
- role: cert-manager

View File

@@ -0,0 +1,54 @@
---
# Role: databases
# Déploie PostgreSQL, Redis et MinIO
- name: Installer PostgreSQL HA
kubernetes.core.helm:
name: postgresql
chart_ref: "{{ helm_charts.postgresql_ha.chart }}"
release_namespace: default
values:
postgresql:
password: "{{ vault_postgres_password }}"
repmgrPassword: "{{ vault_postgres_repmgr_password }}"
persistence:
size: "{{ storage_sizes.postgresql }}"
storageClass: "{{ storage_class }}"
resources:
requests:
cpu: "{{ services.postgresql.resources.requests.cpu }}"
memory: "{{ services.postgresql.resources.requests.memory }}"
- name: Installer Redis Cluster
kubernetes.core.helm:
name: redis
chart_ref: "{{ helm_charts.redis.chart }}"
release_namespace: default
values:
cluster:
nodes: 3
password: "{{ vault_redis_password }}"
persistence:
size: "{{ storage_sizes.redis }}"
storageClass: "{{ storage_class }}"
resources:
requests:
cpu: "100m"
memory: "256Mi"
- name: Installer MinIO
kubernetes.core.helm:
name: minio
chart_ref: "{{ helm_charts.minio.chart }}"
release_namespace: default
values:
auth:
rootUser: "{{ vault_minio_root_user }}"
rootPassword: "{{ vault_minio_root_password }}"
persistence:
size: "{{ storage_sizes.minio }}"
storageClass: "{{ storage_class }}"
resources:
requests:
cpu: "250m"
memory: "512Mi"

View File

@@ -0,0 +1,17 @@
---
# Role: deltalake
# Valeurs par défaut pour Delta Lake
services:
deltalake:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
storage_sizes:
deltalake: "100Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Delta Lake storage layer for data lakehouse architecture on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,30 @@
---
# Role: deltalake
# Déploie Delta Lake
- name: Installer Delta Lake
kubernetes.core.helm:
name: deltalake
chart_ref: "{{ helm_charts.deltalake.chart }}"
chart_version: "{{ helm_charts.deltalake.version }}"
release_namespace: deltalake
create_namespace: true
values:
replicaCount: "{{ services.deltalake.replicas }}"
resources: "{{ services.deltalake.resources }}"
storage:
size: "{{ storage_sizes.deltalake | default('100Gi') }}"
storageClass: "{{ storage_class }}"
service:
type: ClusterIP
ingress:
enabled: true
hosts:
- host: deltalake.digitribe.fr
paths:
- path: /
pathType: Prefix
tls:
- secretName: deltalake-tls
hosts:
- deltalake.digitribe.fr

View File

@@ -0,0 +1,17 @@
---
# Role: duckdb
# Valeurs par défaut pour DuckDB
services:
duckdb:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
storage_sizes:
duckdb: "50Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy DuckDB embedded analytical database on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,30 @@
---
# Role: duckdb
# Déploie DuckDB
- name: Installer DuckDB
kubernetes.core.helm:
name: duckdb
chart_ref: "{{ helm_charts.duckdb.chart }}"
chart_version: "{{ helm_charts.duckdb.version }}"
release_namespace: duckdb
create_namespace: true
values:
replicaCount: "{{ services.duckdb.replicas }}"
resources: "{{ services.duckdb.resources }}"
storage:
size: "{{ storage_sizes.duckdb | default('50Gi') }}"
storageClass: "{{ storage_class }}"
service:
type: ClusterIP
ingress:
enabled: true
hosts:
- host: duckdb.digitribe.fr
paths:
- path: /
pathType: Prefix
tls:
- secretName: duckdb-tls
hosts:
- duckdb.digitribe.fr

View File

@@ -0,0 +1,14 @@
---
# Role: flink
# Valeurs par défaut pour Apache Flink
services:
flink:
replicas: 2
resources:
requests:
cpu: "1000m"
memory: "2Gi"
limits:
cpu: "2000m"
memory: "4Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Apache Flink for stream processing on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: kafka

View File

@@ -0,0 +1,18 @@
---
# Role: flink
# Déploie Apache Flink via l'opérateur
- name: Installer l'opérateur Flink
kubernetes.core.helm:
name: flink-kubernetes-operator
chart_ref: "{{ helm_charts.flink.chart }}"
release_namespace: flink
create_namespace: true
- name: Créer le déploiement Flink
kubernetes.core.k8s:
state: present
template: flink-deployment.yml.j2
vars:
flink_namespace: flink
flink_replicas: "{{ services.flink.replicas }}"

View File

@@ -0,0 +1,140 @@
---
# Role: flink
# Template: flink-deployment.yml.j2
# Déploiement d'un cluster Apache Flink via FlinkKubernetesOperator
# Variables:
# {{ flink_namespace }} - Namespace Kubernetes (défaut: flink)
# {{ flink_replicas }} - Nombre de TaskManagers (défaut: 2)
---
apiVersion: v1
kind: Namespace
metadata:
name: {{ flink_namespace | default('flink') }}
labels:
app: flink
version: "1.18"
---
apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
name: flink-cluster
namespace: {{ flink_namespace | default('flink') }}
labels:
app: flink
version: "1.18"
spec:
image: flink:1.18-scala_2.12
flinkVersion: v1_18
imagePullPolicy: IfNotPresent
# --- JobManager ---
jobmanager:
resource:
memory: "2048m"
cpu: 1
replicas: 1
# --- TaskManager ---
taskmanager:
resource:
memory: "4096m"
cpu: 2
replicas: {{ flink_replicas | default(2) }}
# --- Configuration Flink ---
flinkConfiguration:
taskmanager.numberOfTaskSlots: "2"
state.backend: rocksdb
state.checkpoints.dir: s3://flink-checkpoints
state.savepoints.dir: s3://flink-savepoints
high-availability: zookeeper
high-availability.zookeeper.quorum: zk-cs.{{ flink_namespace | default('flink') }}.svc.cluster.local:2181
web.upload.dir: /tmp/flink-web-upload
---
apiVersion: v1
kind: Service
metadata:
name: flink-jobmanager
namespace: {{ flink_namespace | default('flink') }}
labels:
app: flink
component: jobmanager
version: "1.18"
spec:
type: ClusterIP
selector:
app: flink
component: jobmanager
ports:
- name: rpc
port: 6123
targetPort: 6123
protocol: TCP
- name: blob
port: 6124
targetPort: 6124
protocol: TCP
- name: webui
port: 8081
targetPort: 8081
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: flink-taskmanager
namespace: {{ flink_namespace | default('flink') }}
labels:
app: flink
component: taskmanager
version: "1.18"
spec:
type: ClusterIP
selector:
app: flink
component: taskmanager
ports:
- name: rpc
port: 6122
targetPort: 6122
protocol: TCP
- name: data
port: 6125
targetPort: 6125
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: flink-webui
namespace: {{ flink_namespace | default('flink') }}
labels:
app: flink
component: webui
version: "1.18"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- flink.digitribe.fr
secretName: flink-tls
rules:
- host: flink.digitribe.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: flink-jobmanager
port:
number: 8081

View File

@@ -0,0 +1,36 @@
---
# Role: gis
# Valeurs par défaut pour MapStore, GeoServer et FROST
services:
mapstore:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
geoserver:
replicas: 1
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
frost:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
storage_sizes:
mapstore: "10Gi"
geoserver: "20Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Geographic Information System (GIS) services on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,37 @@
---
# Role: gis
# Déploie MapStore, GeoServer et FROST
- name: Installer MapStore
kubernetes.core.helm:
name: mapstore
chart_ref: "{{ helm_charts.mapstore.chart }}"
release_namespace: mapstore
create_namespace: true
values:
persistence:
size: "{{ storage_sizes.mapstore }}"
resources: "{{ services.mapstore.resources }}"
- name: Installer GeoServer
kubernetes.core.helm:
name: geoserver
chart_ref: "{{ helm_charts.geoserver.chart }}"
release_namespace: geoserver
create_namespace: true
values:
persistence:
geodataDir:
storageClass: "{{ storage_class }}"
size: "{{ storage_sizes.geoserver }}"
resources: "{{ services.geoserver.resources }}"
- name: Installer FROST
kubernetes.core.helm:
name: frost
chart_ref: "{{ helm_charts.frost.chart }}"
release_namespace: iot
values:
persistence:
size: 10Gi
resources: "{{ services.frost.resources }}"

View File

@@ -0,0 +1,17 @@
---
# Role: gitea
# Valeurs par défaut pour Gitea
services:
gitea:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
storage_sizes:
gitea: "20Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Gitea - self-hosted Git service on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,28 @@
---
# Role: gitea
# Déploie Gitea
- name: Installer Gitea
kubernetes.core.helm:
name: gitea
chart_ref: "{{ helm_charts.gitea.chart }}"
release_namespace: gitea
create_namespace: true
values:
gitea:
admin:
username: eric
password: "{{ vault_gitea_admin_password }}"
email: admin@digitribe.fr
config:
server:
DOMAIN: gitea.digitribe.fr
ROOT_URL: https://gitea.digitribe.fr
SSH_DOMAIN: gitea.digitribe.fr
SSH_PORT: 22
persistence:
enabled: true
size: "{{ storage_sizes.gitea }}"
postgresql:
enabled: true
resources: "{{ services.gitea.resources }}"

View File

@@ -0,0 +1,27 @@
---
# Role: iot
# Valeurs par défaut pour EMQX et Mosquitto
services:
emqx:
replicas: 2
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
mosquitto:
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
storage_sizes:
emqx: "10Gi"
mosquitto: "5Gi"

View File

@@ -0,0 +1,13 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy IoT platform services on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases
- role: kafka

View File

@@ -0,0 +1,35 @@
---
# Role: iot
# Déploie les brokers MQTT
- name: Installer EMQX
kubernetes.core.helm:
name: emqx
chart_ref: "{{ helm_charts.emqx.chart }}"
release_namespace: iot
create_namespace: true
values:
replicaCount: "{{ services.emqx.replicas }}"
persistence:
enabled: true
size: "{{ storage_sizes.emqx }}"
resources: "{{ services.emqx.resources }}"
- name: Installer Mosquitto
kubernetes.core.helm:
name: mosquitto
chart_ref: "{{ helm_charts.mosquitto.chart }}"
release_namespace: iot
values:
replicaCount: "{{ services.mosquitto.replicas }}"
persistence:
enabled: true
size: "{{ storage_sizes.mosquitto }}"
resources: "{{ services.mosquitto.resources }}"
config: |
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd
auth:
password: "{{ vault_mosquitto_password }}"

View File

@@ -0,0 +1,17 @@
---
# Role: jupyterhub
# Valeurs par défaut pour JupyterHub
services:
jupyterhub:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
storage_sizes:
jupyterhub: "20Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy JupyterHub for multi-user notebook environments on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,31 @@
---
# Role: jupyterhub
# Déploie JupyterHub
- name: Installer JupyterHub
kubernetes.core.helm:
name: hub
chart_ref: "{{ helm_charts.jupyterhub.chart }}"
release_namespace: jupyterhub
create_namespace: true
values:
hub:
config:
Authenticator:
admin_users:
- eric
JupyterHub:
admin_access: true
db:
pvc:
storage: "{{ storage_sizes.jupyterhub }}"
singleuser:
storage:
capacity: "{{ storage_sizes.jupyterhub }}"
dynamic:
pvcNameTemplate: "jupyterhub-{userid}"
volumeNameTemplate: "jupyterhub-{userid}"
storageClass: "{{ storage_class }}"
proxy:
service:
type: ClusterIP

View File

@@ -0,0 +1,17 @@
---
# Role: kafka
# Valeurs par défaut pour Kafka (Strimzi)
services:
kafka:
replicas: 3
resources:
requests:
cpu: "1000m"
memory: "2Gi"
limits:
cpu: "2000m"
memory: "4Gi"
storage_sizes:
kafka: "100Gi"

View File

@@ -0,0 +1,13 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy and manage Apache Kafka cluster on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: storage
- role: cert-manager

View File

@@ -0,0 +1,19 @@
---
# Role: kafka
# Déploie Kafka via l'opérateur Strimzi
- name: Installer l'opérateur Strimzi
kubernetes.core.helm:
name: strimzi-kafka-operator
chart_ref: "{{ helm_charts.kafka.chart }}"
release_namespace: kafka
create_namespace: true
- name: Créer le cluster Kafka
kubernetes.core.k8s:
state: present
template: kafka-cluster.yml.j2
vars:
kafka_namespace: kafka
kafka_replicas: "{{ services.kafka.replicas }}"
kafka_storage_size: "{{ storage_sizes.kafka }}"

View File

@@ -0,0 +1,295 @@
---
# Role: kafka
# Template: kafka-cluster.yml.j2
# Cluster Kafka via Strimzi KafkaOperator
# Variables:
# {{ kafka_namespace }} - Namespace Kubernetes (défaut: kafka)
# {{ kafka_replicas }} - Nombre de brokers Kafka (défaut: 3)
# {{ kafka_storage_size }} - Taille du stockage par broker (défaut: 100Gi)
---
apiVersion: v1
kind: Namespace
metadata:
name: {{ kafka_namespace | default('kafka') }}
labels:
app: kafka
version: "3.6"
---
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: kafka-cluster
namespace: {{ kafka_namespace | default('kafka') }}
labels:
app: kafka
version: "3.6"
spec:
kafka:
version: 3.6.0
replicas: {{ kafka_replicas | default(3) }}
listeners:
- name: plain
port: 9092
type: internal
tls: false
- name: tls
port: 9093
type: internal
tls: true
- name: external
port: 9094
type: ingress
tls: true
configuration:
bootstrap:
host: kafka-bootstrap.digitribe.fr
brokers:
- broker: 0
host: kafka-broker-0.digitribe.fr
- broker: 1
host: kafka-broker-1.digitribe.fr
- broker: 2
host: kafka-broker-2.digitribe.fr
config:
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 2
default.replication.factor: 3
min.insync.replicas: 2
inter.broker.protocol.version: "3.6"
log.message.format.version: "3.6"
storage:
type: jbod
volumes:
- id: 0
type: persistent-claim
size: {{ kafka_storage_size | default('100Gi') }}
class: standard
deleteClaim: false
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
livenessProbe:
initialDelaySeconds: 30
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 10
timeoutSeconds: 5
metricsConfig:
type: jmxPrometheusExporter
valueFrom:
configMapKeyRef:
name: kafka-metrics
key: kafka-metrics-config.yml
template:
pod:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: strimzi.io/name
operator: In
values:
- kafka-cluster-kafka
topologyKey: kubernetes.io/hostname
zookeeper:
replicas: 3
storage:
type: persistent-claim
size: 20Gi
class: standard
deleteClaim: false
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
livenessProbe:
initialDelaySeconds: 30
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 10
timeoutSeconds: 5
entityOperator:
topicOperator:
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
userOperator:
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
kafkaExporter:
topicRegex: ".*"
groupRegex: ".*"
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: kafka-metrics
namespace: {{ kafka_namespace | default('kafka') }}
labels:
app: kafka
version: "3.6"
data:
kafka-metrics-config.yml: |
# See https://github.com/prometheus/jmx_exporter for more info about JMX Prometheus Exporter metrics
lowercaseOutputName: true
rules:
# Special cases and very specific rules
- pattern: kafka.server<type=(.+), name=(.+), clientId=(.+), topic=(.+), partition=(.*)><>Value
name: kafka_server_$1_$2
type: GAUGE
labels:
clientId: "$3"
topic: "$4"
partition: "$5"
- pattern: kafka.server<type=(.+), name=(.+), clientId=(.+), brokerHost=(.+), brokerPort=(.+)><>Value
name: kafka_server_$1_$2
type: GAUGE
labels:
clientId: "$3"
broker: "$4:$5"
# Generic per-second counters with 0-2 key/value pairs
- pattern: kafka.(\w+)<type=(.+), name=(.+)PerSec\w*, (.+)=(.+), (.+)=(.+)><>Count
name: kafka_$1_$2_$3_total
type: COUNTER
labels:
"$4": "$5"
"$6": "$7"
- pattern: kafka.(\w+)<type=(.+), name=(.+)PerSec\w*, (.+)=(.+)><>Count
name: kafka_$1_$2_$3_total
type: COUNTER
labels:
"$4": "$5"
- pattern: kafka.(\w+)<type=(.+), name=(.+)PerSec\w*><>Count
name: kafka_$1_$2_$3_total
type: COUNTER
# Generic gauges with 0-2 key/value pairs
- pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+), (.+)=(.+)><>Value
name: kafka_$1_$2_$3
type: GAUGE
labels:
"$4": "$5"
"$6": "$7"
- pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+)><>Value
name: kafka_$1_$2_$3
type: GAUGE
labels:
"$4": "$5"
- pattern: kafka.(\w+)<type=(.+), name=(.+)><>Value
name: kafka_$1_$2_$3
type: GAUGE
# Emulate Prometheus 'Summary' metrics for the exported 'Histogram's
- pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+), (.+)=(.+)><>Count
name: kafka_$1_$2_$3_count
type: COUNTER
labels:
"$4": "$5"
"$6": "$7"
- pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.*), (.+)=(.+)><>(\d+)thPercentile
name: kafka_$1_$2_$3
type: SUMMARY
labels:
"$4": "$5"
"$6": "$7"
quantile: 0.95
- pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+)><>Count
name: kafka_$1_$2_$3_count
type: COUNTER
labels:
"$4": "$5"
- pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.*)><>(\d+)thPercentile
name: kafka_$1_$2_$3
type: SUMMARY
labels:
"$4": "$5"
quantile: 0.95
- pattern: kafka.(\w+)<type=(.+), name=(.+)><>Count
name: kafka_$1_$2_$3_count
type: COUNTER
---
apiVersion: v1
kind: Service
metadata:
name: kafka-bootstrap
namespace: {{ kafka_namespace | default('kafka') }}
labels:
app: kafka
component: bootstrap
version: "3.6"
spec:
type: ClusterIP
selector:
strimzi.io/cluster: kafka-cluster
strimzi.io/name: kafka-cluster-kafka
ports:
- name: tcp-internal
port: 9092
targetPort: 9092
protocol: TCP
- name: tcp-tls
port: 9093
targetPort: 9093
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kafka-external
namespace: {{ kafka_namespace | default('kafka') }}
labels:
app: kafka
component: external
version: "3.6"
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "TCP"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- kafka-bootstrap.digitribe.fr
secretName: kafka-bootstrap-tls
rules:
- host: kafka-bootstrap.digitribe.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kafka-cluster-kafka-external-bootstrap
port:
number: 9094

View File

@@ -0,0 +1,17 @@
---
# Role: mindsdb
# Valeurs par défaut pour MindsDB
services:
mindsdb:
replicas: 1
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
storage_sizes:
mindsdb: "20Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy MindsDB - open-source AI/ML database on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,18 @@
---
# Role: mindsdb
# Déploie MindsDB
- name: Installer MindsDB
kubernetes.core.helm:
name: mindsdb
chart_ref: "{{ helm_charts.mindsdb.chart }}"
release_namespace: mindsdb
create_namespace: true
values:
mindsdb:
auth:
username: admin
password: "{{ vault_mindsdb_password }}"
storage:
size: "{{ storage_sizes.mindsdb }}"
resources: "{{ services.mindsdb.resources }}"

View File

@@ -0,0 +1,12 @@
---
# Role: monitoring
# Valeurs par défaut pour Prometheus, Grafana, Loki et Promtail
monitoring:
prometheus_retention: "30d"
grafana_admin_password: "DUMMY_GRAFANA_ADMIN_PASSWORD"
storage_sizes:
prometheus: "50Gi"
grafana: "10Gi"
loki: "50Gi"

View File

@@ -0,0 +1,13 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy monitoring stack (Prometheus, Grafana, Alertmanager) on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: storage
- role: cert-manager

View File

@@ -0,0 +1,41 @@
---
# Role: monitoring
# Déploie Prometheus, Grafana, Loki et Promtail
- name: Installer kube-prometheus-stack
kubernetes.core.helm:
name: prometheus
chart_ref: "{{ helm_charts.prometheus.chart }}"
release_namespace: monitoring
create_namespace: true
values:
prometheus:
prometheusSpec:
retention: "{{ monitoring.prometheus_retention }}"
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: "{{ storage_class }}"
resources:
requests:
storage: "{{ storage_sizes.prometheus }}"
grafana:
adminPassword: "{{ monitoring.grafana_admin_password }}"
persistence:
enabled: true
size: "{{ storage_sizes.grafana }}"
alertmanager:
enabled: false
- name: Installer Loki Stack
kubernetes.core.helm:
name: loki
chart_ref: "{{ helm_charts.loki.chart }}"
release_namespace: monitoring
values:
loki:
persistence:
enabled: true
size: "{{ storage_sizes.loki }}"
promtail:
enabled: true

View File

@@ -0,0 +1,5 @@
---
# Role: namespaces
# Crée les namespaces Kubernetes
# Les namespaces sont définis dans group_vars (variable: namespaces)
# Aucune variable custom supplémentaire requise pour ce rôle.

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Create and manage Kubernetes namespaces for the platform
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: prerequisites

View File

@@ -0,0 +1,17 @@
---
# Role: namespaces
# Crée les namespaces Kubernetes
- name: Créer les namespaces
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ item }}"
labels:
app.kubernetes.io/managed-by: ansible
cluster: "{{ cluster_name }}"
type: kubernetes.io/metadata.v1
loop: "{{ namespaces }}"

View File

@@ -0,0 +1,14 @@
---
# Role: nodered
# Valeurs par défaut pour Node-RED
services:
nodered:
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Node-RED flow-based programming tool on Kubernetes (IoT namespace)
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: iot

View File

@@ -0,0 +1,26 @@
---
# Role: nodered
# Déploie Node-RED
- name: Installer Node-RED
kubernetes.core.helm:
name: nodered
chart_ref: "{{ helm_charts.nodered.chart }}"
release_namespace: iot
values:
replicaCount: "{{ services.nodered.replicas }}"
persistence:
enabled: true
size: 5Gi
resources: "{{ services.nodered.resources }}"
ingress:
enabled: true
hosts:
- host: nodered.digitribe.fr
paths:
- path: /
pathType: Prefix
tls:
- secretName: nodered-tls
hosts:
- nodered.digitribe.fr

View File

@@ -0,0 +1,17 @@
---
# Role: odk
# Valeurs par défaut pour ODK Central
services:
odk:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
storage_sizes:
odk: "20Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy ODK (Open Data Kit) for mobile data collection on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,22 @@
---
# Role: odk
# Déploie ODK Central
- name: Installer ODK Central
kubernetes.core.helm:
name: odk-central
chart_ref: "{{ helm_charts.odk.chart }}"
release_namespace: odk
create_namespace: true
values:
backend:
replicaCount: 1
resources: "{{ services.odk.resources }}"
frontend:
replicaCount: 1
resources: "{{ services.odk.resources }}"
postgres:
enabled: true
storage: "{{ storage_sizes.odk }}"
redis:
enabled: true

View File

@@ -0,0 +1,14 @@
---
# Role: phpipam
# Valeurs par défaut pour phpIPAM
services:
phpipam:
replicas: 1
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy phpIPAM IP address management tool on Kubernetes (IoT namespace)
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: iot

View File

@@ -0,0 +1,27 @@
---
# Role: phpipam
# Déploie phpIPAM
- name: Installer phpIPAM
kubernetes.core.helm:
name: phpipam
chart_ref: "{{ helm_charts.phpipam.chart }}"
release_namespace: phpipam
create_namespace: true
values:
phpipam:
adminPassword: "{{ vault_phpipam_admin_password }}"
persistence:
size: 5Gi
resources: "{{ services.phpipam.resources }}"
ingress:
enabled: true
hosts:
- host: phpipam.digitribe.fr
paths:
- path: /
pathType: Prefix
tls:
- secretName: phpipam-tls
hosts:
- phpipam.digitribe.fr

View File

@@ -0,0 +1,19 @@
---
# Role: prerequisites
# Valeurs par défaut pour les prérequis (repositories Helm)
helm_repos:
- name: stable
url: https://charts.helm.sh/stable
- name: bitnami
url: https://charts.bitnami.com/bitnami
- name: prometheus-community
url: https://prometheus-community.github.io/helm-charts
- name: grafana
url: https://grafana.github.io/helm-charts
- name: traefik
url: https://traefik.github.io/charts
- name: strimzi
url: https://strimzi.io/charts/
- name: jetstack
url: https://charts.jetstack.io

View File

@@ -0,0 +1,11 @@
---
galaxy_info:
author: Eric FELIXINE
description: Prerequisites - Install base tools and dependencies for the Kubernetes platform
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies: []

View File

@@ -0,0 +1,21 @@
---
# Role: prerequisites
# Installe les prérequis sur le cluster Kubernetes
- name: Ajouter les repositories Helm
kubernetes.core.helm_repository:
name: "{{ item.name }}"
repo_url: "{{ item.url }}"
loop: "{{ helm_repos }}"
- name: Mettre à jour les repositories Helm
command: helm repo update
changed_when: false
- name: Installer les CRDs nécessaires
kubernetes.core.k8s:
state: present
src: "{{ item }}"
loop:
- https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.crds.yaml
ignore_errors: true

View File

@@ -0,0 +1,5 @@
---
# Role: smartapp
# Déploiement de l'application Smart City
# Les variables sont définies directement dans les tasks (smartapp_namespace, smartapp_domain).
# Aucune variable custom supplémentaire requise pour ce rôle.

View File

@@ -0,0 +1,13 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy SmartApp intelligent application platform on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases
- role: cert-manager

View File

@@ -0,0 +1,19 @@
---
# Role: smartapp
# Déploie l'application Smart City
- name: Déployer Smart App Web
kubernetes.core.k8s:
state: present
template: smartapp-web.yml.j2
vars:
smartapp_namespace: smartapp
smartapp_domain: smartapp.digitribe.fr
- name: Déployer Smart App API
kubernetes.core.k8s:
state: present
template: smartapp-api.yml.j2
vars:
smartapp_namespace: smartapp
smartapp_domain: api-smartapp.digitribe.fr

View File

@@ -0,0 +1,253 @@
---
# Role: smartapp
# Template: smartapp-api.yml.j2
# Déploiement de l'API backend SmartApp
# Variables:
# {{ smartapp_namespace }} - Namespace Kubernetes (défaut: smartapp)
# {{ smartapp_domain }} - Domaine public (défaut: api-smartapp.digitribe.fr)
---
apiVersion: v1
kind: Namespace
metadata:
name: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: smartapp-api-config
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
data:
APP_ENV: "production"
APP_PORT: "8080"
LOG_LEVEL: "info"
CORS_ORIGINS: "https://smartapp.digitribe.fr"
DATABASE_POOL_SIZE: "10"
REDIS_POOL_SIZE: "5"
---
apiVersion: v1
kind: Secret
metadata:
name: smartapp-api-secrets
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
type: Opaque
stringData:
DATABASE_URL: "postgresql://smartapp:{{ smartapp_db_password | default('changeme') }}@postgres.smartapp.svc.cluster.local:5432/smartapp"
REDIS_URL: "redis://redis.smartapp.svc.cluster.local:6379/0"
JWT_SECRET: "{{ smartapp_jwt_secret | default('change-this-secret-in-production') }}"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: smartapp-api
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
spec:
replicas: 2
selector:
matchLabels:
app: smartapp
component: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: smartapp
component: api
version: "1.0"
spec:
containers:
- name: api
image: digitribe/smartapp-api:{{ smartapp_api_version | default('latest') }}
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
envFrom:
- configMapRef:
name: smartapp-api-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: smartapp-api-secrets
key: DATABASE_URL
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: smartapp-api-secrets
key: REDIS_URL
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: smartapp-api-secrets
key: JWT_SECRET
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /api/v1/health/live
port: http
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/v1/health/ready
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /api/v1/health/live
port: http
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 12
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- smartapp
- key: component
operator: In
values:
- api
topologyKey: kubernetes.io/hostname
---
apiVersion: v1
kind: Service
metadata:
name: smartapp-api
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
spec:
type: ClusterIP
selector:
app: smartapp
component: api
ports:
- name: http
port: 8080
targetPort: 8080
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: smartapp-api
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- {{ smartapp_domain | default('api-smartapp.digitribe.fr') }}
secretName: smartapp-api-tls
rules:
- host: {{ smartapp_domain | default('api-smartapp.digitribe.fr') }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: smartapp-api
port:
number: 8080
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: smartapp-api
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: api
version: "1.0"
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: smartapp-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 25
periodSeconds: 120

View File

@@ -0,0 +1,229 @@
---
# Role: smartapp
# Template: smartapp-web.yml.j2
# Déploiement du frontend web SmartApp (nginx)
# Variables:
# {{ smartapp_namespace }} - Namespace Kubernetes (défaut: smartapp)
# {{ smartapp_domain }} - Domaine public (défaut: smartapp.digitribe.fr)
---
apiVersion: v1
kind: Namespace
metadata:
name: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: web
version: "1.0"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: smartapp-web
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: web
version: "1.0"
spec:
replicas: 2
selector:
matchLabels:
app: smartapp
component: web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: smartapp
component: web
version: "1.0"
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: static-content
mountPath: /usr/share/nginx/html
readOnly: true
volumes:
- name: nginx-config
configMap:
name: smartapp-web-nginx-config
- name: static-content
configMap:
name: smartapp-web-static
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- smartapp
- key: component
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
---
apiVersion: v1
kind: ConfigMap
metadata:
name: smartapp-web-nginx-config
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: web
version: "1.0"
data:
default.conf: |
server {
listen 80;
server_name {{ smartapp_domain | default('smartapp.digitribe.fr') }};
root /usr/share/nginx/html;
index index.html;
# Health check endpoint
location /healthz {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# Static assets with caching
location /static/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: smartapp-web-static
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: web
version: "1.0"
data:
index.html: |
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SmartApp - DigiTribe</title>
</head>
<body>
<h1>SmartApp - DigiTribe</h1>
<p>Frontend web opérationnel.</p>
</body>
</html>
---
apiVersion: v1
kind: Service
metadata:
name: smartapp-web
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: web
version: "1.0"
spec:
type: ClusterIP
selector:
app: smartapp
component: web
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: smartapp-web
namespace: {{ smartapp_namespace | default('smartapp') }}
labels:
app: smartapp
component: web
version: "1.0"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- {{ smartapp_domain | default('smartapp.digitribe.fr') }}
secretName: smartapp-web-tls
rules:
- host: {{ smartapp_domain | default('smartapp.digitribe.fr') }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: smartapp-web
port:
number: 80

View File

@@ -0,0 +1,17 @@
---
# Role: starrocks
# Valeurs par défaut pour StarRocks
services:
starrocks:
replicas: 1
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
storage_sizes:
starrocks: "100Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy StarRocks unified analytics warehouse on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,38 @@
---
# Role: starrocks
# Déploie StarRocks
- name: Ajouter le repository Helm StarRocks
kubernetes.core.helm_repository:
name: starrocks
repo_url: https://starrocks.github.io/starrocks-kubernetes-operator
- name: Installer StarRocks
kubernetes.core.helm:
name: starrocks
chart_ref: "{{ helm_charts.starrocks.chart }}"
chart_version: "{{ helm_charts.starrocks.version }}"
release_namespace: starrocks
create_namespace: true
values:
fe:
replicas: "{{ services.starrocks.replicas }}"
resources: "{{ services.starrocks.resources }}"
storage:
size: "{{ storage_sizes.starrocks | default('50Gi') }}"
storageClass: "{{ storage_class }}"
be:
replicas: 3
resources: "{{ services.starrocks.resources }}"
storage:
size: "{{ storage_sizes.starrocks | default('100Gi') }}"
storageClass: "{{ storage_class }}"
cn:
replicas: 2
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"

View File

@@ -0,0 +1,10 @@
---
# Role: storage
# Valeurs par défaut pour le stockage NFS
# Classe de stockage par défaut
storage_class: "nfs-client"
# Serveur NFS
nfs_server: "10.0.0.1"
nfs_path: "/srv/nfs/k8s"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Provision and manage persistent storage (PVs, PVCs, StorageClasses) on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: namespaces

View File

@@ -0,0 +1,27 @@
---
# Role: storage
# Configure le stockage NFS et les StorageClasses
- name: Créer le namespace storage
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: storage
- name: Installer le NFS provisioner
kubernetes.core.helm:
name: nfs-provisioner
chart_ref: "{{ helm_charts.nfs_provisioner.chart }}"
chart_version: "{{ helm_charts.nfs_provisioner.version }}"
release_namespace: storage
values:
nfs:
server: "{{ nfs_server }}"
path: "{{ nfs_path }}"
storageClass:
name: "{{ storage_class }}"
defaultClass: true
reclaimPolicy: Retain

View File

@@ -0,0 +1,14 @@
---
# Role: streamlit
# Valeurs par défaut pour Streamlit
services:
streamlit:
replicas: 1
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"

View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Eric FELIXINE
description: Deploy Streamlit data application framework on Kubernetes
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Kubernetes
versions:
- "1.28"
dependencies:
- role: databases

View File

@@ -0,0 +1,30 @@
---
# Role: streamlit
# Déploie Streamlit
- name: Installer Streamlit
kubernetes.core.helm:
name: streamlit
chart_ref: "{{ helm_charts.streamlit.chart }}"
chart_version: "{{ helm_charts.streamlit.version }}"
release_namespace: streamlit
create_namespace: true
values:
replicaCount: "{{ services.streamlit.replicas }}"
resources: "{{ services.streamlit.resources }}"
service:
type: ClusterIP
ingress:
enabled: true
hosts:
- host: streamlit.digitribe.fr
paths:
- path: /
pathType: Prefix
tls:
- secretName: streamlit-tls
hosts:
- streamlit.digitribe.fr
env:
STREAMLIT_SERVER_HEADLESS: "true"
STREAMLIT_SERVER_ENABLE_CORS: "false"

Some files were not shown because too many files have changed in this diff Show More