From 8e1e9afc47fbfbc50b6024bf8e8746779f4cd8f4 Mon Sep 17 00:00:00 2001 From: shane-moore Date: Mon, 10 Aug 2026 12:26:32 -0700 Subject: [PATCH] feat(anchor): expose Anchor Prometheus metrics on port 5164 Anchor nodes started with no --metrics flag and no published port, so duty timing, head-event trigger counters, and QBFT instrumentation were unreadable on a local run. SSV nodes already publish theirs (port 9240); this closes that asymmetry, following the same constant + PortSpec shape. Anchor defaults the metrics listen address to 127.0.0.1, which is unreachable from outside the container, hence the explicit 0.0.0.0 bind. The flags exist on every anchor image pinned in this repo (verified against the shipped binaries in sigp/anchor:v1.2.0 and :v1.3.0), so no profile regresses. Verified live on a 4-operator all-Anchor run: all four anchor-node-N publish metrics: 5164/tcp, and each returns HTTP 200 with ~3.6k lines of Prometheus text (anchor_execution_*, anchor_processor_*). --- nodes/anchor/node.star | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/nodes/anchor/node.star b/nodes/anchor/node.star index b91e557..be3c36c 100644 --- a/nodes/anchor/node.star +++ b/nodes/anchor/node.star @@ -1,6 +1,9 @@ constants = import_module("../../utils/constants.star") utils = import_module("../../utils/utils.star") +ANCHOR_METRICS_PORT_NAME = "metrics" +ANCHOR_METRICS_PORT = 5164 + # Start anchor nodes: first node starts alone (to get ENR), remaining start in parallel def start(plan, num_nodes, cl_url, el_rpc, el_ws, key_pems, config, image): IP_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER" @@ -15,8 +18,19 @@ def start(plan, num_nodes, cl_url, el_rpc, el_ws, key_pems, config, image): "--logfile-max-number", "0", "--debug-level", "debug", # mitigation of https://github.com/sigp/anchor/issues/765 "--subscribe-all-subnets", + # Prometheus metrics; anchor defaults the listen address to 127.0.0.1, which is unreachable + # from outside the container, so bind 0.0.0.0 for the published port to work. + "--metrics", "--metrics-address", "0.0.0.0", "--metrics-port", str(ANCHOR_METRICS_PORT), ] + metrics_ports = { + ANCHOR_METRICS_PORT_NAME: PortSpec( + number=ANCHOR_METRICS_PORT, + transport_protocol="TCP", + application_protocol="http", + ), + } + plan.add_service( name="anchor-node-0", description="Starting Anchor bootnode (node 0)", @@ -25,6 +39,7 @@ def start(plan, num_nodes, cl_url, el_rpc, el_ws, key_pems, config, image): entrypoint=["anchor"], cmd=command_arr, files=files, + ports=metrics_ports, private_ip_address_placeholder=IP_PLACEHOLDER, ready_conditions=ReadyCondition( recipe=ExecRecipe( @@ -54,6 +69,7 @@ def start(plan, num_nodes, cl_url, el_rpc, el_ws, key_pems, config, image): entrypoint=["anchor"], cmd=command_arr_with_boot, files=files, + ports=metrics_ports, private_ip_address_placeholder=IP_PLACEHOLDER, ) plan.add_services(remaining_configs, description="Starting {} remaining Anchor nodes in parallel".format(num_nodes - 1))