Commit de5eee82 authored by Sam Calder-Mason's avatar Sam Calder-Mason Committed by GitHub

feat(tooling): Add Ethereum Metrics Exporter (#331)

Adds support for
https://github.com/ethpandaops/ethereum-metrics-exporter. An instance
for every participant is spun up as EME only supports a single EL/CL
pair. Can be enabled on a global or participant level.

Resolves https://github.com/kurtosis-tech/ethereum-package/issues/305
parent e721373f
...@@ -187,6 +187,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi ...@@ -187,6 +187,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi
// Defaults to false // Defaults to false
"snooper_enabled": false, "snooper_enabled": false,
// Enables Ethereum Metrics Exporter for this participant. Can be set globally.
// Defaults to false
"ethereum_metrics_exporter_enabled": false,
// Count of nodes to spin up for this participant // Count of nodes to spin up for this participant
// Default to 1 // Default to 1
"count": 1, "count": 1,
...@@ -276,6 +280,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi ...@@ -276,6 +280,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi
// Default to false // Default to false
"snooper_enabled": false, "snooper_enabled": false,
// Enables Ethereum Metrics Exporter for all participants
// Defaults to false
"ethereum_metrics_exporter_enabled": false,
// Parallelizes keystore generation so that each node has keystores being generated in their own container // Parallelizes keystore generation so that each node has keystores being generated in their own container
// This will result in a large number of containers being spun up than normal. We advise users to only enable this on a sufficiently large machine or in the cloud as it can be resource consuming on a single machine. // This will result in a large number of containers being spun up than normal. We advise users to only enable this on a sufficiently large machine or in the cloud as it can be resource consuming on a single machine.
"parallel_keystore_generation": false, "parallel_keystore_generation": false,
......
...@@ -97,9 +97,13 @@ def run(plan, args={}): ...@@ -97,9 +97,13 @@ def run(plan, args={}):
all_el_client_contexts = [] all_el_client_contexts = []
all_cl_client_contexts = [] all_cl_client_contexts = []
all_ethereum_metrics_exporter_contexts = []
for participant in all_participants: for participant in all_participants:
all_el_client_contexts.append(participant.el_client_context) all_el_client_contexts.append(participant.el_client_context)
all_cl_client_contexts.append(participant.cl_client_context) all_cl_client_contexts.append(participant.cl_client_context)
all_ethereum_metrics_exporter_contexts.append(
participant.ethereum_metrics_exporter_context
)
# Generate validator ranges # Generate validator ranges
validator_ranges_config_template = read_file( validator_ranges_config_template = read_file(
...@@ -347,6 +351,7 @@ def run(plan, args={}): ...@@ -347,6 +351,7 @@ def run(plan, args={}):
all_el_client_contexts, all_el_client_contexts,
all_cl_client_contexts, all_cl_client_contexts,
prometheus_additional_metrics_jobs, prometheus_additional_metrics_jobs,
all_ethereum_metrics_exporter_contexts,
) )
plan.print("Launching grafana...") plan.print("Launching grafana...")
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
"validator_extra_params": [], "validator_extra_params": [],
"builder_network_params": null, "builder_network_params": null,
"validator_count": null, "validator_count": null,
"ethereum_metrics_exporter_enabled": false,
"count": 2 "count": 2
} }
], ],
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
"wait_for_finalization": false, "wait_for_finalization": false,
"global_client_log_level": "info", "global_client_log_level": "info",
"snooper_enabled": false, "snooper_enabled": false,
"ethereum_metrics_exporter_enabled": false,
"parallel_keystore_generation": false, "parallel_keystore_generation": false,
"mev_type": null, "mev_type": null,
"mev_params": { "mev_params": {
......
def new_ethereum_metrics_exporter_context(
pair_name,
ip_addr,
metrics_port_num,
cl_name,
el_name,
):
return struct(
pair_name=pair_name,
ip_addr=ip_addr,
metrics_port_num=metrics_port_num,
cl_name=cl_name,
el_name=el_name,
)
shared_utils = import_module("../shared_utils/shared_utils.star")
constants = import_module("../package_io/constants.star")
static_files = import_module("../static_files/static_files.star")
ethereum_metrics_exporter_context = import_module(
"../ethereum_metrics_exporter/ethereum_metrics_exporter_context.star"
)
HTTP_PORT_ID = "http"
METRICS_PORT_NUMBER = 9090
def launch(
plan,
pair_name,
ethereum_metrics_exporter_service_name,
ethereum_metrics_exporter_image,
el_client_context,
cl_client_context,
):
exporter_service = plan.add_service(
ethereum_metrics_exporter_service_name,
ServiceConfig(
image=ethereum_metrics_exporter_image,
ports={
HTTP_PORT_ID: shared_utils.new_port_spec(
METRICS_PORT_NUMBER,
shared_utils.TCP_PROTOCOL,
shared_utils.HTTP_APPLICATION_PROTOCOL,
)
},
cmd=[
"--metrics-port",
str(METRICS_PORT_NUMBER),
"--consensus-url",
"http://{}:{}".format(
cl_client_context.ip_addr,
cl_client_context.http_port_num,
),
"--execution-url",
"http://{}:{}".format(
el_client_context.ip_addr,
el_client_context.rpc_port_num,
),
],
),
)
return ethereum_metrics_exporter_context.new_ethereum_metrics_exporter_context(
pair_name,
exporter_service.ip_address,
METRICS_PORT_NUMBER,
cl_client_context.client_name,
el_client_context.client_name,
)
...@@ -31,6 +31,8 @@ GENESIS_VALIDATORS_ROOT_PLACEHOLDER = "GENESIS_VALIDATORS_ROOT_PLACEHOLDER" ...@@ -31,6 +31,8 @@ GENESIS_VALIDATORS_ROOT_PLACEHOLDER = "GENESIS_VALIDATORS_ROOT_PLACEHOLDER"
DEFAULT_SNOOPER_IMAGE = "ethpandaops/json-rpc-snoop:1.1.0" DEFAULT_SNOOPER_IMAGE = "ethpandaops/json-rpc-snoop:1.1.0"
DEFAULT_ETHEREUM_METRICS_EXPORTER_IMAGE = "ethpandaops/ethereum-metrics-exporter:0.22.0"
ARCHIVE_MODE = True ARCHIVE_MODE = True
......
...@@ -142,6 +142,9 @@ def input_parser(plan, input_args): ...@@ -142,6 +142,9 @@ def input_parser(plan, input_args):
validator_count=participant["validator_count"], validator_count=participant["validator_count"],
snooper_enabled=participant["snooper_enabled"], snooper_enabled=participant["snooper_enabled"],
count=participant["count"], count=participant["count"],
ethereum_metrics_exporter_enabled=participant[
"ethereum_metrics_exporter_enabled"
],
) )
for participant in result["participants"] for participant in result["participants"]
], ],
...@@ -199,6 +202,7 @@ def input_parser(plan, input_args): ...@@ -199,6 +202,7 @@ def input_parser(plan, input_args):
global_client_log_level=result["global_client_log_level"], global_client_log_level=result["global_client_log_level"],
mev_type=result["mev_type"], mev_type=result["mev_type"],
snooper_enabled=result["snooper_enabled"], snooper_enabled=result["snooper_enabled"],
ethereum_metrics_exporter_enabled=result["ethereum_metrics_exporter_enabled"],
parallel_keystore_generation=result["parallel_keystore_generation"], parallel_keystore_generation=result["parallel_keystore_generation"],
grafana_additional_dashboards=result["grafana_additional_dashboards"], grafana_additional_dashboards=result["grafana_additional_dashboards"],
disable_peer_scoring=result["disable_peer_scoring"], disable_peer_scoring=result["disable_peer_scoring"],
...@@ -267,6 +271,18 @@ def parse_network_params(input_args): ...@@ -267,6 +271,18 @@ def parse_network_params(input_args):
if default_snooper_enabled: if default_snooper_enabled:
participant["snooper_enabled"] = default_snooper_enabled participant["snooper_enabled"] = default_snooper_enabled
ethereum_metrics_exporter_enabled = participant[
"ethereum_metrics_exporter_enabled"
]
if ethereum_metrics_exporter_enabled == False:
default_ethereum_metrics_exporter_enabled = result[
"ethereum_metrics_exporter_enabled"
]
if default_ethereum_metrics_exporter_enabled:
participant[
"ethereum_metrics_exporter_enabled"
] = default_ethereum_metrics_exporter_enabled
validator_count = participant["validator_count"] validator_count = participant["validator_count"]
if validator_count == None: if validator_count == None:
default_validator_count = result["network_params"][ default_validator_count = result["network_params"][
...@@ -357,6 +373,7 @@ def default_input_args(): ...@@ -357,6 +373,7 @@ def default_input_args():
"wait_for_finalization": False, "wait_for_finalization": False,
"global_client_log_level": "info", "global_client_log_level": "info",
"snooper_enabled": False, "snooper_enabled": False,
"ethereum_metrics_exporter_enabled": False,
"parallel_keystore_generation": False, "parallel_keystore_generation": False,
"disable_peer_scoring": False, "disable_peer_scoring": False,
} }
...@@ -406,6 +423,7 @@ def default_participant(): ...@@ -406,6 +423,7 @@ def default_participant():
"v_max_mem": 0, "v_max_mem": 0,
"validator_count": None, "validator_count": None,
"snooper_enabled": False, "snooper_enabled": False,
"ethereum_metrics_exporter_enabled": False,
"count": 1, "count": 1,
} }
......
...@@ -4,6 +4,7 @@ def new_participant( ...@@ -4,6 +4,7 @@ def new_participant(
el_client_context, el_client_context,
cl_client_context, cl_client_context,
snooper_engine_context, snooper_engine_context,
ethereum_metrics_exporter_context,
): ):
return struct( return struct(
el_client_type=el_client_type, el_client_type=el_client_type,
...@@ -11,4 +12,5 @@ def new_participant( ...@@ -11,4 +12,5 @@ def new_participant(
el_client_context=el_client_context, el_client_context=el_client_context,
cl_client_context=cl_client_context, cl_client_context=cl_client_context,
snooper_engine_context=snooper_engine_context, snooper_engine_context=snooper_engine_context,
ethereum_metrics_exporter_context=ethereum_metrics_exporter_context,
) )
...@@ -24,6 +24,10 @@ teku = import_module("./cl/teku/teku_launcher.star") ...@@ -24,6 +24,10 @@ teku = import_module("./cl/teku/teku_launcher.star")
snooper = import_module("./snooper/snooper_engine_launcher.star") snooper = import_module("./snooper/snooper_engine_launcher.star")
ethereum_metrics_exporter = import_module(
"./ethereum_metrics_exporter/ethereum_metrics_exporter_launcher.star"
)
genesis_constants = import_module( genesis_constants = import_module(
"./prelaunch_data_generator/genesis_constants/genesis_constants.star" "./prelaunch_data_generator/genesis_constants/genesis_constants.star"
) )
...@@ -237,6 +241,7 @@ def launch_participant_network( ...@@ -237,6 +241,7 @@ def launch_participant_network(
all_snooper_engine_contexts = [] all_snooper_engine_contexts = []
all_cl_client_contexts = [] all_cl_client_contexts = []
all_ethereum_metrics_exporter_contexts = []
preregistered_validator_keys_for_nodes = validator_data.per_node_keystores preregistered_validator_keys_for_nodes = validator_data.per_node_keystores
for index, participant in enumerate(participants): for index, participant in enumerate(participants):
...@@ -341,6 +346,35 @@ def launch_participant_network( ...@@ -341,6 +346,35 @@ def launch_participant_network(
all_cl_client_contexts.append(cl_client_context) all_cl_client_contexts.append(cl_client_context)
ethereum_metrics_exporter_context = None
if participant.ethereum_metrics_exporter_enabled:
pair_name = "{0}-{1}-{2}".format(index_str, cl_client_type, el_client_type)
ethereum_metrics_exporter_service_name = (
"ethereum-metrics-exporter-{0}".format(pair_name)
)
ethereum_metrics_exporter_image = (
constants.DEFAULT_ETHEREUM_METRICS_EXPORTER_IMAGE
)
ethereum_metrics_exporter_context = ethereum_metrics_exporter.launch(
plan,
pair_name,
ethereum_metrics_exporter_service_name,
ethereum_metrics_exporter_image,
el_client_context,
cl_client_context,
)
plan.print(
"Succesfully added {0} ethereum metrics exporter participants".format(
ethereum_metrics_exporter_context
)
)
all_ethereum_metrics_exporter_contexts.append(ethereum_metrics_exporter_context)
plan.print("Succesfully added {0} CL participants".format(num_participants)) plan.print("Succesfully added {0} CL participants".format(num_participants))
all_participants = [] all_participants = []
...@@ -351,15 +385,24 @@ def launch_participant_network( ...@@ -351,15 +385,24 @@ def launch_participant_network(
el_client_context = all_el_client_contexts[index] el_client_context = all_el_client_contexts[index]
cl_client_context = all_cl_client_contexts[index] cl_client_context = all_cl_client_contexts[index]
if participant.snooper_enabled: if participant.snooper_enabled:
snooper_engine_context = all_snooper_engine_contexts[index] snooper_engine_context = all_snooper_engine_contexts[index]
ethereum_metrics_exporter_context = None
if participant.ethereum_metrics_exporter_enabled:
ethereum_metrics_exporter_context = all_ethereum_metrics_exporter_contexts[
index
]
participant_entry = participant_module.new_participant( participant_entry = participant_module.new_participant(
el_client_type, el_client_type,
cl_client_type, cl_client_type,
el_client_context, el_client_context,
cl_client_context, cl_client_context,
snooper_engine_context, snooper_engine_context,
ethereum_metrics_exporter_context,
) )
all_participants.append(participant_entry) all_participants.append(participant_entry)
......
...@@ -34,11 +34,13 @@ def launch_prometheus( ...@@ -34,11 +34,13 @@ def launch_prometheus(
el_client_contexts, el_client_contexts,
cl_client_contexts, cl_client_contexts,
additional_metrics_jobs, additional_metrics_jobs,
ethereum_metrics_exporter_contexts,
): ):
template_data = new_config_template_data( template_data = new_config_template_data(
el_client_contexts, el_client_contexts,
cl_client_contexts, cl_client_contexts,
additional_metrics_jobs, additional_metrics_jobs,
ethereum_metrics_exporter_contexts,
) )
template_and_data = shared_utils.new_template_and_data( template_and_data = shared_utils.new_template_and_data(
config_template, template_data config_template, template_data
...@@ -86,6 +88,7 @@ def new_config_template_data( ...@@ -86,6 +88,7 @@ def new_config_template_data(
el_client_contexts, el_client_contexts,
cl_client_contexts, cl_client_contexts,
additional_metrics_jobs, additional_metrics_jobs,
ethereum_metrics_exporter_contexts,
): ):
metrics_jobs = [] metrics_jobs = []
# Adding execution clients metrics jobs # Adding execution clients metrics jobs
...@@ -142,6 +145,25 @@ def new_config_template_data( ...@@ -142,6 +145,25 @@ def new_config_template_data(
}, },
) )
) )
# Adding ethereum-metrics-exporter metrics jobs
for context in ethereum_metrics_exporter_contexts:
if context != None:
metrics_jobs.append(
new_metrics_job(
job_name="ethereum-metrics-exporter-{0}".format(context.pair_name),
endpoint="{}:{}".format(
context.ip_addr,
context.metrics_port_num,
),
metrics_path="/metrics",
labels={
"instance": context.pair_name,
"consensus_client": context.cl_name,
"execution_client": context.el_name,
},
)
)
# Adding additional metrics jobs # Adding additional metrics jobs
for job in additional_metrics_jobs: for job in additional_metrics_jobs:
if job == None: if job == None:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment