Skip to content

Latest commit

 

History

History
201 lines (144 loc) · 10.6 KB

local-development.adoc

File metadata and controls

201 lines (144 loc) · 10.6 KB

Local development

The local stack runs with Docker Compose from the root of this repository. compose.yaml only includes the files under compose/, one per layer, and every service belongs to a profile.

Prerequisites

  • Docker Engine or Docker Desktop with Compose 2.20 or newer (include: support).

  • GNU Make.

  • Terraform 1.6 or newer, to provision Keycloak.

  • For running services from source: Node.js and the rmu-* checkouts next to this repository.

First run

cp .env.example .env     # local-only credentials and defaults
make up                  # starts the profiles in COMPOSE_PROFILES
make keycloak-init       # once Keycloak answers on http://localhost:8090/sso

Before the Terraform step, create terraform/stacks/keycloak-local/terraform.tfvars from its .example; see operations/keycloak-terraform.adoc. The APIs start before the realm exists and fail token validation until it has been applied.

Profiles

Profile File Services

infrastructure

compose/infrastructure.yaml

MongoDB, PostgreSQL, Keycloak, Kafka, Mailpit, the local S3 (rmu-s3, SeaweedFS).

apis

compose/apis.yaml

Every backend API, from the published labcabrera/rmu-api-* images.

mfes

compose/mfes.yaml

Micro-frontends as static images (shell, core, strategic, tactical, npcs, items, spells, wiki, assets), served through the gateway; see architecture/frontends.adoc.

tools

compose/tools.yaml

Kafka UI.

gateway

compose/gateway.yaml

Caddy on port 80: the shell at /, the remote MFEs at /mfe/<name>/, the APIs at /api/<name>/, Keycloak at /sso/.

observability

compose/observability.yaml

Grafana, Loki, Tempo, Prometheus and an OTel collector (grafana/otel-lgtm).

COMPOSE_PROFILES in .env selects what docker compose up -d starts. The default is infrastructure,apis,mfes,gateway,tools. On the command line, --profile replaces it for that run.

Important
apis and tools depend on services of infrastructure. Without it, Compose fails with depends on undefined service. The make up-* targets add it for you.

Common combinations

Goal Command

Everything in COMPOSE_PROFILES

make up

Only the infrastructure, to run APIs from source

make up-infra

Infrastructure and APIs

make up-apis

Infrastructure and tools

docker compose --profile infrastructure --profile tools up -d

Add the gateway or observability to what is running

docker compose --profile gateway up -d

Validate the files and list the active services

make config

make down, make ps and make logs [s=<service>] act on every profile, so nothing is left running by accident. make down keeps the volumes; docker compose --profile '*' down -v also deletes the local data.

Endpoints

Service Address

Keycloak

http://localhost/sso (gateway), http://localhost:8090/sso (admin console)

Mailpit (realm mail)

http://localhost:8025

MongoDB

mongodb://admin:admin@localhost:27017/?authSource=admin

Kafka from the host

localhost:9093

Kafka UI

http://localhost:8091

Local S3

http://localhost:8333 (aws --endpoint-url http://localhost:8333); uploads served by the gateway at /media/

Gateway

http://localhost (RMU_GATEWAY_PORT)

Grafana

http://localhost:3000 (admin/admin)

OTLP

localhost:4317 (gRPC), localhost:4318 (HTTP)

APIs

http://localhost:<port>/v1, ports in architecture/services.adoc

Image versions

Each image is labcabrera/rmu-api-<name> or labcabrera/rmu-mfe-<name> with the tag ${RMU_<KIND>_<NAME>_VERSION}, latest by default (local for the items, spells and wiki MFEs, which have no published image yet). latest is fine for day-to-day work; set explicit versions in .env to reproduce a release or a bug.

To run your checkouts as containers:

make up-local                                       # build :local images of every checkout and start the stack with them
make up-local p="rmu-api-tactical rmu-mfe-tactical" # only some projects
make build-local p=rmu-mfe-core NO_CACHE=1          # only build

up-local also runs every project that already has a :local image; a RMU_*_VERSION set in the shell wins. TAG= changes the tag, and BASE_DIR (or RMU_WORKSPACE_DIR) the folder holding the checkouts.

Running an API from source

  1. Start everything else, then stop the container of the API you are working on, for example docker compose stop rmu-api-core.

  2. In the API repository, copy .env.example to .env. From the host, use localhost addresses: Mongo localhost:27017, Keycloak http://localhost:8090/sso for the JWKS and token URIs, the issuer http://localhost/sso/realms/rmu (RMU_IAM_ISSUER_URI) and the audience rmu-api (RMU_IAM_AUDIENCE), Kafka localhost:9093.

  3. pnpm start:dev (Node 24, corepack enable).

Other containers still call http://rmu-api-core:3001, which no longer resolves. To exercise those calls, run the API as a container built from your checkout and attached to the rmu-network network. The APIs with releases do this with make docker-build docker-run (it needs a .env.docker), or use make up-local p=rmu-api-<name> here.

Running the MFEs from source

make mfes-dev                        # MFEs next to this repository
make mfes-dev BASE_DIR=~/src/rmu     # elsewhere

It opens a gnome-terminal tab per checked-out MFE running pnpm run start:live, plus rmu-mfe-assets. Without gnome-terminal, it asks you to start each one by hand. Stop the MFE containers (or leave mfes out of the profiles) and open http://localhost: the gateway falls back to the dev servers; see [_working_on_one_mfe].

Gateway

The gateway profile runs Caddy on port 80 (RMU_GATEWAY_PORT). It is the single origin of the application, http://localhost. Routes are in compose/config/caddy/Caddyfile:

Path Upstream

/

rmu-mfe-shell, or its dev server on the host (:8080) when the container is stopped

/mfe/core/, /mfe/strategic/, /mfe/tactical/, /mfe/npcs/, /mfe/items/, /mfe/spells/, /mfe/wiki/

The remote’s container, or its dev server on the host (:8089, :8082, :8083, :8084, :8088, :8087, :8085) when the container is stopped

/mfe/assets/

rmu-mfe-assets (images and locales), or make start in rmu-mfe-assets (:4000)

/api/core/, /api/strategic/, /api/tactical/

Core, Strategic, Tactical APIs

/api/items/, /api/npcs/, /api/npc-names/, /api/spells/

Items, NPCs, NPC Names, Spells APIs

/api/attack-tables/

Attack Tables API

/api/users/, /api/media/

Users, Media APIs

/sso/

Keycloak

The prefix is stripped, so http://localhost/api/core/v1/races reaches http://rmu-api-core:3001/v1/races. The APIs live under /api/ so that /core/…​, /strategic/…​ stay free for the shell’s routes. Upstreams are resolved per request: the gateway starts with any subset of services, and a stopped one answers 502. Any other /mfe/<name>/ answers 404 until its MFE is added.

Working on one MFE

Everything runs from containers except the MFE you are working on, which runs from source with the debugger attached. The gateway switches to the dev server on its own when the container is stopped, so nothing else is restarted or reconfigured.

make up                     # with mfes and gateway in COMPOSE_PROFILES
make mfe-dev s=core         # docker compose stop rmu-mfe-core
# VS Code in rmu-mfe-core: F5 "Debug core in the shell (gateway)"
#   runs `pnpm run start:live` (:8089) and opens Chrome at http://localhost/core
make mfe-container s=core   # back to the image
  • Always open http://localhost, not the dev server port: the shell’s configuration (env-config.js) uses gateway paths such as /mfe/core/ and /api/core/v1.

  • Inside the shell, a remote reads the shell’s configuration, so the remote’s own .env only matters when it is opened standalone.

  • HMR connects straight to the dev server (ws://localhost:<port>/ws), not through the gateway.

  • If a remote is not available at all (container stopped and no dev server), only its route shows "Module not available".

To try a local build of an MFE, make docker-build in its repository tags labcabrera/rmu-mfe-<name>:local; select it with RMU_MFE_<NAME>_VERSION=local in .env, or use make up-local p=rmu-mfe-<name>. The same works for s=shell, s=strategic, s=tactical, s=npcs, s=items, s=spells and s=assets.

Observability

The observability profile runs grafana/otel-lgtm with the Loki, Prometheus, Tempo and Pyroscope datasources already provisioned. Grafana is at http://localhost:3000 (admin/admin).

APIs are not instrumented yet. Once an API supports OpenTelemetry, configure it with the standard variables:

OTEL_EXPORTER_OTLP_ENDPOINT: http://rmu-otel:4317
OTEL_SERVICE_NAME: rmu-api-<name>

Troubleshooting

  • A stopped MFE does not fall back to its dev server: the gateway reaches the host through host.docker.internal (host-gateway). Check that the dev server listens on 0.0.0.0 and that a host firewall (ufw) allows traffic from the Docker bridge to its port.

  • rmu-network already exists: the network was created by hand with docker network create. Run docker compose --profile '*' down && docker network rm rmu-network once and start again.

  • Kafka from the host bootstraps but then fails: use localhost:9093. Port 9092 advertises rmu-kafka-broker:9092, which only resolves inside the Compose network.

  • MongoDB does not start on Linux 6.19+: MongoDB 8.0 is affected by SERVER-121912. The service sets GLIBC_TUNABLES=glibc.pthread.rseq=1 to avoid it.

  • APIs reject every token: the realm has not been provisioned, RMU_IAM_CLIENT_SECRET in .env does not match rmu_client_secret in terraform.tfvars, or the API’s RMU_IAM_ISSUER_URI / RMU_IAM_AUDIENCE do not match the token (iss must be http://localhost/sso/realms/rmu, aud must contain rmu-api).

  • Login asks for credentials in every new tab: open the shell through the gateway (http://localhost), so the Keycloak cookie is first-party, and check "Remember me" to keep the session after closing the browser.

Initialization data

Warning
Some initialization data is intentionally not in this public repository, because it may contain copyrighted Rolemaster Unified material owned by Iron Crown Enterprises. A fresh environment starts, but lacks part of the reference content. Restore it from a backup of your own (operations/mongo-backup-restore.adoc) and never commit it.