Documentation
Build and run
A service runs in three ways: in dev mode with restart on file changes, as built
JavaScript from dist/, and in a Docker container on the platform's base image.
Running locally#
You need access to the Redis of your installation (directly or through a tunnel) and a .env with its address:
npm install
npm run start:dev
start:dev runs ts-node-dev with tsconfig-paths and source-map-support, and pipes the output
through pino-pretty. When files in src/ change, the process restarts. LOG_LEVEL=debug
enables debug records, including those about channel subscriptions:
INFO RedisClient: Starting redis client. DSN: redis://127.0.0.1:6379/0
INFO RPCAdapterRedis: subscribed channel=kafkastream
INFO RPCAdapterRedis: subscribed channel=broadcast
INFO RPCAgnostic: started
INFO AppServer: Setup completed
Building and running the built code:
npm run build # rimraf dist && tsc
npm run start:prod # node dist/index with tsconfig-paths and source maps
In start:prod, the TS_NODE_BASEURL=./dist variable switches the @app/* alias to the built files.
Base image#
All TypeScript services are built from the rockstat/band-base-ts image. It is built from the
rockme-framework repository: node:22-alpine with tools for native modules (python3, make,
gcc), the framework built in /usr/src/rockme and published via npm link. Thanks to this,
in the service's Dockerfile a plain npm link @rockstat/rock-me-ts is enough, and the framework is taken from the image
rather than from the registry.
Image tags: ng — the current development branch, latest — the stable one. It is rebuilt from
rockmets with the commands make build (or make build_macos_arm64 on Apple Silicon, which builds for
linux/amd64), make tag-ng, make push-ng; all together — make all-ng.
The service's Dockerfile#
ARG BASE_CONTAINER=rockstat/band-base-ts:ng
FROM $BASE_CONTAINER
LABEL band.service.version="1.0.0"
LABEL band.service.title="My service"
LABEL band.service.def_position="3x1"
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm link @rockstat/rock-me-ts
RUN npm install && npm cache clean --force
COPY . .
RUN npm run build
ENV REDIS_DSN=redis://redis:6379
ENV NODE_ENV=production
CMD [ "npm", "run", "start:prod" ]
Layer order matters: first only the manifests are copied and the dependencies are installed, then the sources.
This way editing the code does not invalidate the npm install cache. .dockerignore must exclude node_modules,
dist, and .env*.
The band.service.* labels are read by the Dashboard: the version, a human-readable name, and the position of the service's
card on the dashboard.
If the framework is listed in package.json as file:../rockmets, that path does not exist in the image.
kafkastream solves this by copying: RUN cp -r /usr/src/rockme /app/rockmets after npm i.
It is simpler to specify a regular package version and rely on the npm link from the base image.
Publishing the image#
Every service has the same makefile:
build:
docker build -t my-service .
build_amd64:
docker buildx build --platform linux/amd64 -t my-service .
tag-ng:
docker tag my-service rockstat/my-service:ng
push-ng:
docker push rockstat/my-service:ng
all-ng: build_amd64 tag-ng push-ng
push-dev:
docker tag my-service rockstat/my-service:dev
docker push rockstat/my-service:dev
The working cycle: make all-ng builds the image for linux/amd64 and publishes it with the ng tag,
make push-dev ships a test build under the dev tag without touching ng.
Versions#
The version is stored in three places: package.json, the band.service.version label in the Dockerfile,
and .bumpversion.cfg. bumpversion bumps all three at once:
make bump-patch # 1.0.0 -> 1.0.1
make bump-minor # 1.0.1 -> 1.1.0
The configuration picks up the version automatically via <%= pkg.version %>, so it is visible
in the logs and in the service's status.
Container environment variables#
| Variable | Required | Value |
|---|---|---|
REDIS_DSN | yes | redis://redis:6379 inside the platform network |
NODE_ENV | yes | production |
LOG_LEVEL | no | info by default, warn for quiet services |
STATSD_HOST | no | the Telegraf/StatsD host; without it, metrics are not sent |
TZ | no | UTC, already set in the base image |
SERVICE_GROUP, WORKER_ID | for enrichers | the group and the worker number |
| custom | as needed | KAFKA_BROKERS, CH_DSN, and others that the configuration reads |
Checklist before the first run#
rpc.nameis unique in the installation, and for multiple instances it includesWORKER_ID.- All external connections are made in
setup()before registering bus methods. - The
broadcastorenrichhandler does not wait on the network and returns control immediately. - Secrets come from the environment; in YAML there is only
<%= env.NAME %>. - The
Dockerfileinherits fromrockstat/band-base-ts:ng, and theband.service.*labels are filled in. make all-ngpasses on a clean machine, and the image starts with just aREDIS_DSN.