refactor(compose): split monolith into stella-infra + stella-services

- Extract infrastructure (postgres, valkey, rustfs, zot, rekor) to docker-compose.stella-infra.yml
- Move application services to docker-compose.stella-services.yml
- Convert scalar YAML anchors to .env variables for cross-file compatibility
- Duplicate structural anchors locally in services file
- Remove cross-file depends_on (services already have connection retry)
- Legacy monolith retained for backwards compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-08 13:11:47 +03:00
parent 53f294400f
commit b3198a66c7
4 changed files with 2421 additions and 118 deletions

View File

@@ -1,11 +1,14 @@
# ============================================================================= # =============================================================================
# STELLA OPS ENVIRONMENT CONFIGURATION # STELLA OPS ENVIRONMENT CONFIGURATION
# ============================================================================= # =============================================================================
# Main environment template for docker-compose.stella-ops.yml # Environment variables for the Stella Ops Docker Compose stack.
# Copy to .env and customize for your deployment.
# #
# Usage: # Usage (split infra/services files -- preferred):
# cp env/stellaops.env.example .env # docker compose \
# -f docker-compose.stella-infra.yml \
# -f docker-compose.stella-services.yml up -d
#
# Usage (legacy monolith):
# docker compose -f docker-compose.stella-ops.yml up -d # docker compose -f docker-compose.stella-ops.yml up -d
# #
# ============================================================================= # =============================================================================
@@ -26,6 +29,23 @@ VALKEY_PORT=6379
# RustFS Object Storage # RustFS Object Storage
RUSTFS_HTTP_PORT=8080 RUSTFS_HTTP_PORT=8080
# =============================================================================
# SHARED CONNECTION STRINGS (used by docker-compose.stella-services.yml)
# =============================================================================
# These replace YAML anchors (*postgres-connection, *postgres-authority-connection)
# that cannot cross Docker Compose file boundaries.
STELLAOPS_POSTGRES_CONNECTION=Host=db.stella-ops.local;Port=5432;Database=stellaops_platform;Username=stellaops;Password=stellaops;Maximum Pool Size=50
STELLAOPS_POSTGRES_AUTHORITY_CONNECTION=Host=db.stella-ops.local;Port=5432;Database=stellaops_authority;Username=stellaops;Password=stellaops;Maximum Pool Size=20;Minimum Pool Size=2
# =============================================================================
# SHARED VOLUME MOUNTS (used by docker-compose.stella-services.yml)
# =============================================================================
# These replace YAML anchors (*cert-volume, *ca-bundle) for cross-file usage.
STELLAOPS_CERT_VOLUME=../../etc/authority/keys:/app/etc/certs:ro
STELLAOPS_CA_BUNDLE_VOLUME=./combined-ca-bundle.crt:/etc/ssl/certs/ca-certificates.crt:ro
# ============================================================================= # =============================================================================
# CORE SERVICES # CORE SERVICES
# ============================================================================= # =============================================================================

View File

@@ -0,0 +1,188 @@
# =============================================================================
# STELLA OPS - INFRASTRUCTURE SERVICES
# =============================================================================
# PostgreSQL, Valkey, SeaweedFS (S3-compatible), Zot (OCI registry), Rekor v2.
#
# This file provides the shared infrastructure layer. Application services
# are defined in docker-compose.stella-services.yml and connect via the
# "stellaops" network created here.
#
# Usage (infra only):
# docker compose -f docker-compose.stella-infra.yml up -d
#
# Usage (full stack):
# docker compose \
# -f docker-compose.stella-infra.yml \
# -f docker-compose.stella-services.yml up -d
#
# With overlays (e.g., telemetry, compliance):
# docker compose \
# -f docker-compose.stella-infra.yml \
# -f docker-compose.stella-services.yml \
# -f docker-compose.telemetry.yml up -d
#
# =============================================================================
x-release-labels: &release-labels
com.stellaops.release.version: "2025.10.0"
com.stellaops.release.channel: "stable"
com.stellaops.profile: "default"
networks:
stellaops:
driver: bridge
name: stellaops
frontdoor:
external: true
name: ${FRONTDOOR_NETWORK:-stellaops_frontdoor}
volumes:
postgres-data:
valkey-data:
rustfs-data:
rekor-tiles-data:
registry-data:
services:
# ===========================================================================
# INFRASTRUCTURE SERVICES
# ===========================================================================
postgres:
image: docker.io/library/postgres:18.1
container_name: stellaops-postgres
restart: unless-stopped
environment:
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres-data:/var/lib/postgresql/data
- ./postgres-init:/docker-entrypoint-initdb.d:ro
ports:
- "127.1.1.1:${POSTGRES_PORT:-5432}:5432"
networks:
stellaops:
aliases:
- db.stella-ops.local
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-stellaops} -d ${POSTGRES_DB:-stellaops_platform}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
labels: *release-labels
valkey:
image: docker.io/valkey/valkey:9.0.1
container_name: stellaops-valkey
restart: unless-stopped
command: ["valkey-server", "--appendonly", "yes"]
volumes:
- valkey-data:/data
ports:
- "127.1.1.2:${VALKEY_PORT:-6379}:6379"
networks:
stellaops:
aliases:
- cache.stella-ops.local
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
labels: *release-labels
rustfs:
image: chrislusf/seaweedfs:latest
container_name: stellaops-rustfs
command: ["server", "-s3", "-s3.port=8333", "-volume.port=8080", "-dir=/data"]
restart: unless-stopped
volumes:
- rustfs-data:/data
ports:
- "127.1.1.3:${RUSTFS_HTTP_PORT:-8333}:8333"
networks:
stellaops:
aliases:
- s3.stella-ops.local
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:8333/status || exit 1"]
interval: 30s
timeout: 10s
retries: 3
labels: *release-labels
registry:
image: ghcr.io/project-zot/zot-linux-amd64:v2.1.3
container_name: stellaops-registry
restart: unless-stopped
volumes:
- registry-data:/var/lib/registry
- ./zot-config.json:/etc/zot/config.json:ro
ports:
- "127.1.1.5:80:5000"
networks:
stellaops:
aliases:
- registry.stella-ops.local
healthcheck:
disable: true
labels: *release-labels
rekor-v2:
image: ${REKOR_TILES_IMAGE:-ghcr.io/sigstore/rekor-tiles:latest}
container_name: stellaops-rekor
restart: on-failure:5
command:
- rekor-server
- serve
- --http-address
- 0.0.0.0
- --http-port
- "3322"
- --grpc-address
- 0.0.0.0
- --grpc-port
- "3323"
- --signer-filepath
- /etc/rekor/signer.pem
- --gcp-bucket
- ${REKOR_GCP_BUCKET:-stellaops-rekor-dev}
- --gcp-spanner
- ${REKOR_GCP_SPANNER:-projects/stellaops-dev/instances/rekor/databases/rekor}
volumes:
- rekor-tiles-data:/var/lib/rekor-tiles
- ../../etc/authority/keys/signing-dev.pem:/etc/rekor/signer.pem:ro
ports:
- "127.1.1.4:${REKOR_PORT:-3322}:3322"
networks:
stellaops:
aliases:
- rekor.stella-ops.local
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3322/api/v1/log"]
interval: 30s
timeout: 10s
retries: 3
profiles: ["sigstore-local"]
labels:
<<: *release-labels
com.stellaops.component: "rekor-v2"
rekor-cli:
image: ghcr.io/sigstore/rekor-cli:v1.4.3
entrypoint: ["rekor-cli"]
command: ["version"]
profiles: ["sigstore"]
networks: [stellaops]
labels: *release-labels
cosign:
image: ghcr.io/sigstore/cosign:v3.0.4
entrypoint: ["cosign"]
command: ["version"]
profiles: ["sigstore"]
networks: [stellaops]
labels: *release-labels

View File

@@ -1,10 +1,15 @@
# ============================================================================= # =============================================================================
# STELLA OPS - MAIN STACK # STELLA OPS - MAIN STACK (LEGACY MONOLITH)
# ============================================================================= # =============================================================================
# Consolidated Docker Compose for the complete StellaOps platform. # Consolidated Docker Compose for the complete StellaOps platform.
# Infrastructure: PostgreSQL 18.1, Valkey 9.0.1, SeaweedFS (S3), Rekor v2, Zot (OCI) # Infrastructure: PostgreSQL 18.1, Valkey 9.0.1, SeaweedFS (S3), Rekor v2, Zot (OCI)
# #
# Usage: # PREFERRED: Use the split files instead of this monolith:
# docker compose \
# -f docker-compose.stella-infra.yml \
# -f docker-compose.stella-services.yml up -d
#
# Legacy monolith usage (this file):
# docker compose -f devops/compose/docker-compose.stella-ops.yml up -d # docker compose -f devops/compose/docker-compose.stella-ops.yml up -d
# #
# With Sigstore tools: # With Sigstore tools:
@@ -426,7 +431,7 @@ services:
STELLAOPS_VEXLENS_URL: "http://vexlens.stella-ops.local" STELLAOPS_VEXLENS_URL: "http://vexlens.stella-ops.local"
STELLAOPS_VULNEXPLORER_URL: "http://vulnexplorer.stella-ops.local" STELLAOPS_VULNEXPLORER_URL: "http://vulnexplorer.stella-ops.local"
STELLAOPS_POLICY_ENGINE_URL: "http://policy-engine.stella-ops.local" STELLAOPS_POLICY_ENGINE_URL: "http://policy-engine.stella-ops.local"
STELLAOPS_POLICY_GATEWAY_URL: "http://policy-gateway.stella-ops.local" # STELLAOPS_POLICY_GATEWAY_URL removed: gateway merged into policy-engine
STELLAOPS_RISKENGINE_URL: "http://riskengine.stella-ops.local" STELLAOPS_RISKENGINE_URL: "http://riskengine.stella-ops.local"
STELLAOPS_JOBENGINE_URL: "http://jobengine.stella-ops.local" STELLAOPS_JOBENGINE_URL: "http://jobengine.stella-ops.local"
STELLAOPS_TASKRUNNER_URL: "http://taskrunner.stella-ops.local" STELLAOPS_TASKRUNNER_URL: "http://taskrunner.stella-ops.local"
@@ -439,7 +444,7 @@ services:
STELLAOPS_FINDINGS_LEDGER_URL: "http://findings.stella-ops.local" STELLAOPS_FINDINGS_LEDGER_URL: "http://findings.stella-ops.local"
STELLAOPS_DOCTOR_URL: "http://doctor.stella-ops.local" STELLAOPS_DOCTOR_URL: "http://doctor.stella-ops.local"
STELLAOPS_OPSMEMORY_URL: "http://opsmemory.stella-ops.local" STELLAOPS_OPSMEMORY_URL: "http://opsmemory.stella-ops.local"
STELLAOPS_NOTIFIER_URL: "http://notifier.stella-ops.local" STELLAOPS_NOTIFIER_URL: "http://notify.stella-ops.local"
STELLAOPS_NOTIFY_URL: "http://notify.stella-ops.local" STELLAOPS_NOTIFY_URL: "http://notify.stella-ops.local"
STELLAOPS_SIGNER_URL: "http://signer.stella-ops.local" STELLAOPS_SIGNER_URL: "http://signer.stella-ops.local"
STELLAOPS_SMREMOTE_URL: "http://smremote.stella-ops.local" STELLAOPS_SMREMOTE_URL: "http://smremote.stella-ops.local"
@@ -1074,50 +1079,6 @@ services:
stellaops: stellaops:
aliases: aliases:
- policy-engine.stella-ops.local - policy-engine.stella-ops.local
frontdoor: {}
healthcheck:
test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/$(hostname)/80'"]
<<: *healthcheck-tcp
labels: *release-labels
# --- Slot 15: Policy Gateway -----------------------------------------------
policy:
<<: *resources-medium
image: stellaops/policy:dev
container_name: stellaops-policy
restart: unless-stopped
depends_on: *depends-infra
environment:
ASPNETCORE_URLS: "http://+:8084"
<<: [*kestrel-cert, *router-microservice-defaults, *gc-medium]
ConnectionStrings__Default: *postgres-connection
ConnectionStrings__Redis: "cache.stella-ops.local:6379"
Postgres__Policy__ConnectionString: *postgres-connection
PolicyGateway__ResourceServer__Authority: "https://authority.stella-ops.local/"
PolicyGateway__ResourceServer__RequireHttpsMetadata: "false"
PolicyGateway__ResourceServer__Audiences__0: ""
PolicyGateway__ResourceServer__RequiredScopes__0: "policy:read"
PolicyGateway__ResourceServer__BypassNetworks__0: "172.19.0.0/16"
# In local compose, callers should forward their own token. Disable fallback
# client-credentials to avoid 500s on invalid_scope when no Authorization header is present.
PolicyGateway__PolicyEngine__ClientCredentials__Enabled: "false"
# Bootstrap-prefixed vars (read by StellaOpsConfigurationBootstrapper before DI)
STELLAOPS_POLICY_GATEWAY_PolicyGateway__ResourceServer__Authority: "https://authority.stella-ops.local/"
STELLAOPS_POLICY_GATEWAY_PolicyGateway__ResourceServer__RequireHttpsMetadata: "false"
STELLAOPS_POLICY_GATEWAY_PolicyGateway__ResourceServer__Audiences__0: ""
STELLAOPS_POLICY_GATEWAY_PolicyGateway__ResourceServer__RequiredScopes__0: "policy:read"
STELLAOPS_POLICY_GATEWAY_PolicyGateway__PolicyEngine__ClientCredentials__Enabled: "false"
STELLAOPS_POLICY_GATEWAY_Postgres__Policy__ConnectionString: *postgres-connection
Router__Enabled: "${POLICY_GATEWAY_ROUTER_ENABLED:-true}"
Router__Messaging__ConsumerGroup: "policy-gateway"
volumes:
- *cert-volume
- *ca-bundle
ports:
- "127.1.0.15:80:80"
networks:
stellaops:
aliases:
- policy-gateway.stella-ops.local - policy-gateway.stella-ops.local
frontdoor: {} frontdoor: {}
healthcheck: healthcheck:
@@ -1125,6 +1086,14 @@ services:
<<: *healthcheck-tcp <<: *healthcheck-tcp
labels: *release-labels labels: *release-labels
# --- Slot 15: Policy Gateway (DEPRECATED - merged into policy-engine above)
# Kept commented out for reference; remove in next compose cleanup.
# policy:
# <<: *resources-medium
# image: stellaops/policy:dev
# container_name: stellaops-policy
# ... (merged into policy-engine service above)
# --- Slot 16: RiskEngine [src/Findings/StellaOps.RiskEngine.*] --------------- # --- Slot 16: RiskEngine [src/Findings/StellaOps.RiskEngine.*] ---------------
riskengine-web: riskengine-web:
<<: *resources-medium <<: *resources-medium
@@ -1658,43 +1627,43 @@ services:
<<: *healthcheck-tcp <<: *healthcheck-tcp
labels: *release-labels labels: *release-labels
# --- Slot 28: Notifier ---------------------------------------------------- # --- Slot 28: Notifier (MERGED into notify-web — kept commented for rollback) ---
notifier-web: # notifier-web:
<<: *resources-medium # <<: *resources-medium
image: stellaops/notifier-web:dev # image: stellaops/notifier-web:dev
container_name: stellaops-notifier-web # container_name: stellaops-notifier-web
restart: unless-stopped # restart: unless-stopped
depends_on: *depends-infra # depends_on: *depends-infra
environment: # environment:
ASPNETCORE_URLS: "http://+:8080" # ASPNETCORE_URLS: "http://+:8080"
<<: [*kestrel-cert, *router-microservice-defaults, *gc-medium] # <<: [*kestrel-cert, *router-microservice-defaults, *gc-medium]
ConnectionStrings__Default: *postgres-connection # ConnectionStrings__Default: *postgres-connection
ConnectionStrings__Redis: "cache.stella-ops.local:6379" # ConnectionStrings__Redis: "cache.stella-ops.local:6379"
Authority__ResourceServer__Authority: "https://authority.stella-ops.local/" # Authority__ResourceServer__Authority: "https://authority.stella-ops.local/"
Authority__ResourceServer__MetadataAddress: "https://authority.stella-ops.local/.well-known/openid-configuration" # Authority__ResourceServer__MetadataAddress: "https://authority.stella-ops.local/.well-known/openid-configuration"
Authority__ResourceServer__RequireHttpsMetadata: "false" # Authority__ResourceServer__RequireHttpsMetadata: "false"
Authority__ResourceServer__Audiences__0: "" # Authority__ResourceServer__Audiences__0: ""
Authority__ResourceServer__BypassNetworks__0: "172.19.0.0/16" # Authority__ResourceServer__BypassNetworks__0: "172.19.0.0/16"
Authority__ResourceServer__BypassNetworks__1: "127.0.0.1/32" # Authority__ResourceServer__BypassNetworks__1: "127.0.0.1/32"
Authority__ResourceServer__BypassNetworks__2: "::1/128" # Authority__ResourceServer__BypassNetworks__2: "::1/128"
Authority__ResourceServer__BypassNetworks__3: "0.0.0.0/0" # Authority__ResourceServer__BypassNetworks__3: "0.0.0.0/0"
Authority__ResourceServer__BypassNetworks__4: "::/0" # Authority__ResourceServer__BypassNetworks__4: "::/0"
Router__Enabled: "${NOTIFIER_ROUTER_ENABLED:-true}" # Router__Enabled: "${NOTIFIER_ROUTER_ENABLED:-true}"
Router__Messaging__ConsumerGroup: "notifier" # Router__Messaging__ConsumerGroup: "notifier"
volumes: # volumes:
- *cert-volume # - *cert-volume
- *ca-bundle # - *ca-bundle
ports: # ports:
- "127.1.0.28:80:80" # - "127.1.0.28:80:80"
networks: # networks:
stellaops: # stellaops:
aliases: # aliases:
- notifier.stella-ops.local # - notifier.stella-ops.local
frontdoor: {} # frontdoor: {}
healthcheck: # healthcheck:
test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/$(hostname)/80'"] # test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/$(hostname)/80'"]
<<: *healthcheck-tcp # <<: *healthcheck-tcp
labels: *release-labels # labels: *release-labels
notifier-worker: notifier-worker:
<<: *resources-light <<: *resources-light
@@ -1750,6 +1719,7 @@ services:
stellaops: stellaops:
aliases: aliases:
- notify.stella-ops.local - notify.stella-ops.local
- notifier.stella-ops.local # merged from notifier-web
frontdoor: {} frontdoor: {}
healthcheck: healthcheck:
test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/$(hostname)/80'"] test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/$(hostname)/80'"]
@@ -1786,33 +1756,7 @@ services:
<<: *healthcheck-tcp <<: *healthcheck-tcp
labels: *release-labels labels: *release-labels
# --- Slot 31: SmRemote ---------------------------------------------------- # --- Slot 31: SmRemote (moved to docker-compose.crypto-provider.smremote.yml) ---
smremote:
<<: *resources-light
image: stellaops/smremote:dev
container_name: stellaops-smremote
restart: unless-stopped
depends_on: *depends-infra
environment:
ASPNETCORE_URLS: "http://+:8080"
<<: [*kestrel-cert, *router-microservice-defaults, *gc-light]
ConnectionStrings__Default: *postgres-connection
ConnectionStrings__Redis: "cache.stella-ops.local:6379"
Router__Enabled: "${SMREMOTE_ROUTER_ENABLED:-true}"
Router__Messaging__ConsumerGroup: "smremote"
volumes:
- *cert-volume
ports:
- "127.1.0.31:80:80"
networks:
stellaops:
aliases:
- smremote.stella-ops.local
frontdoor: {}
healthcheck:
test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/$(hostname)/8080'"]
<<: *healthcheck-tcp
labels: *release-labels
# --- Slot 32: AirGap Controller -------------------------------------------- # --- Slot 32: AirGap Controller --------------------------------------------
airgap-controller: airgap-controller:
@@ -2332,7 +2276,7 @@ services:
ADVISORYAI__KnowledgeSearch__VexAdapterEnabled: "true" ADVISORYAI__KnowledgeSearch__VexAdapterEnabled: "true"
ADVISORYAI__KnowledgeSearch__VexAdapterBaseUrl: "http://concelier.stella-ops.local" ADVISORYAI__KnowledgeSearch__VexAdapterBaseUrl: "http://concelier.stella-ops.local"
ADVISORYAI__KnowledgeSearch__PolicyAdapterEnabled: "true" ADVISORYAI__KnowledgeSearch__PolicyAdapterEnabled: "true"
ADVISORYAI__KnowledgeSearch__PolicyAdapterBaseUrl: "http://policy-gateway.stella-ops.local" ADVISORYAI__KnowledgeSearch__PolicyAdapterBaseUrl: "http://policy-engine.stella-ops.local"
Router__Enabled: "${ADVISORYAI_ROUTER_ENABLED:-true}" Router__Enabled: "${ADVISORYAI_ROUTER_ENABLED:-true}"
Router__Messaging__ConsumerGroup: "advisoryai" Router__Messaging__ConsumerGroup: "advisoryai"
ports: ports:
@@ -2375,7 +2319,7 @@ services:
ADVISORYAI__KnowledgeSearch__VexAdapterEnabled: "true" ADVISORYAI__KnowledgeSearch__VexAdapterEnabled: "true"
ADVISORYAI__KnowledgeSearch__VexAdapterBaseUrl: "http://concelier.stella-ops.local" ADVISORYAI__KnowledgeSearch__VexAdapterBaseUrl: "http://concelier.stella-ops.local"
ADVISORYAI__KnowledgeSearch__PolicyAdapterEnabled: "true" ADVISORYAI__KnowledgeSearch__PolicyAdapterEnabled: "true"
ADVISORYAI__KnowledgeSearch__PolicyAdapterBaseUrl: "http://policy-gateway.stella-ops.local" ADVISORYAI__KnowledgeSearch__PolicyAdapterBaseUrl: "http://policy-engine.stella-ops.local"
volumes: volumes:
- *cert-volume - *cert-volume
networks: networks:

File diff suppressed because it is too large Load Diff