video-surveillance-app/docker-compose.yml

173 lines
6.6 KiB
YAML
Raw Permalink Normal View History

# =============================================================================
# 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
# Demarrage seulement : la sonde tolere un serveur media absent, elle
# ne doit pas empecher l'alarme de fonctionner.
mediamtx:
condition: service_started
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}
MEDIA_API_URL: http://mediamtx:9997
MEDIA_API_PASSWORD: ${MEDIA_API_PASSWORD}
CAMERA_PROBE_ENABLED: ${CAMERA_PROBE_ENABLED:-true}
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