Files
parking_solution/docker-compose.prod.yml
T
julian dd0f6e483a fix(reader): real ICMP liveness — QR reader status was a hardcoded "ready"
Two genuinely-offline QR readers showed GREEN: the adapter's healthCheck was
hardcoded to { ready, "stub" } and never probed. These are PUSH devices (scan →
GET our backend, resolve by serial) with NO TCP port, so a connect probe has
nothing to hit — the stub "solved" that by lying. False-healthy is the worst
failure for a status bar.

- Optional reader IP field (monitor-ONLY; scans still resolve by serial,
  operation unchanged).
- Unprivileged ICMP ping (drivers/icmp.ts): shells /bin/ping -c1, exit-0 = reply.
  No native dep, no CAP_NET_RAW. docker-compose.prod.yml sets
  net.ipv4.ping_group_range so it works for the non-root container user.
- healthCheck: replies → ready, no reply → offline, NO IP → degraded
  ("set IP to monitor") — never a false green.

Verified on hardware: readers (10.0.10.7/.8) answer ICMP on the device VLAN;
UI Test connection → "● ready — ping 10.0.10.7". Tests: reader.test.ts (4).

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
2026-06-26 16:47:12 +02:00

67 lines
2.4 KiB
YAML

# PROD override: pull pinned registry images (no local build), restart always, real
# recognizer, and a CADDY reverse proxy in front so operators reach the booth on a clean
# port-80 URL (no :3000) — and a path to real TLS later. Server + vision stay INTERNAL
# (only Caddy publishes a port). Use with the base file and pin TAG to the branch you deploy:
# REGISTRY=git.infra.msai.al/mca/parking_solution TAG=main \
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# See wiki/decisions/container-deployment.md.
services:
# Reverse proxy: :80 → server:3000 (WebSocket /api/ws upgrades pass through natively).
# Caddy is a single static binary with a one-line proxy config; swapping http:// for the
# site's real hostname later enables automatic HTTPS. The booth is reached at
# http://<name-or-ip>/ (the name set via hosts/DNS on-site — NOT baked into any image).
proxy:
image: caddy:2-alpine
restart: always
ports:
- "80:80"
# - "443:443" # uncomment when moving to TLS (and set a real hostname in Caddyfile)
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
depends_on:
- server
networks:
- parking
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
server:
restart: always
# No published port — only the proxy reaches the server, over the private network.
expose:
- "3000"
# Let the server ICMP-ping push-only readers (Dingtian/GEE QR) for an honest
# online/offline status WITHOUT CAP_NET_RAW: opening ping_group_range to all gids
# enables `/bin/ping` in unprivileged SOCK_DGRAM mode for the non-root runtime user.
# (The reader exposes no TCP port, so a connect-probe can't work — see reader.ts /
# wiki/entities/dingtian-qr-reader.md.)
sysctls:
- net.ipv4.ping_group_range=0 2147483647
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
vision:
restart: always
# The real ANPR engine. The image baked the model weights at build (offline-first).
environment:
VISION_RECOGNIZER: fast_alpr
# No published ports — vision is reached only by the server over the private network.
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
caddy-data:
caddy-config: