services: # -- NATS Server ---------------------------- nats: image: nats:latest profiles: ["nats"] ports: - "4222:4222" - "8222:8222" command: > --auth ${NATS_TOKEN:-realsense-dev-token} --http_port 8222 restart: unless-stopped healthcheck: test: ["CMD", "nats-server", "--help"] interval: 5s timeout: 3s retries: 3 start_period: 5s # -- Backend -------------------------------- backend: build: context: . dockerfile: Dockerfile # network: host so that build-time apt/git/pip can reach the # tinyproxy running on the host at localhost:8888. network: host privileged: true ports: - "${API_PORT:-8878}:8878" devices: - "/dev/bus/usb:/dev/bus/usb" volumes: - ./images:/app/images - ./models:/app/models:ro # mscli + master_service socket so the container can stop the # Unitree video_hub_pc4 service (it holds /dev/video4 — the D435i # V4L2 node — and would otherwise make Open() fail with EBUSY). # Mount the *directory*, not the socket file: master_service unlinks # and recreates the socket on every host boot/restart, and a bind # mount of the file pins the old (now dead) inode, so every mscli # call inside the container fails with "Connect to server error". - /unitree/sbin/mscli:/unitree/sbin/mscli:ro - /unitree/var/run:/unitree/var/run entrypoint: - bash - -c - | stop_videohub() { out=$$(/unitree/sbin/mscli stopservice video_hub_pc4 2>&1) || true case "$$out" in *"Connect to server error"*|*"connect error"*) echo "[entrypoint] WARN mscli cannot reach master_service.sock:" \ "/dev/video4 will stay busy and the camera will not open. $$out" >&2 ;; esac } stop_videohub sleep 2 ( while sleep 10; do stop_videohub; done ) & exec /app/entrypoint.sh env_file: .env depends_on: nats: condition: service_healthy required: false restart: unless-stopped labels: # autoheal monitora SOLO i container con questa label = true autoheal: "true" healthcheck: # Healthcheck "vero": fallisce se camera_open != true. # Usa /api/health (pubblico, no API key) che già ritorna camera_open. # 4 retry × 15s ⇒ ~1 min di stato cattivo prima dell'autoheal restart. test: - CMD - python3 - -c - | import urllib.request, json, sys try: r = urllib.request.urlopen('http://localhost:8878/api/health', timeout=3) d = json.loads(r.read()) sys.exit(0 if d.get('data', {}).get('camera_open') else 1) except Exception: sys.exit(1) interval: 15s timeout: 5s retries: 4 start_period: 30s # -- Autoheal ------------------------------- # Monitora i container con label `autoheal=true` e li restarta automaticamente # quando Docker li marca "unhealthy". Risolve il caso "pyrealsense2 SDK stuck". autoheal: image: willfarrell/autoheal:latest container_name: autoheal-d435 restart: always environment: AUTOHEAL_CONTAINER_LABEL: autoheal AUTOHEAL_INTERVAL: 10 AUTOHEAL_START_PERIOD: 30 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro # -- Frontend React Dev (hot reload) -------- frontend-dev: image: node:20-alpine working_dir: /app volumes: - ./src/frontend/react:/app - /app/node_modules command: sh -c "npm ci && npm run dev -- --host 0.0.0.0" ports: - "5173:5173" profiles: [dev]