From c93fc1beb7f8d65c840f389f5f9f337e07c891bb Mon Sep 17 00:00:00 2001 From: willcl-ark Date: Mon, 11 Sep 2023 15:49:19 +0100 Subject: [PATCH] add container resource profiles Adds a few demo profiles such as laptop, desktop, raspberry_pi, server to the graph config. While we are using docker-compose, and not swarm, we cannot easily limit physical CPUS (withough downgrading to docker-compose v2), but we can get "relative resourse usage", and memory limitations. --- README.md | 3 +++ src/templates/example.graphml | 5 +++++ src/warnet/resources.py | 23 +++++++++++++++++++++++ src/warnet/tank.py | 22 ++++++++++++++++++++-- src/warnet/warnet.py | 3 +-- 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/warnet/resources.py diff --git a/README.md b/README.md index 590257608..ed7335860 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ The graphml file has the following specification: * `y` specifies the node's y position when rendered in a GUI * `version` specifies the node's Bitcoin Core major version, or built branch * `bitcoin_config` is a comma-separated list of values the node should apply to it's bitcoin.conf, using bitcoin.conf syntax +* `profile` is a profile name from the pre-built selection in src/warnet/resources.py `version` should be either a version number from the pre-compiled binary list on https://bitcoincore.org/bin/ **or** a built branch using `/#` syntax. @@ -54,6 +55,7 @@ Nodes can be added to the graph as follows: 24.0 uacomment=warnet0_v24,debugexclude=libevent + ``` @@ -66,6 +68,7 @@ Or for a custom built branch with traffic shaping rules applied: vasild/bitcoin#relay_tx_to_priv_nets uacomment=warnet1_custom,debug=1 tc qdisc add dev eth0 root netem delay 100ms +raspberry_pi ``` diff --git a/src/templates/example.graphml b/src/templates/example.graphml index 2bd485061..21a26ba2f 100644 --- a/src/templates/example.graphml +++ b/src/templates/example.graphml @@ -1,6 +1,8 @@ + + 25.0 @@ -13,10 +15,13 @@ 23.2 uacomment=w2 + laptop 22.1 uacomment=w3 + raspberry_pi + tc qdisc add dev eth0 root netem delay 1000ms 0.21.2 diff --git a/src/warnet/resources.py b/src/warnet/resources.py new file mode 100644 index 000000000..5ff66aa77 --- /dev/null +++ b/src/warnet/resources.py @@ -0,0 +1,23 @@ +# resource profile presets +resource_profiles = { + "default": { + "cpus": "4", + "memory": "4G", + }, + "raspberry_pi": { + "cpus": "2", + "memory": "2G", + }, + "laptop": { + "cpus": "4", + "memory": "8G", + }, + "desktop": { + "cpus": "8", + "memory": "16G", + }, + "server": { + "cpus": "16", + "memory": "32G", + }, +} diff --git a/src/warnet/tank.py b/src/warnet/tank.py index 769eb9cbc..ad0ae9fb3 100644 --- a/src/warnet/tank.py +++ b/src/warnet/tank.py @@ -18,6 +18,7 @@ SUPPORTED_TAGS, get_architecture, ) +from warnet.resources import resource_profiles CONTAINER_PREFIX_BITCOIND = "tank" CONTAINER_PREFIX_PROMETHEUS = "prometheus_exporter" @@ -35,6 +36,7 @@ def __init__(self): self.conf_file = None self.torrc_file = None self.netem = None + self.resource_profile = None self.rpc_port = 18443 self.rpc_user = "warnet_user" self.rpc_password = "2themoon" @@ -53,6 +55,7 @@ def __str__(self) -> str: f"\tConf: {self.conf}\n" f"\tConf File: {self.conf_file}\n" f"\tNetem: {self.netem}\n" + f"\tProfile: {self.resource_profile}\n" f"\tIPv4: {self._ipv4}\n" f"\t)" ) @@ -78,6 +81,13 @@ def from_graph_node(cls, index, warnet): self.conf = node["bitcoin_config"] if "tc_netem" in node: self.netem = node["tc_netem"] + # TODO: updgrade to use docker swarm mode, then we can properly set CPU#'s + if "profile" in node: + if node["profile"] not in resource_profiles: + logger.warning(f"Unknown profile {node['profile']} set for node {self.index}") + else: + self.resource_profile = resource_profiles[node["profile"]] + logger.debug(f"Setting profile {node['profile']} for node {self.index}") with open(self.warnet.fork_observer_config, "a") as f: f.write( f""" @@ -160,8 +170,8 @@ def apply_network_conditions(self): return # Apply the network condition to the container - rcode, result = self.exec(self.netem) - if rcode == 0: + result = self.container.exec_run(cmd=self.netem, user="root") + if result.exit_code == 0: logger.info( f"Successfully applied network conditions to {self.container_name}: `{self.netem}`" ) @@ -257,6 +267,14 @@ def add_services(self, services): # } } ) + # this could be updated to deploy {CPU, memory} if we use swarm or similar + if self.resource_profile: + services[self.container_name].update( + { + "cpu_shares": int(self.resource_profile.get("cpus", "0.5")) * 1024, + "mem_limit": self.resource_profile.get("memory", "512M"), + } + ) # Add the prometheus data exporter in a neighboring container services[self.exporter_name] = { diff --git a/src/warnet/warnet.py b/src/warnet/warnet.py index d489ac410..443d5899c 100644 --- a/src/warnet/warnet.py +++ b/src/warnet/warnet.py @@ -175,10 +175,9 @@ def apply_zone_file(self): content_str = f.read().replace("'", "'\\''") # Overwrite all existing content - result = seeder.exec_run( + _result = seeder.exec_run( f"sh -c 'echo \"{content_str}\" > /etc/bind/invalid.zone'" ) - logger.debug(f"result of updating {ZONE_FILE_NAME}: {result}") # Reload that single zone only seeder.exec_run("rndc reload invalid")