From 49fb96fc17ccb47d1e857c66fd35e2510dcea056 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 09:11:18 +0000 Subject: [PATCH 01/12] Refactor host info --- os_capacity/prometheus.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index 5c97e6b..d77a7b3 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -136,7 +136,7 @@ def get_resource_provider_info(compute_client, placement_client): return resource_providers, project_to_aggregate -def print_details(compute_client, placement_client): +def print_host_details(compute_client, placement_client): flavors = list(compute_client.flavors()) capacity_per_flavor = get_capacity_per_flavor(placement_client, flavors) @@ -145,7 +145,7 @@ def print_details(compute_client, placement_client): for flavor_name in flavor_names: counts = capacity_per_flavor.get(flavor_name, {}).values() total = 0 if not counts else sum(counts) - print(f'openstack_total_capacity_per_flavor{{flavor="{flavor_name}"}} {total}') + print(f'openstack_free_capacity_by_flavor{{flavor="{flavor_name}"}} {total}') # capacity per host resource_providers, project_to_aggregate = get_resource_provider_info( @@ -169,7 +169,7 @@ def print_details(compute_client, placement_client): if project_filter: host_str += f',project_filter="{project_filter}"' print( - f'openstack_capacity_by_hostname{{{host_str},flavor="{flavor_name}"}} {our_count}' + f'openstack_free_capacity_by_hypervisor{{{host_str},flavor="{flavor_name}"}} {our_count}' ) free_space_found = True if not free_space_found: @@ -178,14 +178,18 @@ def print_details(compute_client, placement_client): for project, names in project_to_aggregate.items(): for name in names: print( - f'openstack_project_filter{{project="{project}",aggregate="{name}"}} 1' + f'openstack_project_filter_aggregate{{project="{project}",aggregate="{name}"}} 1' ) +def print_project_usage_(indentity_client, placement_client): + pass + + def print_exporter_data(app): - print_details(app.compute_client, app.placement_client) + print_host_free_details(app.compute_client, app.placement_client) if __name__ == "__main__": conn = openstack.connect() - print_details(conn.compute, conn.placement) + print_host_details(conn.compute, conn.placement) From 890e0414f851a186d3569831d20e04461ecd433f Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 09:50:48 +0000 Subject: [PATCH 02/12] Fix up some debug messages --- os_capacity/prometheus.py | 55 ++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index d77a7b3..29ca614 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -10,8 +10,7 @@ def get_capacity_per_flavor(placement_client, flavors): capacity_per_flavor = {} for flavor in flavors: - resources, traits = get_placement_request(flavor) - max_per_host = get_max_per_host(placement_client, resources, traits) + max_per_host = get_max_per_host(placement_client, flavor) capacity_per_flavor[flavor.name] = max_per_host return capacity_per_flavor @@ -46,7 +45,8 @@ def add_defaults(resources, flavor, skip_vcpu=False): return resources, required_traits -def get_max_per_host(placement_client, resources, required_traits): +def get_max_per_host(placement_client, flavor): + resources, required_traits = get_placement_request(flavor) resource_str = ",".join( [key + ":" + str(value) for key, value in resources.items() if value] ) @@ -80,7 +80,7 @@ def get_max_per_host(placement_client, resources, required_traits): if max_counts: count_per_rp[rp_uuid] = min(max_counts) if not count_per_rp: - print(f"# WARNING - no candidates for: {params}") + print(f"# WARNING - no candidates hosts for flavor: {flavor.name} {params}") return count_per_rp @@ -178,12 +178,52 @@ def print_host_details(compute_client, placement_client): for project, names in project_to_aggregate.items(): for name in names: print( - f'openstack_project_filter_aggregate{{project="{project}",aggregate="{name}"}} 1' + f'openstack_project_filter_aggregate{{project_id="{project}",aggregate="{name}"}} 1' ) -def print_project_usage_(indentity_client, placement_client): - pass +def print_project_usage(indentity_client, placement_client, compute_client): + projects = {proj.id: dict(name=proj.name) for proj in indentity_client.projects()} + for project_id in projects.keys(): + # TODO(johngarbutt) On Xena we should do consumer_type=INSTANCE using 1.38! + response = placement_client.get( + f"/usages?project_id={project_id}", + headers={"OpenStack-API-Version": "placement 1.19"}, + ) + response.raise_for_status() + usages = response.json() + projects[project_id]["usages"] = usages["usages"] + + response = compute_client.get( + f"/os-quota-sets/{project_id}", + headers={"OpenStack-API-Version": "compute 2.20"}, + ) + response.raise_for_status() + quotas = response.json().get("quota_set", {}) + projects[project_id]["quotas"] = dict( + CPUS=quotas.get("cores"), MEMORY_MB=quotas.get("ram") + ) + + # print(json.dumps(projects, indent=2)) + for project_id, data in projects.items(): + name = data["name"] + project_usages = data["usages"] + for resource, amount in project_usages.items(): + print( + f'openstack_project_usage{{project_id="{project_id}",' + f'project_name="{name}",resource="{resource}"}} {amount}' + ) + + if not project_usages: + # skip projects with zero usage? + print(f"# WARNING no usage for project: {name} {project_id}") + continue + project_quotas = data["quotas"] + for resource, amount in project_quotas.items(): + print( + f'openstack_project_quota{{project_id="{project_id}",' + f'project_name="{name}",resource="{resource}"}} {amount}' + ) def print_exporter_data(app): @@ -193,3 +233,4 @@ def print_exporter_data(app): if __name__ == "__main__": conn = openstack.connect() print_host_details(conn.compute, conn.placement) + print_project_usage(conn.identity, conn.placement, conn.compute) From 205c00fb9e4a35e837605f0c8913fded8694cecf Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 10:22:38 +0000 Subject: [PATCH 03/12] Add per host capacity details (ignoring flavors) --- os_capacity/prometheus.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index 29ca614..55b453c 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -173,6 +173,7 @@ def print_host_details(compute_client, placement_client): ) free_space_found = True if not free_space_found: + # TODO(johngarbutt) allocation candidates only returns some not all candidates! print(f"# WARNING - no free spaces found for {hostname}") for project, names in project_to_aggregate.items(): @@ -180,6 +181,7 @@ def print_host_details(compute_client, placement_client): print( f'openstack_project_filter_aggregate{{project_id="{project}",aggregate="{name}"}} 1' ) + return resource_providers def print_project_usage(indentity_client, placement_client, compute_client): @@ -226,11 +228,46 @@ def print_project_usage(indentity_client, placement_client, compute_client): ) +def print_host_usage(resource_providers, placement_client): + for name, data in resource_providers.items(): + rp_id = data["uuid"] + response = placement_client.get( + f"/resource_providers/{rp_id}/usages", + headers={"OpenStack-API-Version": "placement 1.19"}, + ) + response.raise_for_status() + rp_usages = response.json()["usages"] + resource_providers[name]["usages"] = rp_usages + + for resource, amount in rp_usages.items(): + print( + f'openstack_hypervisor_usage{{hypervisor="{name}",' + f'resource="{resource}"}} {amount}' + ) + + response = placement_client.get( + f"/resource_providers/{rp_id}/inventories", + headers={"OpenStack-API-Version": "placement 1.19"}, + ) + response.raise_for_status() + inventories = response.json()["inventories"] + resource_providers[name]["inventories"] = inventories + + for resource, data in inventories.items(): + amount = data["total"] - data["reserved"] + print( + f'openstack_hypervisor_allocatable_capacity{{hypervisor="{name}",' + f'resource="{resource}"}} {amount}' + ) + # print(json.dumps(resource_providers, indent=2)) + + def print_exporter_data(app): print_host_free_details(app.compute_client, app.placement_client) if __name__ == "__main__": conn = openstack.connect() - print_host_details(conn.compute, conn.placement) + resource_providers = print_host_details(conn.compute, conn.placement) print_project_usage(conn.identity, conn.placement, conn.compute) + print_host_usage(resource_providers, conn.placement) From 17819a8520390771dcbe932fd9252f1364f6b8c4 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 11:46:41 +0000 Subject: [PATCH 04/12] Starting to add standalone exporter --- os_capacity/prometheus.py | 50 +++++++++++++++++++++++++++++++++++---- requirements.txt | 1 + 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index 55b453c..527ad9d 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -2,8 +2,12 @@ import collections import json +import time +import uuid import openstack +import prometheus_client as prom_client +from prometheus_client import core as prom_core def get_capacity_per_flavor(placement_client, flavors): @@ -266,8 +270,46 @@ def print_exporter_data(app): print_host_free_details(app.compute_client, app.placement_client) +class OpenStackCapacityCollector(object): + def __init__(self): + self.conn = openstack.connect() + openstack.enable_logging(debug=True) + print("got openstack connection") + # for some reason this makes the logging work?! + self.conn.compute.flavors() + + def collect(self): + start_time = time.perf_counter() + collect_id = uuid.uuid4().hex + print(f"Collect started {collect_id}") + guages = [] + + conn = openstack.connect() + openstack.enable_logging(debug=True) + try: + resource_providers = print_host_details(conn.compute, conn.placement) + print_project_usage(conn.identity, conn.placement, conn.compute) + print_host_usage(resource_providers, conn.placement) + except Exception as e: + print(f"error {e}") + + gauge = prom_core.GaugeMetricFamily( + "random_number", + "A random number generator, I have no better idea", + labels=["randomNum"], + ) + gauge.add_metric(["mine"], 42) + guages.append(gauge) + + end_time = time.perf_counter() + duration = end_time - start_time + print(f"Collect complete {collect_id} it took {duration} seconds") + return guages + + if __name__ == "__main__": - conn = openstack.connect() - resource_providers = print_host_details(conn.compute, conn.placement) - print_project_usage(conn.identity, conn.placement, conn.compute) - print_host_usage(resource_providers, conn.placement) + prom_client.start_http_server(9000) + prom_core.REGISTRY.register(OpenStackCapacityCollector()) + # there must be a better way! + while True: + time.sleep(5000) diff --git a/requirements.txt b/requirements.txt index 351edb0..ef00cfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ cliff>=2.8.0 # Apache os-client-config>=1.28.0 # Apache-2.0 pbr>=2.0.0,!=2.1.0 # Apache-2.0 +prometheus-client==0.16.0 From f97eb87ea58931bf22d897fbdccf9524734d173e Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 12:35:15 +0000 Subject: [PATCH 05/12] Add more host guages --- os_capacity/prometheus.py | 60 +++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index 527ad9d..43be396 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -140,18 +140,29 @@ def get_resource_provider_info(compute_client, placement_client): return resource_providers, project_to_aggregate -def print_host_details(compute_client, placement_client): +def get_host_details(compute_client, placement_client): flavors = list(compute_client.flavors()) capacity_per_flavor = get_capacity_per_flavor(placement_client, flavors) # total capacity per flavor + free_by_flavor_total = prom_core.GaugeMetricFamily( + "openstack_free_capacity_by_flavor_total", + "Free capacity if you fill the cloud full of each flavor", + labels=["flavor_name"], + ) flavor_names = sorted([f.name for f in flavors]) for flavor_name in flavor_names: counts = capacity_per_flavor.get(flavor_name, {}).values() total = 0 if not counts else sum(counts) - print(f'openstack_free_capacity_by_flavor{{flavor="{flavor_name}"}} {total}') + free_by_flavor_total.add_metric([flavor_name], total) + # print(f'openstack_free_capacity_by_flavor{{flavor="{flavor_name}"}} {total}') # capacity per host + free_by_flavor_hypervisor = prom_core.GaugeMetricFamily( + "openstack_free_capacity_hypervisor_by_flavor", + "Free capacity if you fill the cloud full of each flavor", + labels=["hypervisor", "flavor_name", "az_aggregate", "project_aggregate"], + ) resource_providers, project_to_aggregate = get_resource_provider_info( compute_client, placement_client ) @@ -165,27 +176,32 @@ def print_host_details(compute_client, placement_client): our_count = all_counts.get(rp_id, 0) if our_count == 0: continue - host_str = f'hypervisor="{hostname}"' - az = rp.get("az") - if az: - host_str += f',az="{az}"' - project_filter = rp.get("project_filter") - if project_filter: - host_str += f',project_filter="{project_filter}"' - print( - f'openstack_free_capacity_by_hypervisor{{{host_str},flavor="{flavor_name}"}} {our_count}' + az = rp.get("az", "") + project_filter = rp.get("project_filter", "") + free_by_flavor_hypervisor.add_metric( + [hostname, flavor_name, az, project_filter], our_count ) free_space_found = True if not free_space_found: # TODO(johngarbutt) allocation candidates only returns some not all candidates! print(f"# WARNING - no free spaces found for {hostname}") + project_filter_aggregates = prom_core.GaugeMetricFamily( + "openstack_project_filter_aggregate", + "Free capacity if you fill the cloud full of each flavor", + labels=["project_id", "aggregate"], + ) for project, names in project_to_aggregate.items(): for name in names: - print( - f'openstack_project_filter_aggregate{{project_id="{project}",aggregate="{name}"}} 1' - ) - return resource_providers + project_filter_aggregates.add_metric([project, name], 1) + # print( + # f'openstack_project_filter_aggregate{{project_id="{project}",aggregate="{name}"}} 1' + # ) + return resource_providers, [ + free_by_flavor_total, + free_by_flavor_hypervisor, + project_filter_aggregates, + ] def print_project_usage(indentity_client, placement_client, compute_client): @@ -287,20 +303,16 @@ def collect(self): conn = openstack.connect() openstack.enable_logging(debug=True) try: - resource_providers = print_host_details(conn.compute, conn.placement) + resource_providers, host_guages = get_host_details( + conn.compute, conn.placement + ) + guages += host_guages + print_project_usage(conn.identity, conn.placement, conn.compute) print_host_usage(resource_providers, conn.placement) except Exception as e: print(f"error {e}") - gauge = prom_core.GaugeMetricFamily( - "random_number", - "A random number generator, I have no better idea", - labels=["randomNum"], - ) - gauge.add_metric(["mine"], 42) - guages.append(gauge) - end_time = time.perf_counter() duration = end_time - start_time print(f"Collect complete {collect_id} it took {duration} seconds") From d2f22de0a141295bfb2504e12338b08dfe028071 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 12:46:17 +0000 Subject: [PATCH 06/12] Add project guages --- os_capacity/prometheus.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index 43be396..cff2ae4 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -160,7 +160,7 @@ def get_host_details(compute_client, placement_client): # capacity per host free_by_flavor_hypervisor = prom_core.GaugeMetricFamily( "openstack_free_capacity_hypervisor_by_flavor", - "Free capacity if you fill the cloud full of each flavor", + "Free capacity for each hypervisor if you fill remaining space full of each flavor", labels=["hypervisor", "flavor_name", "az_aggregate", "project_aggregate"], ) resource_providers, project_to_aggregate = get_resource_provider_info( @@ -188,7 +188,7 @@ def get_host_details(compute_client, placement_client): project_filter_aggregates = prom_core.GaugeMetricFamily( "openstack_project_filter_aggregate", - "Free capacity if you fill the cloud full of each flavor", + "Mapping of project_ids to aggregates in the host free capacity info.", labels=["project_id", "aggregate"], ) for project, names in project_to_aggregate.items(): @@ -204,7 +204,7 @@ def get_host_details(compute_client, placement_client): ] -def print_project_usage(indentity_client, placement_client, compute_client): +def get_project_usage(indentity_client, placement_client, compute_client): projects = {proj.id: dict(name=proj.name) for proj in indentity_client.projects()} for project_id in projects.keys(): # TODO(johngarbutt) On Xena we should do consumer_type=INSTANCE using 1.38! @@ -225,16 +225,23 @@ def print_project_usage(indentity_client, placement_client, compute_client): projects[project_id]["quotas"] = dict( CPUS=quotas.get("cores"), MEMORY_MB=quotas.get("ram") ) - # print(json.dumps(projects, indent=2)) + + project_usage_guage = prom_core.GaugeMetricFamily( + "openstack_project_usage", + "Current placement allocations per project.", + labels=["project_id", "project_name", "placement_resource"], + ) + project_quota_guage = prom_core.GaugeMetricFamily( + "openstack_project_quota", + "Current quota set to limit max resource allocations per project.", + labels=["project_id", "project_name", "quota_resource"], + ) for project_id, data in projects.items(): name = data["name"] project_usages = data["usages"] for resource, amount in project_usages.items(): - print( - f'openstack_project_usage{{project_id="{project_id}",' - f'project_name="{name}",resource="{resource}"}} {amount}' - ) + project_usage_guage.add_metric([project_id, name, resource], amount) if not project_usages: # skip projects with zero usage? @@ -242,10 +249,8 @@ def print_project_usage(indentity_client, placement_client, compute_client): continue project_quotas = data["quotas"] for resource, amount in project_quotas.items(): - print( - f'openstack_project_quota{{project_id="{project_id}",' - f'project_name="{name}",resource="{resource}"}} {amount}' - ) + project_quota_guage.add_metric([project_id, name, resource], amount) + return [project_usage_guage, project_quota_guage] def print_host_usage(resource_providers, placement_client): @@ -308,7 +313,7 @@ def collect(self): ) guages += host_guages - print_project_usage(conn.identity, conn.placement, conn.compute) + guages += get_project_usage(conn.identity, conn.placement, conn.compute) print_host_usage(resource_providers, conn.placement) except Exception as e: print(f"error {e}") From 8609d7f8987de69cccde950b03be4946ee06b436 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 12:59:50 +0000 Subject: [PATCH 07/12] Add host guages --- README.rst | 140 +++----------------------------------- os_capacity/prometheus.py | 30 ++++---- 2 files changed, 26 insertions(+), 144 deletions(-) diff --git a/README.rst b/README.rst index d26cbad..2b60106 100644 --- a/README.rst +++ b/README.rst @@ -3,10 +3,6 @@ os-capacity This is a prototype tool to extract capacity information. -.. note:: - - This is currently quite specific to Ironic powered OpenStack Nova clouds. - Install ------- @@ -21,139 +17,21 @@ Now lets get that installed inside a virtual environment: .. code:: - virtualenv .venv-test - source .venv-test/bin/activate + python3 -m virtualenv .venv + source .venv/bin/activate pip install -U . Prometheus Exporter ------------------- -Assuming you have clouds.yaml in the right place and OS_CLOUD set: +Assuming you have clouds.yaml in the right place, +you can run the exporter doing something like this: .. code:: - ./os_capacity/prometheus.py - openstack_total_capacity_per_flavor{flavor="small"} 1 - openstack_capacity_by_hostname{hypervisor="aio",flavor="small"} 1 - - -TODOs we need support for: - -* add request filter support for require_tenant_aggregate, - map_az_to_placement_aggregate and compute_status_filter - -Configuration -------------- - -The easiest way to configure this is to populate a typical OpenStack RC file: - -.. code:: - - cat > .openrc < mytestrun + cat mytestrun diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index cff2ae4..1d2f1d7 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -253,7 +253,17 @@ def get_project_usage(indentity_client, placement_client, compute_client): return [project_usage_guage, project_quota_guage] -def print_host_usage(resource_providers, placement_client): +def get_host_usage(resource_providers, placement_client): + usage_guage = prom_core.GaugeMetricFamily( + "openstack_hypervisor_placement_allocated", + "Currently allocated resource for each provider in placement.", + labels=["hypervisor", "resource"], + ) + capacity_guage = prom_core.GaugeMetricFamily( + "openstack_hypervisor_placement_allocatable_capacity", + "The total allocatable resource in the placement inventory.", + labels=["hypervisor", "resource"], + ) for name, data in resource_providers.items(): rp_id = data["uuid"] response = placement_client.get( @@ -265,10 +275,7 @@ def print_host_usage(resource_providers, placement_client): resource_providers[name]["usages"] = rp_usages for resource, amount in rp_usages.items(): - print( - f'openstack_hypervisor_usage{{hypervisor="{name}",' - f'resource="{resource}"}} {amount}' - ) + usage_guage.add_metric([name, resource], amount) response = placement_client.get( f"/resource_providers/{rp_id}/inventories", @@ -280,11 +287,9 @@ def print_host_usage(resource_providers, placement_client): for resource, data in inventories.items(): amount = data["total"] - data["reserved"] - print( - f'openstack_hypervisor_allocatable_capacity{{hypervisor="{name}",' - f'resource="{resource}"}} {amount}' - ) + capacity_guage.add_metric([name, resource], amount) # print(json.dumps(resource_providers, indent=2)) + return [usage_guage, capacity_guage] def print_exporter_data(app): @@ -294,7 +299,7 @@ def print_exporter_data(app): class OpenStackCapacityCollector(object): def __init__(self): self.conn = openstack.connect() - openstack.enable_logging(debug=True) + openstack.enable_logging(debug=False) print("got openstack connection") # for some reason this makes the logging work?! self.conn.compute.flavors() @@ -306,15 +311,14 @@ def collect(self): guages = [] conn = openstack.connect() - openstack.enable_logging(debug=True) + openstack.enable_logging(debug=False) try: resource_providers, host_guages = get_host_details( conn.compute, conn.placement ) guages += host_guages - guages += get_project_usage(conn.identity, conn.placement, conn.compute) - print_host_usage(resource_providers, conn.placement) + guages += get_host_usage(resource_providers, conn.placement) except Exception as e: print(f"error {e}") From a78d658d0b8de6cd653073807de8f2943645b5f1 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 13:17:41 +0000 Subject: [PATCH 08/12] Fix host capacity by using allocation ratio --- os_capacity/prometheus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os_capacity/prometheus.py b/os_capacity/prometheus.py index 1d2f1d7..ade0478 100755 --- a/os_capacity/prometheus.py +++ b/os_capacity/prometheus.py @@ -286,7 +286,7 @@ def get_host_usage(resource_providers, placement_client): resource_providers[name]["inventories"] = inventories for resource, data in inventories.items(): - amount = data["total"] - data["reserved"] + amount = int(data["total"] * data["allocation_ratio"]) - data["reserved"] capacity_guage.add_metric([name, resource], amount) # print(json.dumps(resource_providers, indent=2)) return [usage_guage, capacity_guage] From 00f883c5f2772bf753ac1cf5cc60f7201bafc9bc Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 13:18:36 +0000 Subject: [PATCH 09/12] Add a docker build --- .github/workflows/docker.yaml | 56 +++++++++++++++++++++++++++++++++++ Dockerfile | 9 ++++++ 2 files changed, 65 insertions(+) create mode 100644 .github/workflows/docker.yaml create mode 100644 Dockerfile diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 0000000..fbfcc21 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,56 @@ +name: Docker image +# Run the tasks on every push +on: push +jobs: + build_push_api: + name: Build and push execution environment + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Set up Docker layer caching + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Calculate metadata for image + id: image-meta + uses: docker/metadata-action@v3 + with: + images: ghcr.io/stackhpc/os-capacity + # Produce the branch name or tag and the SHA as tags + tags: | + type=ref,event=branch + type=ref,event=tag + type=sha,prefix= + - name: Build and push image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ steps.image-meta.outputs.tags }} + labels: ${{ steps.image-meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + # https://github.com/docker/buildx/pull/535 + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ce69d5f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:22.04 + +RUN apt-get update && apt-get upgrade -y && apt-get install python3-pip tini -y && apt-get clean + +COPY . /opt/os-capacity +RUN pip install -U -e /opt/os-capacity + +ENTRYPOINT ["tini", "-g", "--"] +CMD ["python3", "/opt/os-capacity/os_capacity/prometheus.yaml"] From 4d3f58316d7540267934e0d56c97e31f2f7a4943 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 13:21:18 +0000 Subject: [PATCH 10/12] Fix command in the docker image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ce69d5f..63cd6cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,4 +6,4 @@ COPY . /opt/os-capacity RUN pip install -U -e /opt/os-capacity ENTRYPOINT ["tini", "-g", "--"] -CMD ["python3", "/opt/os-capacity/os_capacity/prometheus.yaml"] +CMD ["python3", "/opt/os-capacity/os_capacity/prometheus.py"] From 8eb51ae9ebee534c2f41434f6e2dd17ecd3a28ba Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 13:35:52 +0000 Subject: [PATCH 11/12] Add docker note to README --- README.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rst b/README.rst index 2b60106..aba4356 100644 --- a/README.rst +++ b/README.rst @@ -35,3 +35,10 @@ you can run the exporter doing something like this: ./os_capacity/prometheus.py & curl localhost:9000 > mytestrun cat mytestrun + +Or just run via docker or similar::: + + docker run -d --name os_capacity \ + --mount type=bind,source=/etc/openstack/,target=/etc/openstack/ \ + --env OS_CLOUD=openstack --env OS_CLIENT_CONFIG_FILE=/etc/openstack/mycloud.yaml \ + -p 9000:9000 ghcr.io/stackhpc/os-capacity:master From 7bd3e7f968d5f3a74898480efe51c89acd409e28 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Fri, 27 Jan 2023 13:41:46 +0000 Subject: [PATCH 12/12] Make a smaller docker file --- Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 63cd6cb..83bcbea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,9 @@ FROM ubuntu:22.04 RUN apt-get update && apt-get upgrade -y && apt-get install python3-pip tini -y && apt-get clean -COPY . /opt/os-capacity -RUN pip install -U -e /opt/os-capacity +COPY ./requirements.txt /opt/os-capacity/requirements.txt +RUN pip install -U -r /opt/os-capacity/requirements.txt -ENTRYPOINT ["tini", "-g", "--"] -CMD ["python3", "/opt/os-capacity/os_capacity/prometheus.py"] +COPY ./os_capacity/prometheus.py /opt/os-capacity/prometheus.py +ENTRYPOINT ["tini", "--"] +CMD ["python3", "/opt/os-capacity/prometheus.py"]