Skip to content

Commit 261a8e1

Browse files
deployment: add N_NODES flag to run multiple nodes in local monitoring stack
1 parent 08e1346 commit 261a8e1

4 files changed

Lines changed: 141 additions & 59 deletions

File tree

deployments/monitoring/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ To force build (useful to enforce applying changes in docker file settings):
2323
./deployments/monitoring/deploy_local_stack.sh up -d --build
2424
```
2525

26+
### Multiple nodes:
27+
Set `N_NODES` (1-5, default 1) to run several sequencer nodes in real consensus, each in its own
28+
container (`sequencer-node-0`, `sequencer-node-1`, ...). Prometheus scrapes every node, so dashboard
29+
panels show one series per node (useful for observing proposer/validator agreement). Pass the same
30+
`N_NODES` when tearing down.
31+
```bash
32+
N_NODES=3 ./deployments/monitoring/deploy_local_stack.sh up -d --build
33+
```
34+
35+
### Real exchange-rate oracle:
36+
By default the stack uses a dummy constant-rate oracle. Set `PRAGMA_API_KEY` to instead pull live
37+
ETH/STRK and STRK/USD rates from Pragma (so the SNIP-35 `fee_target` tracks the real STRK/USD rate):
38+
```bash
39+
N_NODES=3 PRAGMA_API_KEY=<key> ./deployments/monitoring/deploy_local_stack.sh up -d --build
40+
```
41+
2642
## This will deploy a local stack of:
2743
- Sequencer Node Setup
2844
- Sequencer Node (using a config generated by **Sequencer Node Setup**.)

deployments/monitoring/deploy_local_stack.sh

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,27 @@ export DOCKER_BUILDKIT
77
export COMPOSE_DOCKER_CLI_BUILD
88
export MONITORING_ENABLED=${MONITORING_ENABLED:-true}
99
export FOLLOW_LOGS=${FOLLOW_LOGS:-false}
10-
export SEQUENCER_HTTP_PORT=${SEQUENCER_HTTP_PORT:-8081}
11-
export SEQUENCER_MONITORING_PORT=${SEQUENCER_MONITORING_PORT:-8082}
12-
export SEQUENCER_CONFIG_PATH=${SEQUENCER_CONFIG_PATH:-/config/node_0/config.json}
1310
export SEQUENCER_ROOT_DIR=${SEQUENCER_ROOT_DIR:-$(git -C "$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")" rev-parse --show-toplevel)}
1411

12+
# Number of sequencer nodes to run in real consensus, each in its own container (max 5). Default 1.
13+
export N_NODES=${N_NODES:-1}
14+
if ! [[ "$N_NODES" =~ ^[1-5]$ ]]; then
15+
echo "Error: N_NODES must be an integer between 1 and 5 (got '$N_NODES')."
16+
exit 1
17+
fi
18+
# For `up`, enable a profile per extra node so only sequencer-node-0..N-1 start. For `down`, enable
19+
# every node profile so all nodes are torn down regardless of how many were started (otherwise the
20+
# profiled nodes survive `down`).
21+
if [ "$1" == "down" ]; then
22+
export COMPOSE_PROFILES="node1,node2,node3,node4"
23+
else
24+
profiles=""
25+
for ((node_index = 1; node_index < N_NODES; node_index++)); do
26+
profiles="${profiles:+$profiles,}node${node_index}"
27+
done
28+
export COMPOSE_PROFILES="$profiles"
29+
fi
30+
1531
# Exchange-rate oracle source. When PRAGMA_API_KEY is set, point the node at the real Pragma
1632
# oracle so eth_to_strk (L1 gas price conversion) and strk_to_usd (SNIP-35 fee_target) track live
1733
# rates; otherwise fall back to the dummy constant-rate oracle (see docker-compose.yml
@@ -40,7 +56,11 @@ else
4056
fi
4157

4258
if [ "$MONITORING_ENABLED" != true ]; then
43-
services="sequencer_node_setup dummy_recorder dummy_exchange_rate_oracle config_injector sequencer_node sequencer_simulator"
59+
node_services=""
60+
for ((node_index = 0; node_index < N_NODES; node_index++)); do
61+
node_services="${node_services} sequencer-node-${node_index}"
62+
done
63+
services="sequencer_node_setup dummy_recorder dummy_exchange_rate_oracle config_injector${node_services} sequencer_simulator"
4464
fi
4565

4666
echo "Running: ${docker_compose} -f ${monitoring_dir}/local/docker-compose.yml $*"

deployments/monitoring/local/config/prometheus/prometheus.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ scrape_configs:
99
metrics_path: /monitoring/metrics
1010
static_configs:
1111
- targets:
12-
- sequencer_node:8082
12+
- sequencer-node-0:8082
13+
- sequencer-node-1:8082
14+
- sequencer-node-2:8082
15+
- sequencer-node-3:8082
16+
- sequencer-node-4:8082

deployments/monitoring/local/docker-compose.yml

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
# Shared sequencer-node service body. Each node runs in its own container and reaches the others by
2+
# docker DNS name (sequencer-node-0, sequencer-node-1, ...). node_1+ are gated behind profiles so
3+
# only N_NODES of them start (see deploy_local_stack.sh).
4+
x-sequencer-node: &sequencer_node
5+
depends_on:
6+
config_injector:
7+
condition: service_completed_successfully
8+
sequencer_node_setup:
9+
condition: service_completed_successfully
10+
dummy_recorder:
11+
condition: service_started
12+
dummy_exchange_rate_oracle:
13+
condition: service_started
14+
build:
15+
context: ${SEQUENCER_ROOT_DIR}
16+
dockerfile: ${SEQUENCER_ROOT_DIR}/deployments/images/sequencer/Dockerfile
17+
args:
18+
BUILD_MODE: debug
19+
environment:
20+
- RUST_LOG=${RUST_LOG}
21+
- RUST_BACKTRACE=${RUST_BACKTRACE}
22+
volumes:
23+
- data:/data
24+
- config:/config
25+
networks:
26+
- sequencer-network
27+
128
services:
229
prometheus:
330
image: prom/prometheus
@@ -31,7 +58,7 @@ services:
3158
- RUST_BACKTRACE=${RUST_BACKTRACE}
3259
entrypoint: "/bin/bash -c"
3360
command: >
34-
"./target/debug/sequencer_node_setup --output-base-dir ./output --data-prefix-path /data --n-distributed 0 --n-hybrid 0 --n-consolidated 1;
61+
"./target/debug/sequencer_node_setup --output-base-dir ./output --data-prefix-path /data --n-distributed 0 --n-hybrid 0 --n-consolidated ${N_NODES:-1};
3562
cp -r ./output/data/* /data; cp -r ./output/configs/* /config"
3663
volumes:
3764
- data:/data
@@ -71,66 +98,81 @@ services:
7198
build:
7299
context: ${SEQUENCER_ROOT_DIR}
73100
dockerfile: ${SEQUENCER_ROOT_DIR}/deployments/images/sequencer/config_injector.Dockerfile
74-
# TODO(Tsabary): change the setup binary to output the file to a specific path, and use it in the following command.
75-
# TODO(Tsabary): the config changes need to be more robust, probably managed through a suitable rust binary.
76-
command: |
77-
"cp /config/node_0/node_integration_test_config_changes.json ${SEQUENCER_CONFIG_PATH} \
78-
echo 'Injecting config changes...' && \
79-
jq '.\"recorder_url\" = \"http://dummy_recorder:8080\"' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
80-
jq '.\"l1_gas_price_provider_config.eth_to_strk_oracle_config.url_header_list\" = \"${ETH_STRK_ORACLE_URL_HEADERS:-http://dummy_exchange_rate_oracle:9000/eth_to_strk_oracle}\"' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
81-
jq '.\"l1_gas_price_provider_config.eth_to_strk_oracle_config.lag_interval_seconds\" = ${ETH_STRK_ORACLE_LAG_SECONDS:-60}' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
82-
jq '.\"l1_gas_price_provider_config.strk_to_usd_oracle_config.url_header_list\" = \"${STRK_USD_ORACLE_URL_HEADERS:-http://dummy_exchange_rate_oracle:9000/eth_to_strk_oracle}\"' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
83-
jq '.\"l1_gas_price_provider_config.strk_to_usd_oracle_config.lag_interval_seconds\" = ${STRK_USD_ORACLE_LAG_SECONDS:-60}' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
84-
jq '.\"http_server_config.static_config.ip\" = \"0.0.0.0\"' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
85-
jq '.\"http_server_config.static_config.port\" = ${SEQUENCER_HTTP_PORT}' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
86-
jq '.\"monitoring_endpoint_config.port\" = ${SEQUENCER_MONITORING_PORT}' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
87-
# These are here to avoid using an L1 baselayer. This is because anvil was deliberately disabled in the docker test.
88-
jq '.\"components.l1_events_scraper.execution_mode\" = \"Disabled\"' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
89-
jq '.\"l1_events_scraper_config.#is_none\" = true' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
90-
jq '.\"l1_events_provider_config.dummy_mode\" = true' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
91-
jq '.\"components.l1_gas_price_scraper.execution_mode\" = \"Disabled\"' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
92-
jq '.\"l1_gas_price_scraper_config.#is_none\" = true' ${SEQUENCER_CONFIG_PATH} | sponge ${SEQUENCER_CONFIG_PATH} && \
93-
echo 'Printing final config:' && \
94-
echo '----------------------------------------' && \
95-
cat ${SEQUENCER_CONFIG_PATH} && \
96-
echo '----------------------------------------' && \
97-
echo 'Done'"
101+
# Inject local-stack overrides into each node's config. Single $ is interpolated by docker
102+
# compose (host env); $$ is passed through to the container shell. For N>1 the localhost
103+
# full-mesh P2P bootstrap addresses are rewritten to per-node DNS names (entry j -> node j, since
104+
# the list is node-index ordered and identical across nodes).
105+
command:
106+
- |
107+
set -e
108+
N_NODES=${N_NODES:-1}
109+
for i in $$(seq 0 $$((N_NODES - 1))); do
110+
cfg=/config/node_$$i/config.json
111+
cp /config/node_$$i/node_integration_test_config_changes.json "$$cfg"
112+
jq '."recorder_url" = "http://dummy_recorder:8080"' "$$cfg" | sponge "$$cfg"
113+
jq --arg v "${ETH_STRK_ORACLE_URL_HEADERS:-http://dummy_exchange_rate_oracle:9000/eth_to_strk_oracle}" '."l1_gas_price_provider_config.eth_to_strk_oracle_config.url_header_list" = $$v' "$$cfg" | sponge "$$cfg"
114+
jq --argjson v ${ETH_STRK_ORACLE_LAG_SECONDS:-60} '."l1_gas_price_provider_config.eth_to_strk_oracle_config.lag_interval_seconds" = $$v' "$$cfg" | sponge "$$cfg"
115+
jq --arg v "${STRK_USD_ORACLE_URL_HEADERS:-http://dummy_exchange_rate_oracle:9000/eth_to_strk_oracle}" '."l1_gas_price_provider_config.strk_to_usd_oracle_config.url_header_list" = $$v' "$$cfg" | sponge "$$cfg"
116+
jq --argjson v ${STRK_USD_ORACLE_LAG_SECONDS:-60} '."l1_gas_price_provider_config.strk_to_usd_oracle_config.lag_interval_seconds" = $$v' "$$cfg" | sponge "$$cfg"
117+
jq '."http_server_config.static_config.ip" = "0.0.0.0"' "$$cfg" | sponge "$$cfg"
118+
jq '."http_server_config.static_config.port" = 8081' "$$cfg" | sponge "$$cfg"
119+
jq '."monitoring_endpoint_config.port" = 8082' "$$cfg" | sponge "$$cfg"
120+
jq '."components.l1_events_scraper.execution_mode" = "Disabled"' "$$cfg" | sponge "$$cfg"
121+
jq '."l1_events_scraper_config.#is_none" = true' "$$cfg" | sponge "$$cfg"
122+
jq '."l1_events_provider_config.dummy_mode" = true' "$$cfg" | sponge "$$cfg"
123+
jq '."components.l1_gas_price_scraper.execution_mode" = "Disabled"' "$$cfg" | sponge "$$cfg"
124+
jq '."l1_gas_price_scraper_config.#is_none" = true' "$$cfg" | sponge "$$cfg"
125+
if [ "$$N_NODES" -gt 1 ]; then
126+
for key in consensus_manager_config.network_config.bootstrap_peer_multiaddr mempool_p2p_config.network_config.bootstrap_peer_multiaddr state_sync_config.static_config.network_config.bootstrap_peer_multiaddr; do
127+
jq --arg k "$$key" '.[$$k] |= (split(",") | to_entries | map(.key as $$j | .value | sub("/ip4/[0-9.]+/"; "/dns4/sequencer-node-\($$j)/")) | join(","))' "$$cfg" | sponge "$$cfg"
128+
done
129+
fi
130+
done
98131
volumes:
99132
- config:/config
100133
networks:
101134
- sequencer-network
102135

103-
sequencer_node:
104-
depends_on:
105-
config_injector:
106-
condition: service_completed_successfully
107-
dummy_recorder:
108-
condition: service_started
109-
sequencer_node_setup:
110-
condition: service_completed_successfully
111-
build:
112-
context: ${SEQUENCER_ROOT_DIR}
113-
dockerfile: ${SEQUENCER_ROOT_DIR}/deployments/images/sequencer/Dockerfile
114-
args:
115-
BUILD_MODE: debug
116-
environment:
117-
- RUST_LOG=${RUST_LOG}
118-
- RUST_BACKTRACE=${RUST_BACKTRACE}
136+
sequencer-node-0:
137+
<<: *sequencer_node
119138
ports:
120-
- ${SEQUENCER_HTTP_PORT}:${SEQUENCER_HTTP_PORT}
121-
- ${SEQUENCER_MONITORING_PORT}:${SEQUENCER_MONITORING_PORT}
139+
- "8081:8081"
140+
- "8082:8082"
122141
command:
123142
- "--config_file"
124-
- "${SEQUENCER_CONFIG_PATH}"
125-
volumes:
126-
- data:/data
127-
- config:/config
128-
networks:
129-
- sequencer-network
143+
- "/config/node_0/config.json"
144+
145+
sequencer-node-1:
146+
<<: *sequencer_node
147+
profiles: ["node1"]
148+
command:
149+
- "--config_file"
150+
- "/config/node_1/config.json"
151+
152+
sequencer-node-2:
153+
<<: *sequencer_node
154+
profiles: ["node2"]
155+
command:
156+
- "--config_file"
157+
- "/config/node_2/config.json"
158+
159+
sequencer-node-3:
160+
<<: *sequencer_node
161+
profiles: ["node3"]
162+
command:
163+
- "--config_file"
164+
- "/config/node_3/config.json"
165+
166+
sequencer-node-4:
167+
<<: *sequencer_node
168+
profiles: ["node4"]
169+
command:
170+
- "--config_file"
171+
- "/config/node_4/config.json"
130172

131173
sequencer_simulator:
132174
depends_on:
133-
- sequencer_node
175+
- sequencer-node-0
134176
build:
135177
context: ${SEQUENCER_ROOT_DIR}
136178
dockerfile: ${SEQUENCER_ROOT_DIR}/deployments/images/sequencer/simulator.Dockerfile
@@ -141,10 +183,10 @@ services:
141183
entrypoint: "/bin/bash -c"
142184
command: >
143185
"./target/debug/sequencer_simulator \
144-
--http-url http://sequencer_node \
145-
--http-port ${SEQUENCER_HTTP_PORT} \
146-
--monitoring-url http://sequencer_node \
147-
--monitoring-port ${SEQUENCER_MONITORING_PORT} \
186+
--http-url http://sequencer-node-0 \
187+
--http-port 8081 \
188+
--monitoring-url http://sequencer-node-0 \
189+
--monitoring-port 8082 \
148190
$$(if [ \"$$SIMULATOR_RUN_FOREVER\" = \"true\" ]; then echo '--run-forever'; fi)"
149191
networks:
150192
- sequencer-network

0 commit comments

Comments
 (0)