Skip to content

Commit 34b0e4d

Browse files
jakemulfmargaretkennedychipkenthythloda
authored
Prometheus sample app (#44)
* Prometheus sample app * cleaned up code comments and improved readme * Apply suggestions from code review Co-authored-by: margaretkennedy <82049573+margaretkennedy@users.noreply.github.com> * added prometheus intro * apparently this was an easy fix and works * Converted to prometheus time stamp * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Chip Kent <5250374+chipkent@users.noreply.github.com> * code review updates * added sample queries on the created tables * Update prometheus/Dockerfile Co-authored-by: Amanda L Martin <hythloda@gmail.com> * Update prometheus/docker-compose.yml Co-authored-by: Amanda L Martin <hythloda@gmail.com> * file restructure from code review * Clarified where to run ngrok Co-authored-by: margaretkennedy <82049573+margaretkennedy@users.noreply.github.com> Co-authored-by: Chip Kent <5250374+chipkent@users.noreply.github.com> Co-authored-by: Amanda L Martin <hythloda@gmail.com>
1 parent b199726 commit 34b0e4d

7 files changed

Lines changed: 293 additions & 0 deletions

File tree

prometheus/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM ghcr.io/deephaven/grpc-api
2+
COPY app.d /app.d
3+
RUN pip3 install -r /app.d/requirements.txt

prometheus/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Prometheus
2+
3+
[Prometheus](https://prometheus.io/) is an open-source systems monitoring and alerting toolkit that collects and stores its metrics as time series data. This sample app shows how to ingest data from Prometheus into [Deephaven](https://deephaven.io/).
4+
5+
## How it works
6+
7+
### Deephaven application mode
8+
9+
This app runs using [Deephaven's application mode](https://deephaven.io/core/docs/how-to-guides/app-mode/).
10+
11+
### Components
12+
13+
* `Dockerfile` - The dockerfile for the application. This extends the default Deephaven image to add dependencies. See our guide, [How to install Python packages](https://deephaven.io/core/docs/how-to-guides/install-python-packages/#add-packages-to-a-custom-docker-image), for more information.
14+
* `docker-compose.yml` - The Docker Compose file for the application. This is mostly the same as the [Deephaven docker-compose file](https://raw.githubusercontent.com/deephaven/deephaven-core/main/containers/python-examples/docker-compose.yml) with modifications to run Prometheus, application mode, and the custom dependencies.
15+
* `start.sh` - A simple helper script to launch the application.
16+
* `app.d/app.app` - The Deephaven application mode app file.
17+
* `app.d/requirements.txt` - Python dependencies for the application.
18+
* `app.d/prometheus.py` - The Python script that pulls the data from Prometheus and stores it into Deephaven.
19+
20+
### High level overview
21+
22+
This app pulls data from [Prometheus's API](https://prometheus.io/docs/prometheus/latest/querying/api/) through HTTP requests. The API responses are deserialized, and the desired values are extracted and stored into a Deephaven table.
23+
24+
Once data is collected and tables are created, various [Deephaven queries](https://deephaven.io/core/docs/how-to-guides/simple-python-query/) are then performed on the tables.
25+
26+
This app writes to Deephaven tables both statically and dynamically.
27+
28+
## Dependencies
29+
30+
* The [Deephaven-core dependencies](https://github.com/deephaven/deephaven-core#required-dependencies) are required for this project.
31+
* If you want to use a different Prometheus instance than the default instance, you will need to [install Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) at your desired location.
32+
33+
## Launch
34+
35+
Before launching, you can modify the `PROMETHEUS_QUERIES` and `BASE_URL` values in `prometheus.py` to see the results of different queries, and to point the application at different Prometheus instances.
36+
37+
Once you are set, simply run the following to launch the app:
38+
39+
```
40+
sh start.sh
41+
```
42+
43+
Go to [http://localhost:10000/ide](http://localhost:10000/ide) to view the tables in the top right **Panels** tab!
44+
45+
### Ngrok
46+
47+
48+
:::note
49+
50+
If you are running Prometheus locally and seeing errors like:
51+
52+
```
53+
HTTPConnectionPool(host='localhost', port=9090): Max retries exceeded with url: /api/v1/query?query=up (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f4619929a90>: Failed to establish a new connection: [Errno 111] Connection refused'))
54+
```
55+
56+
you may need to use [Ngrok](https://ngrok.com/) to make HTTP requests to your Prometheus instance.
57+
58+
:::
59+
60+
[Install Ngrok](https://ngrok.com/download) on the machine that is running your Prometheus instance, then run the following in a new terminal window on that same machine:
61+
62+
```
63+
ngrok http 9090
64+
```
65+
66+
Use the URL on the terminal that forwards to <http://localhost:9090> to construct the `BASE_URL` value. Edit this value in `prometheus.py` and re-launch the application. For example:
67+
68+
```
69+
BASE_URL = "{base}/api/v1/query".format(base="http://c818-2603-6081-2300-2640-50c5-4e0a-6c65-498d.ngrok.io")
70+
```

prometheus/app.d/app.app

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type=script
2+
scriptType=python
3+
enabled=true
4+
id=prometheus
5+
name=Prometheus application
6+
file_0=./prometheus.py

prometheus/app.d/prometheus.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
prometheus.py
3+
4+
A simple python script that pulls data from Prometheus's API, and
5+
stores it in a Deephaven table.
6+
7+
This is expected to be run within Deephaven's application mode https://deephaven.io/core/docs/how-to-guides/app-mode/.
8+
9+
After launching, there will be 2 tables within the "Panels" section of the Deephaven UI.
10+
One will be a static table and the other will be continually updating with real data.
11+
12+
@author Jake Mulford
13+
@copyright Deephaven Data Labs LLC
14+
"""
15+
from deephaven.TableTools import newTable, stringCol, dateTimeCol, doubleCol
16+
from deephaven import DynamicTableWriter
17+
from deephaven.DBTimeUtils import millisToTime
18+
import deephaven.Types as dht
19+
from typing import Callable
20+
21+
import requests
22+
23+
import threading
24+
import time
25+
26+
PROMETHEUS_QUERIES = ["up", "go_memstats_alloc_bytes"] #Edit this and add your queries here
27+
BASE_URL = "{base}/api/v1/query".format(base="http://prometheus:9090") #Edit this to your base URL if you're not using a local Prometheus instance
28+
29+
ApplicationState = jpy.get_type("io.deephaven.appmode.ApplicationState")
30+
31+
def make_prometheus_request(prometheus_query, query_url):
32+
"""
33+
A helper method that makes a request on the Prometheus API with the given
34+
query, and returns a list of results containing the timestamp, job, instance, and value for the query.
35+
The data returned by this method will be stored in a Deephaven table.
36+
37+
This assumes that the query is going to return a "vector" type from the Prometheus API.
38+
https://prometheus.io/docs/prometheus/latest/querying/api/#instant-vectors
39+
40+
Args:
41+
prometheus_query (str): The Prometheus query to execute with the API request.
42+
query_url (str): The URL of the query endpoint.
43+
Returns:
44+
list[(date-time, str, str, float)]: List of the timestamps, jobs, instances, and values from the API response.
45+
"""
46+
results = []
47+
query_parameters = {
48+
"query": prometheus_query
49+
}
50+
response = requests.get(query_url, params=query_parameters)
51+
response_json = response.json()
52+
53+
if "data" in response_json.keys():
54+
if "resultType" in response_json["data"] and response_json["data"]["resultType"] == "vector":
55+
for result in response_json["data"]["result"]:
56+
#Prometheus timestamps are in seconds. We multiply by 1000 to convert it to
57+
#milliseconds, then cast to an int() to use the millisToTime() method
58+
timestamp = millisToTime(int(result["value"][0] * 1000))
59+
job = result["metric"]["job"]
60+
instance = result["metric"]["instance"]
61+
value = float(result["value"][1])
62+
results.append((timestamp, job, instance, value))
63+
return results
64+
65+
def start_dynamic(app: ApplicationState):
66+
"""
67+
Deephaven Application Mode method that starts the dynamic data collector.
68+
"""
69+
column_names = ["DateTime", "PrometheusQuery", "Job", "Instance", "Value"]
70+
column_types = [dht.datetime, dht.string, dht.string, dht.string, dht.double]
71+
72+
table_writer = DynamicTableWriter(
73+
column_names,
74+
column_types
75+
)
76+
77+
result = table_writer.getTable()
78+
79+
def thread_func():
80+
while True:
81+
for prometheus_query in PROMETHEUS_QUERIES:
82+
values = make_prometheus_request(prometheus_query, BASE_URL)
83+
84+
for (date_time, job, instance, value) in values:
85+
table_writer.logRow(date_time, prometheus_query, job, instance, value)
86+
time.sleep(2)
87+
88+
app.setField("result_dynamic", result)
89+
thread = threading.Thread(target = thread_func)
90+
thread.start()
91+
92+
def start_static(app: ApplicationState, query_count=5):
93+
"""
94+
Deephaven Application Mode method that starts the static data collector.
95+
96+
query_count sets the number of requests to make. It is recommended to keep this number low,
97+
since it delays how long the Deephaven UI takes to become accessible.
98+
"""
99+
date_time_list = []
100+
prometheus_query_list = []
101+
job_list = []
102+
instance_list = []
103+
value_list = []
104+
105+
for i in range(query_count):
106+
for prometheus_query in PROMETHEUS_QUERIES:
107+
values = make_prometheus_request(prometheus_query, BASE_URL)
108+
109+
for (date_time, job, instance, value) in values:
110+
date_time_list.append(date_time)
111+
prometheus_query_list.append(prometheus_query)
112+
job_list.append(job)
113+
instance_list.append(instance)
114+
value_list.append(value)
115+
time.sleep(2)
116+
117+
result = newTable(
118+
dateTimeCol("DateTime", date_time_list),
119+
stringCol("PrometheusQuery", prometheus_query_list),
120+
stringCol("Job", job_list),
121+
stringCol("Instance", instance_list),
122+
doubleCol("Value", value_list)
123+
)
124+
app.setField("result_static", result)
125+
126+
def update(app: ApplicationState):
127+
"""
128+
Deephaven Application Mode method that does various updates on the initial tables.
129+
130+
You can throw any Deehaven Query in here. The ones in here are simply examples.
131+
"""
132+
#Get the tables from the app
133+
result_static = app.getField("result_static").value()
134+
result_dynamic = app.getField("result_dynamic").value()
135+
136+
#Perform the desired queries, and set the results as new fields
137+
result_static_update = result_static.by("PrometheusQuery")
138+
app.setField("result_static_update", result_static_update)
139+
140+
result_static_average = result_static.dropColumns("DateTime", "Job", "Instance").avgBy("PrometheusQuery")
141+
app.setField("result_static_average", result_static_average)
142+
143+
result_dynamic_update = result_dynamic.by("PrometheusQuery")
144+
app.setField("result_dynamic_update", result_dynamic_update)
145+
146+
result_dynamic_average = result_dynamic.dropColumns("DateTime", "Job", "Instance").avgBy("PrometheusQuery")
147+
app.setField("result_dynamic_average", result_dynamic_average)
148+
149+
def initialize(func: Callable[[ApplicationState], None]):
150+
"""
151+
Deephaven Application Mode initialization method.
152+
"""
153+
app = jpy.get_type("io.deephaven.appmode.ApplicationContext").get()
154+
func(app)
155+
156+
#Start the static and dynamic data collectors
157+
initialize(start_static)
158+
initialize(start_dynamic)
159+
#Run the table updates
160+
initialize(update)

prometheus/app.d/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
certifi==2021.5.30
2+
charset-normalizer==2.0.6
3+
idna==3.2
4+
requests==2.26.0
5+
urllib3==1.26.7

prometheus/docker-compose.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
version: "3.4"
2+
3+
services:
4+
grpc-api:
5+
image: prometheus-deephaven/grpc-api:latest
6+
expose:
7+
- '8080'
8+
volumes:
9+
- ./data:/data
10+
- api-cache:/cache
11+
environment:
12+
- JAVA_TOOL_OPTIONS=-Xmx4g -Ddeephaven.console.type=python -Ddeephaven.application.dir=/app.d
13+
14+
web:
15+
image: ghcr.io/deephaven/web:${VERSION:-latest}
16+
expose:
17+
- '80'
18+
volumes:
19+
- ./data:/data
20+
- web-tmp:/tmp
21+
22+
grpc-proxy:
23+
image: ghcr.io/deephaven/grpc-proxy:${VERSION:-latest}
24+
environment:
25+
- BACKEND_ADDR=grpc-api:8080
26+
depends_on:
27+
- grpc-api
28+
expose:
29+
- '8080'
30+
31+
envoy:
32+
image: ghcr.io/deephaven/envoy:${VERSION:-latest}
33+
depends_on:
34+
- web
35+
- grpc-proxy
36+
- grpc-api
37+
ports:
38+
- "10000:10000"
39+
40+
prometheus:
41+
image: prom/prometheus
42+
ports:
43+
- "9090:9090"
44+
45+
volumes:
46+
web-tmp:
47+
api-cache:

prometheus/start.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
docker build --tag prometheus-deephaven/grpc-api .
2+
docker-compose up

0 commit comments

Comments
 (0)