mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-25 03:43:05 +08:00
208 lines
8.8 KiB
YAML
208 lines
8.8 KiB
YAML
# RedundantClient + RedundantServer — ONE compose file, the full HA matrix, env-driven.
|
|
#
|
|
# Server consistency (eventual|strong) and client mode (independent|coordinated, and the
|
|
# coordinated store's eventual|strong flavor) are chosen INDEPENDENTLY by an environment
|
|
# variable, so any cell of the matrix runs from this single file — e.g. a strong server
|
|
# against an eventual coordinated client. Eventual is the default. Both sides log their
|
|
# failover / HA behavior, INCLUDING data loss.
|
|
#
|
|
# Selection is by Docker COMPOSE PROFILES (an env var). The adjacent `.env` sets the
|
|
# eventual default, so a bare `up` needs no flags. Override the env var to pick any cell:
|
|
#
|
|
# Profiles are two independent groups — pick one server + one client:
|
|
# server-eventual scalable active/active servers, CRDT gossip + DNS discovery (scales)
|
|
# server-strong fixed 3-node active/passive Raft servers, no data loss (no scale)
|
|
# client-independent N managed clients, each fails over on its own (scales)
|
|
# client-coordinated fixed 3-node client replica set, one active client (no scale)
|
|
#
|
|
# For client-coordinated, CLIENT_CONSISTENCY (eventual|strong, default eventual) selects
|
|
# the shared-store flavor used to hand the session over to the promoted client.
|
|
#
|
|
# Run from the repository ROOT (the build context is the repo root):
|
|
#
|
|
# # DEFAULT: 3 eventual servers + 2 independent clients (failover + data loss demoable).
|
|
# docker compose -f samples/RedundantClient/docker-compose.yml up --build
|
|
#
|
|
# # Scale the eventual server and the independent clients independently:
|
|
# docker compose -f samples/RedundantClient/docker-compose.yml up --build \
|
|
# --scale server=5 --scale client=3
|
|
#
|
|
# # STRONG server (no data loss) x independent clients:
|
|
# COMPOSE_PROFILES=server-strong,client-independent \
|
|
# docker compose -f samples/RedundantClient/docker-compose.yml up --build
|
|
#
|
|
# # Any matrix cell, e.g. STRONG server x EVENTUAL coordinated client:
|
|
# COMPOSE_PROFILES=server-strong,client-coordinated CLIENT_CONSISTENCY=eventual \
|
|
# docker compose -f samples/RedundantClient/docker-compose.yml up --build
|
|
#
|
|
# Only the eventual server (`server`) and the independent clients (`client`) are
|
|
# --scale-able; the strong server and the coordinated client are fixed odd Raft quorums
|
|
# (3 nodes) — a quorum needs stable identities, so they are named replicas.
|
|
#
|
|
# No host ports are published (so --scale works); clients run inside the compose network
|
|
# and reach the servers through the `server` DNS name (the strong replicas alias it).
|
|
|
|
x-server-image: &server-image
|
|
build:
|
|
context: ../..
|
|
dockerfile: samples/RedundantServer/Dockerfile
|
|
image: opcua-redundant-server
|
|
|
|
x-client-image: &client-image
|
|
build:
|
|
context: ../..
|
|
dockerfile: samples/RedundantClient/Dockerfile
|
|
image: opcua-redundant-client
|
|
# Connect to the `server` name (the eventual service, or the strong server-a alias);
|
|
# WithServerRedundancy() discovers the redundant set from the connected server and fails
|
|
# over transparently. --duration 00:00:00 runs until the container is stopped.
|
|
command:
|
|
- "--server"
|
|
- "opc.tcp://server:62543/RedundantServer"
|
|
- "--autoaccept"
|
|
- "--nosecurity"
|
|
- "--duration"
|
|
- "00:00:00"
|
|
|
|
# Shared environment for the three named strong (Raft) server replicas.
|
|
x-strong-server-env: &strong-server-env
|
|
HA_MODE: ap
|
|
HA_CONSISTENCY: strong
|
|
REDUNDANCY_MODE: hot
|
|
# Isolated demo posture: run without record protection / gossip auth. For a secure run,
|
|
# REMOVE HA_INSECURE and configure gossip mutual TLS + HA_RECORD_KEY (see the READMEs).
|
|
HA_INSECURE: "true"
|
|
HA_RAFT_MEMBERS: "3"
|
|
HA_RAFT_BIND: "tcp://0.0.0.0:6560"
|
|
|
|
# Shared environment for the three named coordinated client replicas. CLIENT_CONSISTENCY
|
|
# picks the shared-store flavor (eventual = CRDT bulk + Raft strong keyspaces; strong = Raft).
|
|
x-coordinated-client-env: &coordinated-client-env
|
|
CLIENT_MODE: "${CLIENT_CONSISTENCY:-eventual}"
|
|
CLIENT_RAFT_MEMBERS: "3"
|
|
CLIENT_RAFT_BIND: "tcp://0.0.0.0:6561"
|
|
CLIENT_GOSSIP_PORT: "4841"
|
|
CLIENT_INSECURE: "true"
|
|
|
|
services:
|
|
# ----- SERVER: eventual, active/active, scalable (profile: server-eventual) -----
|
|
server:
|
|
<<: *server-image
|
|
profiles: ["server-eventual"]
|
|
deploy:
|
|
replicas: ${SERVER_REPLICAS:-3}
|
|
environment:
|
|
HA_MODE: aa
|
|
HA_CONSISTENCY: eventual
|
|
HA_PEER_DISCOVERY: dns
|
|
HA_SERVICE_NAME: server
|
|
HA_GOSSIP_PORT: "4840"
|
|
# Advertise the round-robin `server` endpoint (a DNS name), so the listener binds to
|
|
# all interfaces while returning opc.tcp://server:62543 to clients. Combined with the
|
|
# shared ApplicationUri, a session reconnect resolves `server` to a SURVIVING replica,
|
|
# so failover works over DNS. Each replica keeps a unique HA_NODE_ID for ActiveReplica.
|
|
HA_HOST: server
|
|
# The scalable `server` name round-robins across replicas, and CreateSession validates
|
|
# the client-supplied serverUri against the replica's ApplicationUri. Share ONE
|
|
# ApplicationUri so a connection landing on any replica is accepted (this demo uses
|
|
# SecurityMode None, so no shared certificate is required).
|
|
HA_APPLICATION_URI: urn:OPCFoundation:RedundantServer:eventual
|
|
HA_INSECURE: "true"
|
|
entrypoint:
|
|
- /bin/sh
|
|
- -c
|
|
- |
|
|
export HA_NODE_ID="$${HA_NODE_ID:-$$(hostname)}"
|
|
exec dotnet RedundantServer.dll --port 62543
|
|
|
|
# ----- SERVER: strong, active/passive 3-node Raft (profile: server-strong) -----
|
|
server-a:
|
|
<<: *server-image
|
|
profiles: ["server-strong"]
|
|
hostname: server-a
|
|
# Alias the `server` name onto server-a so the clients' opc.tcp://server URL resolves
|
|
# in the strong profile too (WithServerRedundancy discovers server-b / server-c).
|
|
networks:
|
|
default:
|
|
aliases:
|
|
- server
|
|
command: ["--port", "62543"]
|
|
environment:
|
|
<<: *strong-server-env
|
|
HA_HOST: server-a
|
|
HA_NODE_ID: replica-a
|
|
HA_RAFT_ID: "1"
|
|
HA_RAFT_PEERS: "tcp://server-b:6560,tcp://server-c:6560"
|
|
HA_REDUNDANT_PEERS: "urn:localhost:OPCFoundation:RedundantServer:replica-b|RedundantServer replica-b|opc.tcp://server-b:62543/RedundantServer,urn:localhost:OPCFoundation:RedundantServer:replica-c|RedundantServer replica-c|opc.tcp://server-c:62543/RedundantServer"
|
|
|
|
server-b:
|
|
<<: *server-image
|
|
profiles: ["server-strong"]
|
|
hostname: server-b
|
|
command: ["--port", "62543"]
|
|
environment:
|
|
<<: *strong-server-env
|
|
HA_HOST: server-b
|
|
HA_NODE_ID: replica-b
|
|
HA_RAFT_ID: "2"
|
|
HA_RAFT_PEERS: "tcp://server-a:6560,tcp://server-c:6560"
|
|
HA_REDUNDANT_PEERS: "urn:localhost:OPCFoundation:RedundantServer:replica-a|RedundantServer replica-a|opc.tcp://server-a:62543/RedundantServer,urn:localhost:OPCFoundation:RedundantServer:replica-c|RedundantServer replica-c|opc.tcp://server-c:62543/RedundantServer"
|
|
|
|
server-c:
|
|
<<: *server-image
|
|
profiles: ["server-strong"]
|
|
hostname: server-c
|
|
command: ["--port", "62543"]
|
|
environment:
|
|
<<: *strong-server-env
|
|
HA_HOST: server-c
|
|
HA_NODE_ID: replica-c
|
|
HA_RAFT_ID: "3"
|
|
HA_RAFT_PEERS: "tcp://server-a:6560,tcp://server-b:6560"
|
|
HA_REDUNDANT_PEERS: "urn:localhost:OPCFoundation:RedundantServer:replica-a|RedundantServer replica-a|opc.tcp://server-a:62543/RedundantServer,urn:localhost:OPCFoundation:RedundantServer:replica-b|RedundantServer replica-b|opc.tcp://server-b:62543/RedundantServer"
|
|
|
|
# ----- CLIENT: independent managed clients, scalable (profile: client-independent) -----
|
|
client:
|
|
<<: *client-image
|
|
profiles: ["client-independent"]
|
|
deploy:
|
|
replicas: ${CLIENT_REPLICAS:-2}
|
|
environment:
|
|
CLIENT_MODE: independent
|
|
|
|
# ----- CLIENT: coordinated 3-node replica set (profile: client-coordinated) -----
|
|
# Exactly one client is active; the others are standby and take over on leader loss,
|
|
# continuing from the shared session state. Fixed odd Raft quorum (3), not --scale-able.
|
|
client-a:
|
|
<<: *client-image
|
|
profiles: ["client-coordinated"]
|
|
hostname: client-a
|
|
environment:
|
|
<<: *coordinated-client-env
|
|
CLIENT_NODE_ID: replica-a
|
|
CLIENT_RAFT_ID: "1"
|
|
CLIENT_RAFT_PEERS: "tcp://client-b:6561,tcp://client-c:6561"
|
|
CLIENT_GOSSIP_PEERS: "client-b:4841,client-c:4841"
|
|
|
|
client-b:
|
|
<<: *client-image
|
|
profiles: ["client-coordinated"]
|
|
hostname: client-b
|
|
environment:
|
|
<<: *coordinated-client-env
|
|
CLIENT_NODE_ID: replica-b
|
|
CLIENT_RAFT_ID: "2"
|
|
CLIENT_RAFT_PEERS: "tcp://client-a:6561,tcp://client-c:6561"
|
|
CLIENT_GOSSIP_PEERS: "client-a:4841,client-c:4841"
|
|
|
|
client-c:
|
|
<<: *client-image
|
|
profiles: ["client-coordinated"]
|
|
hostname: client-c
|
|
environment:
|
|
<<: *coordinated-client-env
|
|
CLIENT_NODE_ID: replica-c
|
|
CLIENT_RAFT_ID: "3"
|
|
CLIENT_RAFT_PEERS: "tcp://client-a:6561,tcp://client-b:6561"
|
|
CLIENT_GOSSIP_PEERS: "client-a:4841,client-b:4841"
|