Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.1.0 #10

Merged
merged 19 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Speedtest-grafana container environment variables
# Speeder container environment variables
# The interval in seconds to run speedtests on
# Defaults to 5 minutes
SPEEDTEST_INTERVAL=300
SPEEDTEST_SERVER_ID=
SPEEDER_SPEEDTEST_INTERVAL=300
SPEEDER_SPEEDTEST_SERVER_ID=
# InfluxDB container environment variables
INFLUXDB_IMAGE_TAG=2.7.1
# You SHOULD change these!
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build

on:
workflow_dispatch:
push:
tags:
- 'v*'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
12 changes: 12 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Lint

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
with:
options: "--check --verbose"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
72 changes: 52 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,76 @@
# Speedtest-grafana
# Speeder

Use [Grafana](https://grafana.com/), [InfluxDB](https://www.influxdata.com/products/influxdb/) and the [librespeed/speedtest-cli](https://github.com/librespeed/speedtest-cli) to monitor your internet speed! 🚀
![Build](https://github.com/dbrennand/speeder/actions/workflows/build.yml/badge.svg)
![Lint](https://github.com/dbrennand/speeder/actions/workflows/lint.yml/badge.svg)

![Dashboard](images/dashboard.png)
Python script to monitor your internet speed! 🚀

## Prerequisites
Periodically run [librespeed/speedtest-cli](https://github.com/librespeed/speedtest-cli) and send results to [InfluxDB](https://www.influxdata.com/products/influxdb/).

1. Docker
# Quick Start

2. Docker Compose
> **Note**
> The assumption is made that you've already setup `InfluxDB 2.x.x`. Alternatively, you can use the [Docker Compose](#docker-compose-stack---influxdb-and-grafana) method where this is setup for you.

## Usage
List available server IDs:

1. Build the speedtest-grafana container image:
```bash
docker run --rm -it ghcr.io/dbrennand/speeder:latest /librespeed --list
```

```bash
docker compose build
```
Next, start and configure speeder using the environment variables:

```bash
docker run -itd --rm --name speeder \
-e "SPEEDER_SPEEDTEST_INTERVAL=300" \
-e "SPEEDER_SPEEDTEST_SERVER_ID=49" \
-e "SPEEDER_INFLUXDB_HOST=influx.example.com" \
-e "SPEEDER_INFLUXDB_TOKEN=<Token>" \
-e "SPEEDER_INFLUXDB_ORG=speeder" \
-e "SPEEDER_INFLUXDB_BUCKET=speeder" \
ghcr.io/dbrennand/speeder:latest
```

# Environment Variables

The [speeder](speeder.py) script is configured using the below environment variables:

| Name | Description | Default Value |
| ----------------------------- | ------------------------------------------------------------------------------------------ | ------------- |
| `SPEEDER_SPEEDTEST_INTERVAL` | Interval in seconds to run speedtests on. | 300 |
| `SPEEDER_SPEEDTEST_SERVER_ID` | Server ID to run speedtests against. Supports multiple IDs using a comma separated string. | "" |
| `SPEEDER_INFLUXDB_HOST` | InfluxDB hostname. | influxdb |
| `SPEEDER_INFLUXDB_PORT` | InfluxDB port. | 8086 |
| `SPEEDER_INFLUXDB_TOKEN` | InfluxDB token. | root |
| `SPEEDER_INFLUXDB_ORG` | InfluxDB organisation name. | speeder |
| `SPEEDER_INFLUXDB_BUCKET` | InfluxDB bucket name to write speedtest results to. | speeder |

# Docker Compose Stack - InfluxDB and Grafana

2. Set the `SPEEDTEST_SERVER_ID` environment variable located in the [.env](.env) file to the server ID to perform speedtests against.
The [docker-compose.yml](docker-compose.yml) file in this repository will deploy speeder, InfluxDB `2.7.1` and Grafana containers. Grafana will be provisioned with InfluxDB as the [data source](grafana-config/datasources/datasource.yml) and the pre-created [dashboard](grafana-config/dashboards/dashboard.json):

![Dashboard](images/dashboard.png)

![Dashboard 1](images/dashboard1.png)

1. Set the `SPEEDER_SPEEDTEST_SERVER_ID` environment variable located in the [.env](.env) file to the server IDs to perform speedtests against.

> **Note**
>
> If you don't know any server IDs, run the following command to list them:
> ```bash
> docker run --rm -it speedtest-grafana:1.0.0 /librespeed --list
> docker run --rm -it ghcr.io/dbrennand/speeder:latest /librespeed --list
> ```

3. Set the `DOCKER_INFLUXDB_INIT_PASSWORD`, `DOCKER_INFLUXDB_INIT_ADMIN_TOKEN` and `GF_SECURITY_ADMIN_PASSWORD` environment variables located in the [.env](.env) file.
2. Set the `DOCKER_INFLUXDB_INIT_PASSWORD`, `DOCKER_INFLUXDB_INIT_ADMIN_TOKEN` and `GF_SECURITY_ADMIN_PASSWORD` environment variables located in the [.env](.env) file.

4. Start the containers:
3. Start the compose stack:

```bash
docker compose up -d
```

5. Access Grafana at [`http://localhost:3000`](http://localhost:3000)

> **Note**
>
> Grafana will also be available from your host's IP address.
Grafana will be accessible at [`http://localhost:3000`](http://localhost:3000) and your host's IP address.

## Disclaimer

Expand All @@ -52,4 +83,5 @@ If you like this project then please give their repositories a star! ⭐
[**Daniel Brennand**](https://github.com/dbrennand) - *Author*

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) for details.
39 changes: 19 additions & 20 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,29 @@ services:
- influxdb:/var/lib/influxdb2
restart: always
networks:
- speedtest-grafana
- speeder
environment:
- "DOCKER_INFLUXDB_INIT_MODE=setup"
- "DOCKER_INFLUXDB_INIT_USERNAME=${DOCKER_INFLUXDB_INIT_USERNAME}"
- "DOCKER_INFLUXDB_INIT_PASSWORD=${DOCKER_INFLUXDB_INIT_PASSWORD}"
- "DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN}"
- "DOCKER_INFLUXDB_INIT_ORG=internet_speed"
- "DOCKER_INFLUXDB_INIT_BUCKET=internet_speed"
speedtest-grafana:
build: .
image: speedtest-grafana:1.0.0
container_name: speedtest-grafana
- "DOCKER_INFLUXDB_INIT_ORG=speeder"
- "DOCKER_INFLUXDB_INIT_BUCKET=speeder"
speeder:
image: ghcr.io/dbrennand/speeder:latest
container_name: speeder
depends_on:
- influxdb
networks:
- speedtest-grafana
- speeder
environment:
- "SPEEDTEST_INTERVAL=${SPEEDTEST_INTERVAL}"
- "SPEEDTEST_SERVER_ID=${SPEEDTEST_SERVER_ID}"
- "INFLUXDB_HOST=influxdb"
- "INFLUXDB_PORT=8086"
- "INFLUXDB_ORG=internet_speed"
- "INFLUXDB_BUCKET=internet_speed"
- "INFLUXDB_TOKEN=${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN}"
- "SPEEDER_SPEEDTEST_INTERVAL=${SPEEDER_SPEEDTEST_INTERVAL}"
- "SPEEDER_SPEEDTEST_SERVER_ID=${SPEEDER_SPEEDTEST_SERVER_ID}"
- "SPEEDER_INFLUXDB_HOST=influxdb"
- "SPEEDER_INFLUXDB_PORT=8086"
- "SPEEDER_INFLUXDB_ORG=speeder"
- "SPEEDER_INFLUXDB_BUCKET=speeder"
- "SPEEDER_INFLUXDB_TOKEN=${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN}"
grafana:
image: grafana/grafana:10.0.3
container_name: grafana
Expand All @@ -43,19 +42,19 @@ services:
- ./grafana-config/:/etc/grafana/provisioning
depends_on:
- influxdb
- speedtest-grafana
- speeder
networks:
- speedtest-grafana
- speeder
environment:
- "GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}"
- "GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}"
- "INFLUXDB_ORG=internet_speed"
- "INFLUXDB_BUCKET=internet_speed"
- "INFLUXDB_ORG=speeder"
- "INFLUXDB_BUCKET=speeder"
- "INFLUXDB_TOKEN=${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN}"

volumes:
influxdb:
grafana-storage:

networks:
speedtest-grafana:
speeder:
4 changes: 2 additions & 2 deletions dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ FROM python:3.9-alpine
COPY requirements.txt /
RUN pip install --no-cache-dir -r /requirements.txt
# Copy speedtest script
COPY speedtest.py /
COPY speeder.py /
WORKDIR /
# Copy the librespeed CLI binary to the Python alpine image to reduce size
COPY --from=build /speedtest-cli/librespeed .
# Run speedtest script
CMD ["python", "speedtest.py"]
CMD ["python", "speeder.py"]
55 changes: 40 additions & 15 deletions grafana-config/dashboards/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@
"type": "fill"
}
],
"measurement": "internet_speed",
"measurement": "speeder",
"orderByTime": "ASC",
"policy": "default",
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"internet_speed\" and\n r._field == \"upload\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"speeder\" and\n r._field == \"upload\" and\n r.server_name == \"${Server}\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"refId": "A",
"resultFormat": "time_series",
"select": [
Expand Down Expand Up @@ -198,7 +198,7 @@
"uid": "P5697886F9CA74929"
},
"hide": false,
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"internet_speed\" and\n r._field == \"download\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"speeder\" and\n r._field == \"download\" and\n r.server_name == \"${Server}\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"refId": "B"
}
],
Expand Down Expand Up @@ -330,10 +330,10 @@
"type": "fill"
}
],
"measurement": "internet_speed",
"measurement": "speeder",
"orderByTime": "ASC",
"policy": "default",
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"internet_speed\" and\n r._field == \"bytes_sent\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"speeder\" and\n r._field == \"bytes_sent\" and\n r.server_name == \"${Server}\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"refId": "A",
"resultFormat": "time_series",
"select": [
Expand Down Expand Up @@ -382,7 +382,7 @@
"uid": "P5697886F9CA74929"
},
"hide": false,
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"internet_speed\" and\n r._field == \"bytes_received\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"speeder\" and\n r._field == \"bytes_received\" and\n r.server_name == \"${Server}\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"refId": "B"
}
],
Expand Down Expand Up @@ -513,10 +513,10 @@
"type": "fill"
}
],
"measurement": "internet_speed",
"measurement": "speeder",
"orderByTime": "ASC",
"policy": "default",
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"internet_speed\" and\n r._field == \"ping\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"speeder\" and\n r._field == \"ping\" and\n r.server_name == \"${Server}\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"refId": "A",
"resultFormat": "time_series",
"select": [
Expand Down Expand Up @@ -565,7 +565,7 @@
"uid": "P5697886F9CA74929"
},
"hide": false,
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"internet_speed\" and\n r._field == \"jitter\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => \n r._measurement == \"speeder\" and\n r._field == \"jitter\" and\n r.server_name == \"${Server}\"\n )\n |> drop(columns: [\"server_name\", \"server_url\"])",
"refId": "B"
}
],
Expand Down Expand Up @@ -769,7 +769,7 @@
],
"orderByTime": "ASC",
"policy": "default",
"query": "from(bucket: \"internet_speed\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => r._measurement == \"internet_speed\")",
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => r._measurement == \"speeder\" and\n r.server_name == \"${Server}\")",
"rawQuery": true,
"refId": "A",
"resultFormat": "table",
Expand All @@ -794,14 +794,17 @@
"transformations": [
{
"id": "prepareTimeSeries",
"options": {}
"options": {
"format": "wide"
}
},
{
"id": "labelsToFields",
"options": {
"keepLabels": [
"server_name"
]
],
"mode": "columns"
}
},
{
Expand Down Expand Up @@ -840,10 +843,32 @@
"style": "dark",
"tags": [],
"templating": {
"list": []
"list": [
{
"current": {},
"datasource": {
"type": "influxdb",
"uid": "P5697886F9CA74929"
},
"definition": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => r._measurement == \"speeder\")\n |> keep(columns: [\"server_name\"])\n |> distinct(column: \"server_name\")\n |> keep(columns: [\"_value\"])",
"description": "Server Name.",
"hide": 0,
"includeAll": false,
"label": "Server",
"multi": false,
"name": "Server",
"options": [],
"query": "from(bucket: \"speeder\")\n |> range(start: ${__from:date:iso})\n |> filter(fn: (r) => r._measurement == \"speeder\")\n |> keep(columns: [\"server_name\"])\n |> distinct(column: \"server_name\")\n |> keep(columns: [\"_value\"])",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
"time": {
"from": "now-1h",
"from": "now-6h",
"to": "now"
},
"timepicker": {
Expand All @@ -864,6 +889,6 @@
"timezone": "",
"title": "Speedtest Results",
"uid": "ll6ARVfGk",
"version": 3,
"version": 2,
"weekStart": ""
}
Binary file modified images/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/dashboard1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading