- Redundancy.md: full rewrite — Akka-leader-driven ServiceLevel replaces operator-managed RedundancyRole. Documents the 5-tier ServiceLevelCalculator, RedundancyStateActor cluster singleton, and the DPS data flow. - ServiceHosting.md: full rewrite — single fused OtOpcUa.Host binary with OTOPCUA_ROLES env gating. Documents the conditional DI graph and the new health endpoints (/health/ready, /health/active, /healthz). - security.md: v2 banner at top covering path/project renames + new JWT bearer + DataProtection persisted to ConfigDb. Body unchanged because the 4-concern security model is unchanged in v2; full per-section rewrite waits for F15 (Admin pages migration) since security.md references many pages that move. - README.md: platform overview updated to v2 (fused Host + role gating).
77 lines
4.1 KiB
Markdown
77 lines
4.1 KiB
Markdown
# Service Hosting (v2)
|
|
|
|
## Overview
|
|
|
|
A production OtOpcUa deployment runs **one binary per node**, plus the optional Wonderware historian sidecar:
|
|
|
|
| Process | Project | Runtime | Platform | Responsibility |
|
|
|---|---|---|---|---|
|
|
| **OtOpcUa Host** | `src/Server/ZB.MOM.WW.OtOpcUa.Host` | .NET 10 | AnyCPU | Single fused binary. `OTOPCUA_ROLES` env decides what to mount: `admin` (Blazor + auth + control-plane singletons), `driver` (OPC UA endpoint + per-driver actors), or both. |
|
|
| **OtOpcUa Wonderware Historian** *(optional)* | `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware` | .NET Framework 4.8 | x86 (32-bit) | Out-of-process sidecar exposing the Wonderware Historian SDK over a named pipe. Required only when `Historian:Wonderware:Enabled=true`. |
|
|
|
|
Galaxy access still uses the separately-installed **mxaccessgw** sidecar (see `docs/v2/Galaxy.ParityRig.md`); the gateway owns the MXAccess COM bitness constraint (its worker is x86 net48). Nothing in the OtOpcUa repo carries that constraint anymore.
|
|
|
|
> **v2 change.** v1's separate `OtOpcUa.Server` + `OtOpcUa.Admin` Windows services merged into a single role-gated `OtOpcUa.Host` binary. Two installers became one (with a `-Roles` parameter). The whole DI graph is composed in `OtOpcUa.Host/Program.cs`; per-role wiring is conditional on the env var.
|
|
|
|
## Role gating
|
|
|
|
`Program.cs` reads `OTOPCUA_ROLES`, parses it with `RoleParser`, and conditionally registers services:
|
|
|
|
| Role present | Wires |
|
|
|---|---|
|
|
| `admin` | `AddOtOpcUaAuth`, `AddAdminUI`, `AddSignalR`, `AddOtOpcUaAdminClients`, `MapOtOpcUaAuth`, `MapAdminUI<App>`, `MapOtOpcUaHubs`, `WithOtOpcUaControlPlaneSingletons` (5 admin singletons via `Akka.Hosting`) |
|
|
| `driver` | `WithOtOpcUaRuntimeActors` (DriverHostActor + DbHealthProbeActor) — and the OPC UA endpoint on port 4840 |
|
|
| Either / both | `AddOtOpcUaConfigDb`, `AddOtOpcUaCluster`, `AddOtOpcUaHealth` (`/health/ready`, `/health/active`, `/healthz`) |
|
|
|
|
Single-node dev: `OTOPCUA_ROLES=admin,driver`. Production: typically two admin nodes (HA pair) + N driver nodes.
|
|
|
|
## Akka cluster
|
|
|
|
The host joins an Akka.NET cluster bound to the address in `appsettings.json::Cluster`:
|
|
|
|
```json
|
|
{
|
|
"Cluster": {
|
|
"Hostname": "0.0.0.0",
|
|
"Port": 4053,
|
|
"PublicHostname": "node-a.lan",
|
|
"SeedNodes": ["akka.tcp://otopcua@node-a.lan:4053"],
|
|
"Roles": ["admin", "driver"]
|
|
}
|
|
}
|
|
```
|
|
|
|
- `WithOtOpcUaClusterBootstrap` (in `OtOpcUa.Cluster`) loads the embedded HOCON (split-brain resolver, pinned dispatcher, failure detector tuning) and overlays remote endpoint + cluster options.
|
|
- All cluster singletons + per-node actors live on this single ActorSystem — there is no second Akka instance.
|
|
|
|
See [Redundancy.md](Redundancy.md) for the role-leader + ServiceLevel story.
|
|
|
|
## Health endpoints
|
|
|
|
Both admin and driver nodes expose:
|
|
|
|
| Path | Status meaning |
|
|
|---|---|
|
|
| `/healthz` | Process alive. |
|
|
| `/health/ready` | ConfigDb reachable + cluster member state is `Up`. |
|
|
| `/health/active` | Admin-role leader (the node Traefik or an HA LB should route traffic to). |
|
|
|
|
Used by Traefik for the active-leader-only routing pattern (see [Task 63 traefik docs](v2/Architecture-v2.md) — TODO).
|
|
|
|
## OtOpcUa Wonderware Historian (optional)
|
|
|
|
Unchanged from v1. Pipe IPC contract lives in `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Contracts/`; sidecar pipe handler in `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Pipe/`. Install via `scripts/install/Install-Services.ps1 -InstallWonderwareHistorian`.
|
|
|
|
## Install / Uninstall
|
|
|
|
- `scripts/install/Install-Services.ps1 -Roles admin,driver` — installs `OtOpcUaHost`. v2 rewrite tracked as plan Task 62.
|
|
- `scripts/install/Uninstall-Services.ps1` — stops + removes the host service (and the historian sidecar if installed).
|
|
|
|
## Logging
|
|
|
|
Serilog with rolling-daily file sinks. Each host writes to `logs/otopcua-*.log` plus stdout (NSSM/systemd-friendly). Per-environment log level overrides go in `appsettings.{Environment}.json`.
|
|
|
|
## Depth reference
|
|
|
|
For the full host-architecture rationale (why fused vs. split, role-gating tradeoffs, multi-node deployment shapes), see `docs/plans/2026-05-26-akka-hosting-alignment-design.md` §3-4.
|