# ============================================================================= # iot-alarm-system # # Deux reseaux : # - "default" : prive, Kafka et PostgreSQL n'y sont joignables que par l'app. # Aucun port n'est publie sur l'hote - le broker n'est pas expose a Internet. # - "edge" : reseau externe partage, ou le reverse proxy Caddy du serveur # vient chercher l'application pour la servir en HTTPS. # # Note sur Zookeeper : absent volontairement. Kafka tourne en mode KRaft, son # quorum interne. Zookeeper est deprecie depuis Kafka 3.5 et supprime en 4.0 ; # le garder ajouterait un conteneur, ~400 Mo de RAM et une piece a maintenir. # ============================================================================= services: # --------------------------------------------------------------------------- # Bus d'evenements - Kafka en KRaft mono-noeud # --------------------------------------------------------------------------- kafka: image: apache/kafka:3.9.1 container_name: iot-alarm-kafka restart: unless-stopped environment: CLUSTER_ID: "iot-alarm-cluster-0001" KAFKA_NODE_ID: 1 KAFKA_PROCESS_ROLES: broker,controller KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093 KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT # Mono-noeud : aucune replication possible, on l'assume explicitement. KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 KAFKA_LOG_RETENTION_HOURS: 72 KAFKA_LOG_DIRS: /var/lib/kafka/data KAFKA_HEAP_OPTS: "-Xmx400m -Xms256m" volumes: - kafka_data:/var/lib/kafka/data healthcheck: test: ["CMD-SHELL", "/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092 >/dev/null 2>&1"] interval: 15s timeout: 10s retries: 12 start_period: 40s mem_limit: 800m networks: [default] # --------------------------------------------------------------------------- # Persistance - write model et read model # --------------------------------------------------------------------------- postgres: image: postgres:16-alpine container_name: iot-alarm-postgres restart: unless-stopped environment: POSTGRES_DB: iotalarm POSTGRES_USER: iotalarm POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD est obligatoire, voir .env.example} # Le checksum des pages detecte une corruption disque silencieuse. POSTGRES_INITDB_ARGS: "--data-checksums" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U iotalarm -d iotalarm"] interval: 10s timeout: 5s retries: 10 start_period: 15s mem_limit: 256m networks: [default] # --------------------------------------------------------------------------- # Application Spring Boot # --------------------------------------------------------------------------- app: build: context: . dockerfile: Dockerfile image: iot-alarm-system:1.0.0 container_name: iot-alarm-app restart: unless-stopped depends_on: kafka: condition: service_healthy postgres: condition: service_healthy environment: SPRING_PROFILES_ACTIVE: prod KAFKA_BOOTSTRAP_SERVERS: kafka:9092 DB_URL: jdbc:postgresql://postgres:5432/iotalarm DB_USERNAME: iotalarm DB_PASSWORD: ${DB_PASSWORD} SIMULATOR_ENABLED: ${SIMULATOR_ENABLED:-true} SIMULATOR_INTERVAL: ${SIMULATOR_INTERVAL:-8s} ALARM_EXIT_DELAY: ${ALARM_EXIT_DELAY:-30s} ALARM_MIN_CONFIDENCE: ${ALARM_MIN_CONFIDENCE:-60} LOG_LEVEL: ${LOG_LEVEL:-INFO} mem_limit: 512m # Joint les deux reseaux : "default" pour parler a Kafka et PostgreSQL, # "edge" pour etre joignable par le reverse proxy. networks: [default, edge] # --------------------------------------------------------------------------- # Serveur media - ingestion du flux iPhone, restitution navigateur # # L'iPhone POUSSE vers ce service (SRT ou RTMP). Il ne peut pas etre # interroge : en 4G comme derriere la box du studio, il n'a pas d'adresse # joignable depuis Internet. Seul un flux sortant traverse le CGNAT. # # Les ports d'ingestion sont publies sur l'hote, ce sont les seuls du projet # a l'etre. Les sorties HTTP (HLS 8888, WebRTC 8889) restent internes et # passent par Caddy, qui leur apporte le TLS du domaine. # --------------------------------------------------------------------------- mediamtx: image: bluenviron/mediamtx:1.21.0 container_name: iot-alarm-mediamtx restart: unless-stopped volumes: - ./deploy/mediamtx/mediamtx.yml:/mediamtx.yml:ro ports: - "1935:1935" # RTMP (TCP) - ingestion, repli - "8890:8890/udp" # SRT (UDP) - ingestion, a privilegier - "8189:8189/udp" # WebRTC ICE - media vers les navigateurs mem_limit: 256m # Image sans shell (base scratch) : pas de healthcheck par commande. # La disponibilite se lit sur /actuator du cote applicatif et dans les # logs du conteneur. networks: [default, edge] # --------------------------------------------------------------------------- # Nginx - profil "standalone" uniquement # # Sur le serveur de production, le frontal est le Caddy deja en place (il # detient 80/443 et sert cinq autres domaines) : voir deploy/Caddyfile.snippet. # Ce Nginx sert au cas "je clone le depot et je lance tout sur ma machine" : # docker compose --profile standalone up -d -> http://localhost:8081 # --------------------------------------------------------------------------- nginx: image: nginx:alpine container_name: iot-alarm-nginx profiles: [standalone] restart: unless-stopped depends_on: [app] ports: - "8081:80" volumes: - ./deploy/nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro mem_limit: 64m networks: [default] volumes: kafka_data: postgres_data: networks: default: name: iot-alarm-internal edge: external: true name: edge