Cost Exporter is a small tool that gets your cost and usage metrics from AWS Cost Explorer and outputs them in the Prometheus format.
The main motivation for this project is because I can.
There are other similar projects out there (see the "Inspiration" section) but each of them seems to solve a very special use case of their respective creators.
Thus, I have created another project that solves my special use case! Also, there is a caveat when working with the AWS Cost Explorer API: you have to pay $0.01 for each API call. While it doesn't sound like much, it's rather amusing that one has to pay to save costs.
Some of the other projects make AWS API calls ad-hoc when a request for metrics comes. This can make the whole setup rather expensive for such a simple task. This project makes calls every hour or day (depends on the metrics granularity), (see "Implementation" for more details), thus reducing the number of API calls to a minimum. These data doesn't change that often anyway.
There is however a general purpose Cost Exporter by Grafana. You should use that one if you're planning to run things in production, because it's maintained by an actual company and not a random dude on the internet, who spends half a year battling the weather-induced depresion.
Still, by the time I discovered the Grafana's exporter, I have already started some work related to this one. In theory, this exporter should also be fairely extensible both in terms of the cloud probider support (see the and in terms of the available formats and output methods (see the "Implementation" section for more information).
However, I work with AWS, so only AWS is supported for now. Also, the metrcs are available in the Prometheus format on an HTTP endpoint, because this is kind of the industry standard.
There is a ready-to-go Helm chart available in the charts/cost-exporter directory.
However, Cost Exporter is a Go application which also has a Docker image available.
So, you can run it in any environment you want.
Cost Exporter comes with a Helm chart, so you can deploy into a Kubernetes cluster.
The chart is located in the charts/cost-exporter directory of
this repository.
To install it using the Helm chart, do:
helm repo add ...helm upgrade --install cost-exporter oci://ghcr.io/grem11n/charts/cost-exporter --set serviceAccount.awsRoleArn="..."You need to provide an IAM Role ARN, so the Cost Exporter pods can access AWS API, as well as the required configuration for AWS Cost Explorer.
For the rest of the available Helm values, see the README in the chart directory.
There is an example configuration available in the config.example.yaml file.
Cost Exporter tries to use sane defaults, so you only really need to care about which metrics do you want to expose.
Currently, only the Prometheus format is supported, and the exporter outpts mertics on an HTTP endpoint. You can configure both the port and the endpoint to scrape the metrics, though.
On top of the cost metrics, Cost Explorer also outputs some custom metrics on the same endpoint. Here's the list of available internal metrics with their types, units and descriptions.
| Metric Name | Type | Unit | Description |
|---|---|---|---|
| aws_calls_total | count |
Total calls made to AWS API | |
| aws_get_metrics_duration | histogram |
ms |
Duration of API calls to AWS |
| cost_metrics_total | counter |
Total number of the exported cost metrics | |
| prometheus_aws_conversion_duration_bucket | histogram |
ms |
Time it takes to convert the cost metrics |
Cost Exporter uses Uber's zap library to provide structured logs.
You can control the verbosity using the LOG_LEVEL environment variable.
Standard log levels are available, e.g. INFO, DEBUG, ERROR, etc.
The default log level is set to INFO.
In nutshell, Cost Exporter is just a bunch of control loops that use a single exchange point. Because everything is a control loop, when you have worked with Kubernetes long enough.
There are three types of loops:
- Clients: cloud provider clients, take care of retrieving metrics from the cloud provider API
- Converters: take care of converting whatever format a cloud provider provides into th e output format e.g. Prometheus. For now, I assume that while there can be multiple cloud clients, there should be only a single output format
- Outputs: "metric sinks", take care of providing converted metrics to the clients
All the loops use a single sync.Map as an exchange point.
flowchart TD
CC[Cloud Client] -->|Fetch data from the cloud API| CA[Cache]
CA -->|Get raw cost metrics from cache| CV[Converter]
CV -->|Put converted metrics| CA
CA -->|Get converted metrics| O[Output]
O --> CL([Client])
Clients, converters, and outputs are implemented as registries of plugins. Thus, it should be relatively easy to add new ones. However, I personally believe that that is only make sense to have a single converted format per exporter.
Initially, this project was designed around a single cache to decrease the number of AWS API calls, since those are paid. However, later on I realized that this could be a neat way to make this exporter extensible.
- Cloud Clients:
- AWS
- Converters:
- AWS to Prometheus
- Outputs:
- HTTP listener
Since each client, converter, and output is essentially a plugin, it's possible to extend this exporter to support other cloud providers, output formats, and metric sinks.
For example, it should be possible to add a client for Azure or Google Cloud. However, I am personally not familiar with these providers.
Also, it should be possible to, for example, push metrics to CloudWatch using their metrics format, etc. After all, CloudWatch is an AWS native tool.
There are some things that could be improved in the codebase, e.g.:
- Reduce the amount of hardcode required to set Prometheus labels and metric names
- Automate the sync between the tag creation and Helm chart update somehow
- Add other deploy manifests. For example, Terraform configuration for AWS ECS
- Create a Grafana dashboard based on the metrics
Since it's not particularly likely that I will do any serious updates to this project, feel free to create a PR! Otherwise, create an new issue with your feedback and suggestions!
Also, there are some ideas for improvements in the "Further Thoughts" section.
Release Process
- Keep the version and the
AppVersionin the Helm chart in sync - Preferably, keep the version and the Helm chart version itself also in sync
- In case of any changes to the Helm chart, make sure to re-generate its README file
- Update this README file with the new version icon before releasing
- Create a new tag and let GHA and GoReleaser do their job