master_service unlinks and recreates master_service.sock on every host boot or restart, and a bind mount of the file pins the old (dead) inode, so every mscli call inside the container failed with "Connect to server error". Mount the directory instead. The entrypoint wraps "mscli stopservice video_hub_pc4" in stop_videohub(), which prints a WARN when mscli cannot reach master_service (then /dev/video4 stays busy and the camera will not open), at start and in the 10 s loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
117 lines
3.7 KiB
YAML
117 lines
3.7 KiB
YAML
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]
|