docs: add deployments/ catalog with per-deployment markdown

One file per local Docker cluster (docker-cluster, docker-cluster-env2)
keyed by Transport.SourceEnvironment. README indexes the set.
This commit is contained in:
Joseph Doherty
2026-05-28 09:27:43 -04:00
parent d8eda2f508
commit 6d87ee3c3b
3 changed files with 262 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# Deployments
Catalog of ScadaLink deployments. Each entry below has a corresponding markdown file with topology, ports, infrastructure dependencies, and operational commands. The deployment **ID** matches the `Transport.SourceEnvironment` value stamped into exported bundle manifests (Component #24), so an audit trail of "where did this bundle come from" maps 1:1 to a file in this folder.
| Deployment ID | Type | Topology | Doc |
|---------------|------|----------|-----|
| `docker-cluster` | Local Docker (primary) | 2 central + 3 sites × 2 nodes (8 nodes) | [docker-cluster.md](docker-cluster.md) |
| `docker-cluster-env2` | Local Docker (secondary) | 2 central + 1 site × 2 nodes (4 nodes) | [docker-cluster-env2.md](docker-cluster-env2.md) |
## Conventions
- One markdown file per deployment, named for the deployment ID.
- Add a new file **and** a row above when introducing a new environment.
- Keep the deployment ID, container-name prefix, host-port range, and database names consistent across the doc, `appsettings.*.json`, and any `Transport.SourceEnvironment` value.
- When a deployment is decommissioned, move its row to a `## Decommissioned` section rather than deleting the file — the ID may still appear in historic Transport bundle manifests.
+118
View File
@@ -0,0 +1,118 @@
# docker-cluster-env2
Secondary local Docker deployment — a minimal ScadaLink topology that runs **concurrently with** [`docker-cluster`](docker-cluster.md) so the Transport (#24) feature can be exercised end-to-end across two real environments (export a bundle from one, import into the other).
- **Deployment ID:** `docker-cluster-env2`
- **Transport.SourceEnvironment:** `docker-cluster-env2`
- **Source directory:** [`docker-env2/`](../docker-env2/)
- **Container-name prefix:** `scadalink-env2-`
- **Host-port range:** `91XX` (primary `90XX` + 100)
## Topology
```
┌───────────────────┐
│ Traefik LB :9100 │ ◄── CLI / Browser
│ Dashboard :8181 │
└────────┬──────────┘
│ routes to active node
┌──────────────────────┼──────────────────────────────┐
│ Env2 Central Cluster │
│ env2-central-a (UI :9101, Akka :9111) │
│ env2-central-b (UI :9102, Akka :9112) │
└───────────┬─────────────────────────────────────────┘
│ Akka.NET remoting + gRPC streaming
Env2 Site-X (Env2 Site X)
Akka 9121/9122
gRPC 9123/9124
```
Same active/standby pattern as the primary, scaled down to a single site cluster to keep the footprint small. Built specifically for cross-environment Transport testing — not a general-purpose dev environment.
## Nodes
| Node | Container Name | Host Web | Host Akka | Host gRPC |
|------|----------------|----------|-----------|-----------|
| Traefik LB | `scadalink-env2-traefik` | 9100 | — | — |
| Central A | `scadalink-env2-central-a` | 9101 | 9111 | — |
| Central B | `scadalink-env2-central-b` | 9102 | 9112 | — |
| Site-X A | `scadalink-env2-site-x-a` | — | 9121 | 9123 |
| Site-X B | `scadalink-env2-site-x-b` | — | 9122 | 9124 |
## Sites
| Site Identifier | Central UI Name |
|-----------------|-----------------|
| `site-x` | Env2 Site X |
## Infrastructure Dependencies
Env2 reuses the primary's `infra/` containers via the shared `scadalink-net` bridge network — there is no separate `infra/` stack to start.
| Service | Shared Container | What env2 uses it for |
|---------|------------------|-----------------------|
| MS SQL | `scadalink-mssql` | Env2 databases `ScadaLinkConfig2` / `ScadaLinkMachineData2` (separate DBs on the same instance) |
| LDAP | `scadalink-ldap` | Authentication (same test users) |
| SMTP | `scadalink-smtp` | Notification capture (env2 emails distinguishable by `FromAddress`) |
| OPC UA | `scadalink-opcua` | Simulated tags for `site-x` data connections |
| REST API | `scadalink-restapi` | External REST integration testing |
## Databases
| Database | Owner |
|----------|-------|
| `ScadaLinkConfig2` | Configuration Database (#17) — env2 instance |
| `ScadaLinkMachineData2` | Machine data — env2 instance |
Created by `docker-env2/init-db.sh`, which `deploy.sh` runs automatically.
## Commands
```bash
# Primary infra must already be up (creates scadalink-net + MS SQL + LDAP + SMTP + OPC UA + REST)
cd infra && docker compose up -d && cd ..
# Build image (shared with primary), create env2 DBs, deploy env2 containers
bash docker-env2/deploy.sh
# First-time only: seed the env2 test site
bash docker-env2/seed-sites.sh
# View logs
docker compose -f docker-env2/docker-compose.yml logs -f
docker logs -f scadalink-env2-central-a
# Stop env2 (preserves SQLite + logs)
bash docker-env2/teardown.sh
# Also drop env2 databases
docker exec scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaLink_Dev1#' -C \
-Q "DROP DATABASE ScadaLinkConfig2; DROP DATABASE ScadaLinkMachineData2;"
```
## CLI Access
```bash
dotnet run --project src/ScadaLink.CLI -- \
--url http://localhost:9100 \
--username multi-role --password password \
template list
```
## What's Different from `docker-cluster`
- Single site (`site-x`) instead of three (`site-a`/`site-b`/`site-c`).
- Host port range `91XX` instead of `90XX`.
- Container names prefixed `scadalink-env2-` instead of `scadalink-`.
- Databases `ScadaLinkConfig2` / `ScadaLinkMachineData2` on the **shared** `scadalink-mssql`.
- `Transport.SourceEnvironment = "docker-cluster-env2"` stamped into exported bundle manifests.
- Distinct `Security.JwtSigningKey` so sessions cannot cross environments.
## Notes
- Concurrent with [`docker-cluster`](docker-cluster.md) — both stacks share `scadalink-net` and the `infra/` services. Disjoint host ports + DB names + container names allow them to run side-by-side.
- Design rationale: [`docs/plans/2026-05-24-second-environment-design.md`](../docs/plans/2026-05-24-second-environment-design.md).
- Transport golden-path verification: [`docs/plans/2026-05-24-second-environment-verification.md`](../docs/plans/2026-05-24-second-environment-verification.md).
- Setup details, port table, and CLI examples in [`docker-env2/README.md`](../docker-env2/README.md).
+129
View File
@@ -0,0 +1,129 @@
# docker-cluster
Primary local Docker deployment — the full ScadaLink reference topology used for day-to-day development and integration testing. Runs on the developer workstation under Docker / OrbStack.
- **Deployment ID:** `docker-cluster`
- **Transport.SourceEnvironment:** `docker-cluster`
- **Source directory:** [`docker/`](../docker/)
- **Container-name prefix:** `scadalink-`
- **Host-port range:** `90XX`
## Topology
```
┌───────────────────┐
│ Traefik LB :9000 │ ◄── CLI / Browser
│ Dashboard :8180 │
└────────┬──────────┘
│ routes to active node
┌──────────────────────┼──────────────────────────────┐
│ Central Cluster │
│ central-node-a (UI :9001, Akka :9011) │
│ central-node-b (UI :9002, Akka :9012) │
└───────────┬─────────────────────────────────────────┘
│ Akka.NET remoting + gRPC streaming
├──────────────────┬──────────────────┐
▼ ▼ ▼
Site-A (Test Plant A) Site-B (Test Plant B) Site-C (Test Plant C)
Akka 9021/9022 Akka 9031/9032 Akka 9041/9042
gRPC 9023/9024 gRPC 9033/9034 gRPC 9043/9044
```
Two-node active/standby everywhere. Central runs the Blazor UI, Template Engine, Deployment Manager, Notification Outbox, Site Call Audit, Audit Log, and Inbound API. Each site runs the Site Runtime, Data Connection Layer, Store-and-Forward, and Site Event Logging, with a per-node gRPC streaming server (port 8083) that central nodes subscribe to for real-time attribute/alarm streams.
## Nodes
| Node | Container Name | Host Web | Host Akka | Host gRPC |
|------|----------------|----------|-----------|-----------|
| Traefik LB | `scadalink-traefik` | 9000 | — | — |
| Central A | `scadalink-central-a` | 9001 | 9011 | — |
| Central B | `scadalink-central-b` | 9002 | 9012 | — |
| Site-A A | `scadalink-site-a-a` | — | 9021 | 9023 |
| Site-A B | `scadalink-site-a-b` | — | 9022 | 9024 |
| Site-B A | `scadalink-site-b-a` | — | 9031 | 9033 |
| Site-B B | `scadalink-site-b-b` | — | 9032 | 9034 |
| Site-C A | `scadalink-site-c-a` | — | 9041 | 9043 |
| Site-C B | `scadalink-site-c-b` | — | 9042 | 9044 |
Port pattern: `90X1`/`90X2` (Akka), `90X3`/`90X4` (gRPC), where X = 0 (central), 2 (site-a), 3 (site-b), 4 (site-c).
## Sites
| Site Identifier | Central UI Name |
|-----------------|-----------------|
| `site-a` | Test Plant A |
| `site-b` | Test Plant B |
| `site-c` | Test Plant C |
## Infrastructure Dependencies
All from `infra/docker-compose.yml`, attached via the shared `scadalink-net` bridge network:
| Service | Container | Host Port | Purpose |
|---------|-----------|-----------|---------|
| MS SQL 2022 | `scadalink-mssql` | 1433 | `ScadaLinkConfig`, `ScadaLinkMachineData` |
| LDAP (GLAuth) | `scadalink-ldap` | 3893 | Authentication |
| SMTP (Mailpit) | `scadalink-smtp` | 1025 / 8025 | Notification capture |
| OPC UA | `scadalink-opcua` | 50000 / 8080 | Simulated device tags |
| REST API | `scadalink-restapi` | 5200 | External REST integration testing |
## Databases
| Database | Owner |
|----------|-------|
| `ScadaLinkConfig` | Configuration Database (#17) |
| `ScadaLinkMachineData` | Machine data (template/instance runtime data) |
## Commands
```bash
# Start infra first (creates scadalink-net, MS SQL, LDAP, SMTP, OPC UA, REST API)
cd infra && docker compose up -d && cd ..
# Build image + deploy all 8 nodes
bash docker/deploy.sh
# First-time only: seed test sites with Akka + gRPC addresses
bash docker/seed-sites.sh
# View logs
docker compose -f docker/docker-compose.yml logs -f
docker logs -f scadalink-central-a
# Health
curl -s http://localhost:9001/health/ready | python3 -m json.tool
curl -s http://localhost:9002/health/ready | python3 -m json.tool
# Stop application nodes (preserves SQLite + logs)
bash docker/teardown.sh
```
## CLI Access
The CLI talks to the Traefik LB, which routes to the active central node.
```bash
dotnet run --project src/ScadaLink.CLI -- \
--url http://localhost:9000 \
--username multi-role --password password \
template list
```
Direct node access: `http://localhost:9001` (central-a), `http://localhost:9002` (central-b).
## Test Users
All passwords are `password`. See `infra/glauth/config.toml` for the full list.
| Username | Roles |
|----------|-------|
| `admin` | Admin |
| `designer` | Design |
| `deployer` | Deployment |
| `multi-role` | Admin, Design, Deployment |
## Notes
- Reference deployment for the project — when a change ships, it gets validated here first.
- Concurrent with [`docker-cluster-env2`](docker-cluster-env2.md) on the same host; the two stacks share the `scadalink-net` network and `infra/` services but use disjoint host ports (`90XX` vs `91XX`) and databases.
- Detailed setup, failover testing, and build-cache notes live in [`docker/README.md`](../docker/README.md).