From 7fde58bf6ab338ae176063457a99f5508e639bed Mon Sep 17 00:00:00 2001 From: Eric F Date: Wed, 10 Jun 2026 13:39:47 -0400 Subject: [PATCH] Complete infrastructure: CitrineOS+OpenADR+FM integration, asset sync, scheduling, IaC scripts, K8s manifests, documentation --- docs/INFRASTRUCTURE.md | 306 ++++++++++++++++--------------------- iac/k8s/README.md | 339 +++++++++++++++++++++++++++++++++++++++++ iac/scripts/deploy.sh | 142 +++++++++++++++++ 3 files changed, 609 insertions(+), 178 deletions(-) create mode 100644 iac/k8s/README.md create mode 100644 iac/scripts/deploy.sh diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 76e7e79..0f96b16 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -3,228 +3,178 @@ ## Architecture Overview ``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ CARIFLEX EMS │ -│ Energy Management System for Martinique │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ OpenADR │ │ FlexMeasures│ │ Grafana │ │ -│ │ VTN/VEN │───▶│ Server │───▶│ Dashboard │ │ -│ │ (S2+OpenADR)│ │ (EMS Core) │ │ (14 panels)│ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -│ │ │ │ │ -│ ▼ ▼ ▼ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ PostgreSQL │ │ Redis │ │ Traefik │ │ -│ │ (time-series)│ │ (job queue) │ │ (reverse │ │ -│ │ │ │ │ │ proxy) │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ CARIFLEX EMS │ +│ Energy Management System for Martinique │ +├─────────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ OpenADR │ │ FlexMeasures│ │ Grafana │ │ +│ │ VTN/VEN │───▶│ Server │───▶│ Dashboard │ │ +│ │ (S2+OpenADR)│ │ (EMS Core) │ │ (17 panels)│ │ +│ └──────────────┘ └──────┬───────┘ └──────────────┘ │ +│ │ │ │ +│ │ ▼ │ +│ │ ┌──────────────┐ │ +│ │ │ CitrineOS │ │ +│ │ │ (OCPP 2.0.1)│ │ +│ │ └──────┬───────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ PostgreSQL │ │ RabbitMQ │ │ +│ │ (time-series)│ │ (AMQP/OCPP) │ │ +│ └──────────────┘ └──────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────┘ ``` +## Integration Flow + +### OpenADR → FlexMeasures → CitrineOS +``` +1. OpenADR VTN creates events (price, load control) +2. OpenADR VEN receives events via poll +3. VEN posts data to FlexMeasures API (sensors 84, 86, 87) +4. FlexMeasures creates charging schedules based on prices +5. Schedules sent to CitrineOS via RabbitMQ (OCPP) +6. CitrineOS sends OCPP profiles to EV charge points +7. Grafana displays real-time data from all sources +``` + +### Asset Synchronization +- FM EV assets (sensors 61-70) ↔ CitrineOS charge points (CP001-CP010) +- Sync script ensures consistency of: count, power, location +- OCPP 2.0.1 protocol for charge point communication + ## Network Architecture ### Networks | Network | Subnet | Purpose | External | |---------|--------|---------|----------| -| `traefik-public` | 172.29.0.0/16 | Public services (FM, Grafana) | Yes | -| `openadr-internal` | 192.168.240.0/24 | OpenADR VTN/VEN communication | No | +| `traefik-public` | 172.29.0.0/16 | Public services | Yes | +| `config_cariflex-internal` | 172.23.0.0/16 | Internal services | No | +| `openadr-internal` | 192.168.240.0/24 | OpenADR VTN/VEN | No | ### DNS/Hosts | Service | Domain | Container | Port | |---------|--------|-----------|------| | FlexMeasures | cariflex.digitribe.fr | flexmeasures-server | 5000 | | Grafana | grafana.digitribe.fr | smart-city-grafana | 3000 | +| CitrineOS | citrineos.digitribe.fr | cariflex-citrineos-server | 8080 | +| RabbitMQ Mgmt | amqp.digitribe.fr | cariflex-amqp | 15672 | | OpenADR VTN | - | openadr-vtn | 8080 | | OpenADR VEN | - | openadr-ven | - | -### Firewall (UFW) -- Public ports: 80 (HTTP), 443 (HTTPS), 51820 (WireGuard) -- Internal networks: 172.16.0.0/12, 10.0.0.0/8, 192.168.0.0/16, 192.168.240.0/24 - ## Docker Containers ### Cariflex EMS Stack -| Container | Image | Network | IP | Port | Status | -|-----------|-------|---------|----|---- |--------| -| flexmeasures-server | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | 5000 | ✅ UP | -| flexmeasures-worker | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | - | ✅ UP | -| flexmeasures-db | postgres:17 | traefik-public | 172.29.0.x | 5432 | ✅ UP | -| flexmeasures-redis | redis:7-alpine | traefik-public | 172.29.0.x | 6379 | ✅ UP | -| openadr-vtn | flexmeasures-openadr-vtn | openadr-internal | 192.168.240.10 | 8080 | ✅ UP | -| openadr-ven | flexmeasures-openadr-ven | openadr-internal | 192.168.240.11 | - | ✅ UP | -| smart-city-grafana | grafana/grafana | traefik-public | - | 3000 | ✅ UP | - -## IP Address Allocation (phpIPAM) - -### Cariflex Subnet: 192.168.240.0/24 -| IP | Service | Container | MAC | -|----|---------|-----------|-----| -| 192.168.240.1 | Gateway | - | - | -| 192.168.240.10 | OpenADR VTN | openadr-vtn | - | -| 192.168.240.11 | OpenADR VEN | openadr-ven | - | -| 192.168.240.128-254 | Reserved | - | - | - -### Public Subnet (traefik-public): 172.29.0.0/16 -| IP | Service | Domain | -|----|---------|--------| -| 172.29.0.x | FlexMeasures | cariflex.digitribe.fr | -| 172.29.0.x | Grafana | grafana.digitribe.fr | +| Container | Image | Network | IP | Status | +|-----------|-------|---------|----|--------| +| flexmeasures-server | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | ✅ UP | +| flexmeasures-worker | lfenergy/flexmeasures:latest | traefik-public | 172.29.0.x | ✅ UP | +| flexmeasures-db | postgres:17 | traefik-public | 172.29.0.x | ✅ UP | +| flexmeasures-redis | redis:7-alpine | traefik-public | 172.29.0.x | ✅ UP | +| openadr-vtn | flexmeasures-openadr-vtn | openadr-internal | 192.168.240.10 | ✅ UP | +| openadr-ven | flexmeasures-openadr-ven | openadr-internal | 192.168.240.11 | ✅ UP | +| cariflex-citrineos-server | ghcr.io/citrineos/citrineos-server:latest | config_cariflex-internal | 172.23.0.4 | ✅ UP | +| cariflex-citrineos-db | postgis/postgis:16-3.5 | config_cariflex-internal | 172.23.0.x | ✅ UP | +| cariflex-amqp | rabbitmq:3-management | config_cariflex-internal | 172.23.0.3 | ✅ UP | +| smart-city-grafana | grafana/grafana | traefik-public | - | ✅ UP | ## Sensor Mapping -### FlexMeasures Sensors -| ID | Name | Unit | Type | Source | -|----|------|------|------|--------| -| 41-50 | pv_01..10_power | kW | PV Production | Simulator | -| 51-60 | bat_01..10_power | kWh | Battery SOC | Simulator | -| 61-70 | ev_01..10_power | kWh | EV Charge | Simulator | -| 81 | irradiance | W/m² | Weather | Open-Meteo | -| 82 | temperature | °C | Weather | Open-Meteo | -| 83 | wind_speed | m/s | Weather | Open-Meteo | -| 84 | consumption_price | EUR/MWh | Price | OpenADR VTN | -| 85 | production_price | EUR/MWh | Price | Simulated | -| 86 | load_control_signal | % | DSR | OpenADR VTN | -| 87 | demand_response_signal | dimensionless | DSR | OpenADR VTN | +### OpenADR Sensors (from VTN) +| ID | Name | Unit | Source | +|----|------|------|--------| +| 84 | consumption_price | EUR/MWh | OpenADR VTN | +| 86 | load_control_signal | % | OpenADR VTN | +| 87 | demand_response_signal | dimensionless | OpenADR VTN | -### S2 Resource Mapping -| S2 Resource | FM Sensor | Asset | Min Power | Max Power | -|-------------|-----------|-------|-----------|-----------| -| battery_01-05 | 51-55 | Bat_01-05 | -50 kW | +50 kW | -| ev_01-05 | 61-65 | EV_01-05 | -22 kW | +22 kW | +### EV Charging Sensors (CitrineOS ↔ FM) +| FM Sensor | CitrineOS CP | Connector | Max Power | +|-----------|--------------|-----------|-----------| +| 61 | CP001 | 1 | 22 kW | +| 62 | CP002 | 1 | 22 kW | +| 63 | CP003 | 1 | 22 kW | +| 64 | CP004 | 1 | 22 kW | +| 65 | CP005 | 1 | 22 kW | +| 66 | CP006 | 1 | 11 kW | +| 67 | CP007 | 1 | 11 kW | +| 68 | CP008 | 1 | 11 kW | +| 69 | CP009 | 1 | 11 kW | +| 70 | CP010 | 1 | 11 kW | -## OpenADR Protocol +## Grafana Dashboard (17 panels) -### VTN (Virtual Top Node) -- **Container**: openadr-vtn -- **Port**: 8080 (internal) -- **Profile**: OpenADR 2.0b -- **Events**: ENERGY_PRICE (24h), LOAD_CONTROL (4h) -- **Schedule**: Every 10 seconds poll - -### VEN (Virtual End Node) -- **Container**: openadr-ven -- **VTN URL**: http://192.168.240.10:8080/OpenADR2/Simple/2.0b -- **Registration ID**: reg-Cariflex-VEN-001 -- **Event Handler**: on_event -> FM API (sensors 84, 86) - -### Event Flow -1. VTN creates events (price, load control) -2. VEN polls VTN every 10s -3. VEN receives oadrDistributeEvent -4. VEN sends event responses (optIn) -5. VEN posts data to FM API -6. Grafana displays real-time data - -## S2 Protocol Integration - -### S2 Service -- **Location**: openadr-ven container (background process) -- **Protocol**: S2 (FRBC - Flexibility Resource Bank Control) -- **Mapping**: S2 resources -> FM sensors (51-65) -- **Functions**: - - process_load_control(): Converts OpenADR load control to S2 FRBC instructions - - process_demand_response(): Converts OpenADR demand response to S2 FRBC instructions - -### S2 Message Types -- FRBCInstruction: Power setpoint commands -- FRBCSystemDescription: Resource capabilities -- FRBCActuatorStatus: Resource status feedback -- FRBCStorageStatus: Battery/EV storage status +| # | Panel | Data Source | +|---|-------|-------------| +| 1 | Production PV (kW) | FM sensors 41-50 | +| 2 | Consommation VE (kW) | FM sensors 61-70 | +| 3 | SOC Batteries (kWh) | FM sensors 51-60 | +| 4 | SOC V2G (kWh) | FM sensors 71-80 | +| 5 | Flexibilite Totale (kWh) | Calculated | +| 6 | Impact Reseau (kW) | Calculated | +| 7 | Irradiance & Temperature | FM sensors 81-82 | +| 8 | Prix DSO (EUR/MWh) | FM sensor 85 | +| 9 | DSR Price Signal (EUR/MWh) | FM sensor 84 (OpenADR) | +| 10 | Load Control Signal (%) | FM sensor 86 (OpenADR) | +| 11 | Impact Flexibilite Reseau (kW) | Calculated | +| 12 | Battery Power (kW) | FM sensors 51-60 (S2) | +| 13 | EV Power (kW) | FM sensors 61-70 (S2) | +| 14 | Flexibility Impact (kW) | Calculated (S2) | +| 15 | EV Charging Power (kW) | FM sensors 61-70 (CitrineOS) | +| 16 | Active Charging Sessions | Calculated | +| 17 | EV Flexibility Potential (kWh) | Calculated | ## Backup Strategy ### Automated Backups - **Script**: `/home/eric/cariflex/scripts/full_backup.sh` - **Location**: `/home/eric/backups/cariflex/YYYYMMDD_HHMMSS.tar.gz` -- **Contents**: - - PostgreSQL dump (flexmeasures DB) - - Redis dump (RDB file) - - FM config and templates - - OpenADR scripts and Dockerfiles - - Grafana dashboards (JSON) - - Docker container states - - Network configuration +- **Contents**: PostgreSQL, Redis, FM config, OpenADR scripts, Grafana dashboards, container states, network config -### Backup Retention -- Daily backups kept for 30 days -- Weekly backups kept for 12 weeks -- Monthly backups kept indefinitely +## Deployment -## Deployment Environments +### Docker Compose Files +| File | Purpose | +|------|---------| +| `/home/eric/flexmeasures/docker-compose.yml` | FM core stack | +| `/home/eric/flexmeasures/docker-compose.openadr.yml` | OpenADR VTN/VEN | +| `/home/eric/cariflex/config/docker-compose-citrineos.yml` | CitrineOS stack | -### Development -- **Host**: Local VM or developer machine -- **Docker Compose**: Single node -- **Data**: Simulated -- **URL**: localhost +### Deployment Commands +```bash +# FM stack +cd /home/eric/flexmeasures && docker compose up -d -### Test -- **Host**: Test server -- **Docker Compose**: Single node -- **Data**: Anonymized production data -- **URL**: test.cariflex.digitribe.fr +# OpenADR stack +cd /home/eric/flexmeasures && docker compose -f docker-compose.openadr.yml up -d -### Production -- **Host**: Production server (this deployment) -- **Docker Compose**: Single node (scalable to Swarm/K8s) -- **Data**: Real-time production data -- **URL**: cariflex.digitribe.fr +# CitrineOS stack +cd /home/eric/cariflex/config && docker compose -f docker-compose-citrineos.yml up -d -## Security - -### Credentials -| Service | Username | Password | Location | -|---------|----------|----------|----------| -| FlexMeasures | admin@digitribe.fr | Digitribe972 | FM DB | -| PostgreSQL | flexmeasures | Digitribe972 | PostgreSQL | -| Redis | - | (disabled) | Redis | -| Grafana | admin | admin | Grafana DB | - -### SSL/TLS -- All public services use Let's Encrypt certificates -- Traefik handles SSL termination -- Internal services use HTTP (isolated network) - -## Monitoring - -### Health Checks -- FlexMeasures: https://cariflex.digitribe.fr/health -- Grafana: https://grafana.digitrobe.fr/api/health -- OpenADR VTN: http://192.168.240.10:8080/OpenADR2/Simple/2.0b - -### Logs -- Docker: `docker logs ` -- FM: `/app/flexmeasures/logs/` -- Grafana: `/var/log/grafana/` +# Verify all +docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "flexmeasures|openadr|citrine|grafana" +``` ## Troubleshooting -### Common Issues -1. **Redis ACL error**: `docker exec flexmeasures-redis redis-cli CONFIG SET requirepass ""` -2. **OpenADR connection refused**: Check UFW rules for 192.168.240.0/24 -3. **Grafana no data**: Check datasource UID matches PostgreSQL DS -4. **FM worker not running**: `docker restart flexmeasures-worker` +### OpenADR Issues +- **VEN not connecting**: Check UFW rules for 192.168.240.0/24 +- **No price data**: Check VTN logs: `docker logs openadr-vtn` -### Useful Commands -```bash -# View all containers -docker ps -a | grep -E "flexmeasures|openadr|grafana" +### CitrineOS Issues +- **RabbitMQ connection**: Check network aliases on config_cariflex-internal +- **OCPP communication**: Verify charge point network connectivity -# Check OpenADR communication -docker logs openadr-ven | grep -i "price\|load\|event" - -# Check FM API -curl -s https://cariflex.digitribe.fr/api/v3_0/sensors - -# Database query -docker exec flexmeasures-db psql -U flexmeasures -d flexmeasures -c "SELECT * FROM sensor WHERE id BETWEEN 80 AND 90;" -``` +### FM Scheduling Issues +- **No schedules created**: Check price sensor data (84) +- **Worker not running**: `docker restart flexmeasures-worker` ## References - FlexMeasures: https://flexmeasures.readthedocs.io/ - OpenLEADR: https://openleadr.org/ +- CitrineOS: https://citrineos.github.io/ - S2 Protocol: https://s2-standard.github.io/ -- Grafana: https://grafana.com/docs/ +- OCPP 2.0.1: https://www.openchargealliance.org/protocols/ocpp-201/ diff --git a/iac/k8s/README.md b/iac/k8s/README.md new file mode 100644 index 0000000..4fd1107 --- /dev/null +++ b/iac/k8s/README.md @@ -0,0 +1,339 @@ +# Cariflex EMS - Kubernetes Manifests + +## Namespace +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cariflex + labels: + app: cariflex-ems + environment: production +``` + +## ConfigMap +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: cariflex-config + namespace: cariflex +data: + # FlexMeasures + FM_ENV: "production" + FM_LOG_LEVEL: "WARNING" + FM_DB_HOST: "postgres-service" + FM_REDIS_HOST: "redis-service" + + # OpenADR + VTN_ID: "Cariflex-VTN" + VEN_ID: "Cariflex-VEN" + VTN_PORT: "8080" + + # CitrineOS + CITRINEOS_ENV: "production" + AMQP_HOST: "rabbitmq-service" + AMQP_PORT: "5672" + DB_HOST: "citrineos-postgres-service" +``` + +## Secrets +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: cariflex-secrets + namespace: cariflex +type: Opaque +stringData: + FM_SECRET_KEY: "${FM_SECRET_KEY}" + FM_DB_PASSWORD: "${FM_DB_PASSWORD}" + REDIS_PASSWORD: "${REDIS_PASSWORD}" + CITRINEOS_DB_PASSWORD: "${CITRINEOS_DB_PASSWORD}" + RABBITMQ_PASSWORD: "${RABBITMQ_PASSWORD}" +``` + +## FlexMeasures Deployment +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flexmeasures-server + namespace: cariflex +spec: + replicas: 2 + selector: + matchLabels: + app: flexmeasures-server + template: + metadata: + labels: + app: flexmeasures-server + spec: + containers: + - name: flexmeasures + image: lfenergy/flexmeasures:latest + ports: + - containerPort: 5000 + envFrom: + - configMapRef: + name: cariflex-config + - secretRef: + name: cariflex-secrets + resources: + requests: + memory: "1Gi" + cpu: "500m" + limits: + memory: "2Gi" + cpu: "2" + livenessProbe: + httpGet: + path: /api/v3_0/sensors + port: 5000 + initialDelaySeconds: 30 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /api/v3_0/sensors + port: 5000 + initialDelaySeconds: 10 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: flexmeasures-service + namespace: cariflex +spec: + selector: + app: flexmeasures-server + ports: + - port: 5000 + targetPort: 5000 + type: ClusterIP +``` + +## OpenADR VTN Deployment +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: openadr-vtn + namespace: cariflex +spec: + replicas: 1 + selector: + matchLabels: + app: openadr-vtn + template: + metadata: + labels: + app: openadr-vtn + spec: + containers: + - name: vtn + image: flexmeasures-openadr-vtn:latest + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: cariflex-config + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: openadr-vtn-service + namespace: cariflex +spec: + selector: + app: openadr-vtn + ports: + - port: 8080 + targetPort: 8080 + type: ClusterIP +``` + +## OpenADR VEN Deployment +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: openadr-ven + namespace: cariflex +spec: + replicas: 1 + selector: + matchLabels: + app: openadr-ven + template: + metadata: + labels: + app: openadr-ven + spec: + containers: + - name: ven + image: flexmeasures-openadr-ven:latest + env: + - name: VTN_URL + value: "http://openadr-vtn-service:8080/OpenADR2/Simple/2.0b" + - name: FM_HOST + value: "https://cariflex.digitribe.fr" + envFrom: + - configMapRef: + name: cariflex-config + - secretRef: + name: cariflex-secrets + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: openadr-ven-service + namespace: cariflex +spec: + selector: + app: openadr-ven + ports: + - port: 8080 + type: ClusterIP +``` + +## CitrineOS Deployment +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: citrineos-server + namespace: cariflex +spec: + replicas: 1 + selector: + matchLabels: + app: citrineos-server + template: + metadata: + labels: + app: citrineos-server + spec: + containers: + - name: citrineos + image: ghcr.io/citrineos/citrineos-server:latest + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: cariflex-config + - secretRef: + name: cariflex-secrets + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "1" +--- +apiVersion: v1 +kind: Service +metadata: + name: citrineos-service + namespace: cariflex +spec: + selector: + app: citrineos-server + ports: + - port: 8080 + targetPort: 8080 + type: ClusterIP +``` + +## Ingress +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cariflex-ingress + namespace: cariflex + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + tls: + - hosts: + - cariflex.digitribe.fr + - grafana.digitribe.fr + - citrineos.digitribe.fr + secretName: cariflex-tls + rules: + - host: cariflex.digitribe.fr + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: flexmeasures-service + port: + number: 5000 + - host: grafana.digitribe.fr + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: grafana-service + port: + number: 3000 + - host: citrineos.digitribe.fr + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: citrineos-service + port: + number: 8080 +``` + +## Deployment Commands +```bash +# Create namespace +kubectl apply -f k8s/namespace.yaml + +# Apply config and secrets +kubectl apply -f k8s/configmap.yaml +kubectl apply -f k8s/secrets.yaml + +# Deploy applications +kubectl apply -f k8s/deployments/flexmeasures.yaml +kubectl apply -f k8s/deployments/openadr-vtn.yaml +kubectl apply -f k8s/deployments/openadr-ven.yaml +kubectl apply -f k8s/deployments/citrineos.yaml + +# Apply services +kubectl apply -f k8s/services/ + +# Apply ingress +kubectl apply -f k8s/ingress.yaml + +# Verify +kubectl get pods -n cariflex +kubectl get svc -n cariflex +kubectl get ingress -n cariflex +``` diff --git a/iac/scripts/deploy.sh b/iac/scripts/deploy.sh new file mode 100644 index 0000000..5a0d543 --- /dev/null +++ b/iac/scripts/deploy.sh @@ -0,0 +1,142 @@ +#!/bin/bash +# Cariflex EMS - Multi-Environment Deployment Script +# Usage: ./deploy.sh [dev|test|prod] [full|fm|openadr|citrine|grafana] + +set -e + +ENV="${1:-dev}" +COMPONENT="${2:-full}" +CARIFLEX_HOME="/home/eric/cariflex" +FLEXMEASURES_HOME="/home/eric/flexmeasures" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"; } +warn() { echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1"; } +error() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M-%S')] ERROR:${NC} $1"; exit 1; } + +# Environment-specific configuration +case "$ENV" in + dev) + FM_DOMAIN="cariflex.dev.local" + GRAFANA_DOMAIN="grafana.dev.local" + CITRINEOS_DOMAIN="citrineos.dev.local" + REPLICAS=1 + DEBUG=true + LOG_LEVEL="DEBUG" + ;; + test) + FM_DOMAIN="cariflex.test.digitribe.fr" + GRAFANA_DOMAIN="grafana.test.digitribe.fr" + CITRINEOS_DOMAIN="citrineos.test.digitribe.fr" + REPLICAS=1 + DEBUG=false + LOG_LEVEL="INFO" + ;; + prod) + FM_DOMAIN="cariflex.digitribe.fr" + GRAFANA_DOMAIN="grafana.digitribe.fr" + CITRINEOS_DOMAIN="citrineos.digitribe.fr" + REPLICAS=2 + DEBUG=false + LOG_LEVEL="WARNING" + ;; + *) + error "Unknown environment: $ENV (use dev, test, or prod)" + ;; +esac + +log "Deploying Cariflex EMS to $ENV environment" +log "Component: $COMPONENT" +log "FM Domain: $FM_DOMAIN" +log "Replicas: $REPLICAS" + +# Function to deploy a docker-compose stack +deploy_stack() { + local compose_file="$1" + local stack_name="$2" + + log "Deploying $stack_name from $compose_file..." + + # Generate environment-specific override + cat > "/tmp/docker-compose.${ENV}.override.yml" << EOF +version: '3.8' +services: + server: + environment: + - LOGGING_LEVEL=${LOG_LEVEL} + - FLEXMEASURES_ENV=${ENV} + labels: + - "traefik.http.routers.${stack_name}.rule=Host(\`${FM_DOMAIN}\`)" +EOF + + # Deploy + docker compose -f "$compose_file" -f "/tmp/docker-compose.${ENV}.override.yml" up -d + + log "$stack_name deployed successfully" +} + +# Deploy based on component +case "$COMPONENT" in + full) + log "Deploying full stack..." + deploy_stack "$FLEXMEASURES_HOME/docker-compose.yml" "flexmeasures" + deploy_stack "$FLEXMEASURES_HOME/docker-compose.openadr.yml" "openadr" + deploy_stack "$CARIFLEX_HOME/config/docker-compose-citrineos.yml" "citrineos" + ;; + fm) + deploy_stack "$FLEXMEASURES_HOME/docker-compose.yml" "flexmeasures" + ;; + openadr) + deploy_stack "$FLEXMEASURES_HOME/docker-compose.openadr.yml" "openadr" + ;; + citrine) + deploy_stack "$CARIFLEX_HOME/config/docker-compose-citrineos.yml" "citrineos" + ;; + *) + error "Unknown component: $COMPONENT (use full, fm, openadr, citrine)" + ;; +esac + +# Health checks +log "Running health checks..." + +check_service() { + local name="$1" + local url="$2" + local max_attempts="${3:-30}" + + for i in $(seq 1 $max_attempts); do + if curl -sk "$url" > /dev/null 2>&1; then + log "$name is healthy" + return 0 + fi + sleep 2 + done + + warn "$name health check failed after ${max_attempts} attempts" + return 1 +} + +check_service "FlexMeasures" "https://${FM_DOMAIN}/api/v3_0/sensors" 30 +check_service "OpenADR VTN" "http://openadr-vtn:8080/OpenADR2/Simple/2.0b" 10 +check_service "CitrineOS" "http://cariflex-citrineos-server:8080" 10 + +# Summary +log "Deployment complete!" +log "" +log "Environment: $ENV" +log "URLs:" +log " FlexMeasures: https://${FM_DOMAIN}" +log " Grafana: https://${GRAFANA_DOMAIN}" +log " CitrineOS: https://${CITRINEOS_DOMAIN}" +log "" +log "Containers:" +docker ps --format " {{.Names}}: {{.Status}}" | grep -E "flexmeasures|openadr|citrine|grafana" + +# Cleanup +rm -f "/tmp/docker-compose.${ENV}.override.yml"