# docker-dev Mac-friendly four-node OtOpcUa fleet for manual UI exercise + integration smoke tests. Spins up an Akka cluster + SQL Server + OpenLDAP + Traefik in front of two admin nodes. ## Stack | Service | Role | Ports | |---|---|---| | `sql` | SQL Server 2022 (`ConfigDb` backing store) | host `14330` → container `1433` | | `ldap` | OpenLDAP with dev users `alice` / `bob` | host `3893` → container `1389` | | `admin-a` | OtOpcUa.Host, `OTOPCUA_ROLES=admin`, cluster seed | internal `9000` | | `admin-b` | OtOpcUa.Host, `OTOPCUA_ROLES=admin`, joins admin-a | internal `9000` | | `driver-a` | OtOpcUa.Host, `OTOPCUA_ROLES=driver` | host `4840` → container `4840` | | `driver-b` | OtOpcUa.Host, `OTOPCUA_ROLES=driver` | host `4841` → container `4840` | | `traefik` | Routes `:80` to whichever admin-* currently passes `/health/active` | host `80`, dashboard `8080` | All six containers share an Akka cluster bound to port `4053` inside the Compose network. The Akka `PublicHostname` of each container matches its Compose service name; the seed-node list points at `admin-a` so the other three join via that. ## Bring up ```bash # from the repo root docker compose -f docker-dev/docker-compose.yml up -d --build # wait ~15 seconds for SQL to come up + the cluster to form open http://localhost # Blazor admin UI via Traefik open http://localhost:8080 # Traefik dashboard ``` The first build takes a few minutes (.NET SDK image + restore + publish). Subsequent rebuilds are faster with Docker's layer cache. ## Auth (dev only) Use one of the LDAP dev users from `LDAP_USERS` in `docker-compose.yml`: | Username | Password | |---|---| | `alice` | `alice123` | | `bob` | `bob123` | The compose mounts everyone into `ou=FleetAdmin` so the dev role mapping resolves to `FleetAdmin`. ## Tear down ```bash docker compose -f docker-dev/docker-compose.yml down -v ``` The `-v` drops the SQL + LDAP volumes; remove it to keep ConfigDb state across restarts. ## Failover smoke 1. Watch the Traefik dashboard at `http://localhost:8080`. Both `admin-a` and `admin-b` should be listed as healthy in the `otopcua-admin` service. 2. `docker compose -f docker-dev/docker-compose.yml stop admin-a` — `admin-b` should pick up the admin role-leader within ~15 s (Akka split-brain stable-after). Traefik will route traffic to `admin-b` once its `/health/active` returns 200. 3. `docker compose -f docker-dev/docker-compose.yml start admin-a` — `admin-a` rejoins as a follower; `admin-b` keeps the leader role until something disturbs it. ## Notes - This compose is for the **local Mac/Linux developer rig**. The team's CI + soak runs go to the remote docker host at `10.100.0.35` (see `docs/v2/dev-environment.md`); the file there mirrors this one with adjusted port bindings. - The OPC UA driver endpoints (`opc.tcp://localhost:4840`, `opc.tcp://localhost:4841`) are reachable directly from the host — Traefik is only in front of the admin HTTP surface. - Galaxy + Wonderware drivers can't run in Linux containers (they need the Windows-only mxaccessgw + Historian SDK). On non-Windows, `DriverInstanceActor.ShouldStub(driverType, roles)` returns `true` for those types and the actor goes straight to a `Stubbed` state that returns deterministic success.