move_detect/deploy/README.md
유창욱 2ffa74b639 feat(deploy): air-gapped offline bundle + systemd unit for edge daemon
Offline-installable bundle (wheelhouse + model + source) built on a machine with
internet and installed on the air-gapped Pi with --no-index (zero fetches), plus a
systemd unit (auto-restart, device access, hardening) and env template. Runs the
package from source via python -m edge (no offline build backend). Drift-guard test
keeps the env template in sync with what the daemon reads and rejects inline comments
that systemd EnvironmentFile= would fold into values.
2026-07-12 17:51:45 +09:00

86 lines
3.8 KiB
Markdown

# Edge deployment (Raspberry Pi 5, air-gapped / 폐쇄망)
Offline-installable bundle + systemd unit for the `move-detect` edge daemon.
The bundle is fully self-contained (all wheels + pose model + source): the Pi
performs **zero network fetches** at install or run time. The only runtime
network use is the cloud POST / config-poll / heartbeat, and even that tolerates
being offline (events buffer in the local sqlite queue and flush on reconnect).
> Scope: this covers the **CPU MoveNet + USB (UVC) webcam** path — the one the
> daemon wires today (`edge/daemon.py:main`) and the one verified end-to-end.
> Hailo-8 acceleration (`edge/adapters/hailo_pose.py`) and the CSI camera
> (`edge/adapters/picamera_source.py`) are separate device-validated paths — see
> `docs/hardware-bringup.md`.
## Files
| File | Runs on | Purpose |
|------|---------|---------|
| `bundle/build_offline_bundle.sh` | build machine (has internet) | downloads wheels + model, stages source, emits one tarball |
| `bundle/install_offline.sh` | the Pi (no internet) | venv + `--no-index` install, systemd unit, service account |
| `systemd/move-detect-edge.service` | the Pi | the daemon unit (auto-restart, device access, hardening) |
| `systemd/move-detect-edge.env.example` | the Pi | env template → `/etc/move-detect/edge.env` |
## 1. Build the bundle (on a machine WITH internet)
**Most reliable: build on an aarch64 host** (a Pi 5, or an arm64 CI runner) so the
downloaded wheels match the target exactly:
```bash
deploy/bundle/build_offline_bundle.sh # native aarch64 wheels
```
From an x86 box you can attempt a cross build (best-effort — onnxruntime/opencv
wheel tags may not resolve; if it fails, build natively):
```bash
deploy/bundle/build_offline_bundle.sh --cross # target aarch64 / cp311
```
Output: `dist/move-detect-edge-bundle-aarch64-cp311.tar.gz` (+ `MANIFEST.txt`
listing the bundled wheels). This single file crosses the air gap.
## 2. Transfer + install (on the air-gapped Pi)
Copy the tarball across the gap (USB, one-way transfer, etc.), then:
```bash
tar xzf move-detect-edge-bundle-aarch64-cp311.tar.gz
cd move-detect-edge-bundle
sudo ./install_offline.sh
```
The installer: creates the `movedetect` service account (in `video`/`render`/
`plugdev`/`hailo` groups), lays down source + model under `/opt/move-detect`,
builds a venv and installs the bundled wheels with `--no-index`, verifies the
package imports, seeds `/etc/move-detect/edge.env`, and installs the systemd unit.
## 3. Configure + start
```bash
sudo nano /etc/move-detect/edge.env # DEVICE_ID, DEVICE_SECRET, CAFE24_URL, ...
sudo systemctl enable --now move-detect-edge
journalctl -u move-detect-edge -f
```
`DEVICE_ID`/`DEVICE_SECRET` must match a `devices` row in the cloud (see
`cloud/docs/cloud-deploy.md` §6). `MOVENET_ONNX` should point at the bundled
model (`/opt/move-detect/models/movenet_lightning.onnx`).
## 4. Verify the air gap (do this before trusting a build)
- [ ] On a host with **networking disabled**, `install_offline.sh` completes —
the `--no-index` installs prove nothing is fetched. If pip tries to reach
PyPI, a wheel is missing from the bundle: rebuild (step 1) natively.
- [ ] Step [4/6] of the installer prints `edge import OK` (onnxruntime + cv2 +
numpy + requests all import from the venv).
- [ ] `systemctl status move-detect-edge` is `active (running)`; the log shows
frames processed and (if the cloud is reachable) heartbeats.
- [ ] Pull the network cable → events keep queueing (`QUEUE_DB` grows) → restore
→ the queue flushes. (End-to-end behavior verified in the edge e2e harness.)
## Not bundled (by design)
- **MySQL / the cloud app** — a separate host; see `cloud/docs/cloud-deploy.md`.
- **HailoRT + `yolov8s_pose.hef`** — the Hailo runtime and HEF are installed via
Hailo's own Pi packages; the CPU MoveNet path here needs neither.