docs: v2 updates to Redundancy, ServiceHosting, security, README (Task 64)
- 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).
This commit is contained in:
@@ -1,62 +1,76 @@
|
||||
# Service Hosting
|
||||
# Service Hosting (v2)
|
||||
|
||||
## Overview
|
||||
|
||||
A production OtOpcUa deployment runs **two or three processes**, each
|
||||
with a distinct runtime and install surface:
|
||||
A production OtOpcUa deployment runs **one binary per node**, plus the optional Wonderware historian sidecar:
|
||||
|
||||
| Process | Project | Runtime | Platform | Responsibility |
|
||||
|---|---|---|---|---|
|
||||
| **OtOpcUa Server** | `src/Server/ZB.MOM.WW.OtOpcUa.Server` | .NET 10 | x64 | Hosts the OPC UA endpoint; loads every driver in-process (Modbus, S7, AbCip, AbLegacy, TwinCAT, FOCAS, OPC UA Client, Galaxy via mxaccessgw); exposes `/healthz`. |
|
||||
| **OtOpcUa Admin** | `src/Server/ZB.MOM.WW.OtOpcUa.Admin` | .NET 10 (ASP.NET Core / Blazor Server) | x64 | Operator UI for Config DB editing + fleet status, SignalR hubs (`FleetStatusHub`, `AlertHub`), Prometheus `/metrics`. |
|
||||
| **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` in `appsettings.json`. |
|
||||
| **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 uses a separately-installed **mxaccessgw** running out
|
||||
of a sibling repo (`c:\Users\dohertj2\Desktop\mxaccessgw\`) — see
|
||||
`docs/v2/Galaxy.ParityRig.md` for setup. The mxaccessgw owns the
|
||||
MXAccess COM bitness constraint (its worker is x86 net48); nothing
|
||||
in the OtOpcUa repo carries that constraint anymore. PR 7.2 retired
|
||||
the legacy in-process `Galaxy.Host` / `Galaxy.Proxy` / `Galaxy.Shared`
|
||||
projects + the `OtOpcUaGalaxyHost` Windows service.
|
||||
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.
|
||||
|
||||
## OtOpcUa Server
|
||||
> **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.
|
||||
|
||||
Hosted via `Microsoft.Extensions.Hosting` with `AddWindowsService`
|
||||
(decision #30 — replaced TopShelf in v2). The host's `Build()`
|
||||
returns immediately when launched interactively (e.g. `dotnet run`)
|
||||
but blocks for SCM signals when running as a Windows service.
|
||||
## Role gating
|
||||
|
||||
In-process drivers are registered at startup in `Program.cs`'s
|
||||
`DriverFactoryRegistry` block; the `DriverInstance` rows in the
|
||||
central Config DB select which driver factories materialise into
|
||||
live `IDriver` instances. See `docs/v2/driver-specs.md` for the
|
||||
per-driver `DriverConfig` JSON shapes.
|
||||
`Program.cs` reads `OTOPCUA_ROLES`, parses it with `RoleParser`, and conditionally registers services:
|
||||
|
||||
## OtOpcUa Admin
|
||||
| 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`) |
|
||||
|
||||
Same hosting model; runs the Blazor Server UI + SignalR hubs.
|
||||
Reads from the same Config DB the Server writes to.
|
||||
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)
|
||||
|
||||
When `Historian:Wonderware:Enabled=true`, the Server speaks to a
|
||||
sidecar that wraps the Wonderware Historian SDK (which is .NET
|
||||
Framework only). The pipe IPC contract is in
|
||||
`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Contracts/`
|
||||
and the sidecar's pipe handler lives at
|
||||
`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Pipe/`.
|
||||
|
||||
Install via the `-InstallWonderwareHistorian` switch on
|
||||
`scripts/install/Install-Services.ps1`.
|
||||
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` — installs `OtOpcUa` and
|
||||
optionally `OtOpcUaWonderwareHistorian`.
|
||||
- `scripts/install/Uninstall-Services.ps1` — stops + removes both,
|
||||
plus `OtOpcUaGalaxyHost` if a pre-7.2 rig still carries it.
|
||||
- `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 service writes to
|
||||
`%ProgramData%\OtOpcUa\<service>-*.log` plus stdout (NSSM-friendly).
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user