diff --git a/CLAUDE.md b/CLAUDE.md index 099ea6c..6024ac0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -314,7 +314,9 @@ Each scenario creates an isolated Docker bridge network: - Execution client container joins the network - K6 and Alloy containers join the same network -- Enables service discovery by container name +- Enables service discovery by container name; the execution client is also reachable as + `execution-client` (Alloy scrapes that alias, because container names longer than 63 + characters do not resolve) - Network is removed during cleanup ### Resource Limiting diff --git a/src/expb/_version.py b/src/expb/_version.py index 0594ff5..2040ea9 100644 --- a/src/expb/_version.py +++ b/src/expb/_version.py @@ -1,2 +1,2 @@ -__version__ = "0.1.0" -__commit__ = "7621458" +__version__ = "0.1.0" +__commit__ = "9609a1b" diff --git a/src/expb/payloads/executor/executor.py b/src/expb/payloads/executor/executor.py index 0b4eab5..bb176c5 100644 --- a/src/expb/payloads/executor/executor.py +++ b/src/expb/payloads/executor/executor.py @@ -681,7 +681,26 @@ def start_execution_client( run_kwargs["mem_swappiness"] = self.config.resources.mem_swappiness if self.config.execution_client_security_opt: run_kwargs["security_opt"] = self.config.execution_client_security_opt - container = self.config.docker_client.containers.run(**run_kwargs) + if container_network is None: + return self.config.docker_client.containers.run(**run_kwargs) + # Attach the client under its short aliases as well: the container name exceeds the + # 63-character DNS label limit for long scenario names, and a name that does not resolve + # silently leaves the client unscraped, which shows up as a spurious CPU saving. + name = run_kwargs["name"] + if len(name) > 63: + self.log.warning( + "Execution client container name exceeds the DNS label limit; use its network alias", + container=name, + aliases=self.config.get_execution_client_aliases(), + ) + run_kwargs.pop("detach") + container = self.config.docker_client.containers.create(**run_kwargs) + container_network.disconnect(container) + container_network.connect( + container, aliases=self.config.get_execution_client_aliases() + ) + container.start() + container.reload() return container def _client_host_pid(self, container: Container, timeout: int = 60) -> int | None: diff --git a/src/expb/payloads/executor/executor_config.py b/src/expb/payloads/executor/executor_config.py index 43b6e30..d62579a 100644 --- a/src/expb/payloads/executor/executor_config.py +++ b/src/expb/payloads/executor/executor_config.py @@ -32,6 +32,9 @@ from expb.payloads.executor.services.snapshots import SnapshotService +EXECUTION_CLIENT_ALIAS = "execution-client" + + # ExecutorConfig class is a collection of helper functions and configuration options for the Executor class class ExecutorConfig: def __init__( @@ -232,9 +235,15 @@ def get_execution_client_ports(self) -> dict[str, tuple[str, str]]: # f"{CLIENT_P2P_PORT}/udp": ("127.0.0.1", f"{CLIENT_P2P_PORT}"), } + def get_execution_client_aliases(self) -> list[str]: + # Docker's embedded DNS resolves a name only if it fits a 63-character DNS label. Scenario + # names push the client's container name past that, so the client also gets a short alias + # on the scenario network and everything that addresses it by name uses the alias. + return [EXECUTION_CLIENT_ALIAS] + def get_execution_metrics_address(self) -> str: - # Metrics endpoint is required before the actual execution container is started - return f"{self.get_execution_client_container_name()}:{CLIENT_METRICS_PORT}" + # Rendered into the Alloy config before the execution container exists, so it cannot be an IP. + return f"{EXECUTION_CLIENT_ALIAS}:{CLIENT_METRICS_PORT}" def get_execution_client_engine_url( self,