Repository: robinmanuelthiel/speedtest Branch: main Commit: 9b93de5350c4 Files: 7 Total size: 15.2 KB Directory structure: gitextract_905bh62o/ ├── .github/ │ └── workflows/ │ └── release.yaml ├── Dockerfile ├── LICENSE ├── README.md ├── demo/ │ ├── dashboard.json │ └── docker-compose.yaml └── speedtest.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/release.yaml ================================================ name: Release on: workflow_dispatch: inputs: version: description: "The version to release" default: "1.0.0" required: true jobs: release: name: Update Container Images runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Login to DockerHub uses: docker/login-action@v1.10.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@master with: platforms: all - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - run: | docker buildx build \ --push \ --platform linux/arm/v7,linux/arm64/v8,linux/amd64 \ --tag robinmanuelthiel/speedtest:${{ github.event.inputs.version }} \ --tag robinmanuelthiel/speedtest:latest \ . - name: Create GitHub Release uses: softprops/action-gh-release@v1 with: tag_name: v${{ github.event.inputs.version }} name: Version ${{ github.event.inputs.version }} generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: Dockerfile ================================================ FROM debian:bullseye # Install basics RUN apt-get update && apt-get install -y curl jq gnupg1 apt-transport-https dirmngr # Install speedtest cli RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash RUN apt-get install -y speedtest COPY ./speedtest.sh . CMD ["./speedtest.sh"] ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Robin-Manuel Thiel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Internet Speed Test in a Container [![Docker](https://img.shields.io/badge/Docker%20Hub-robinmanuelthiel/speedtest-blue.svg?logo=docker)](https://hub.docker.com/r/robinmanuelthiel/speedtest/) Check your internet bandwidth using the [Speedtest CLI](https://www.speedtest.net/apps/cli) from a Docker container. You can configure the tool to run periodically and save the results to an InfluxDB for visualization or long-term records. ```bash docker run --rm robinmanuelthiel/speedtest:latest ``` The result will then look like this: ```bash Running a Speed Test... Your download speed is 334 Mbps (29284399 Bytes/s). Your upload speed is 42 Mbps (4012944 Bytes/s). Your ping is 6.223 ms. Speedtest took 23 seconds." ``` ## Configuration | Environment variable | Default value | Description | |-----------------------|-------------------------|--------------------------------------------------------------------------------------| | `LOOP` | `false` | Run Speedtest in a loop | | `LOOP_DELAY` | `60` | Delay in seconds between the runs | | `DB_SAVE` | `false` | Save values to InfluxDB | | `DB_HOST` | `http://localhost:8086` | InfluxDB Hostname | | `DB_NAME` | `speedtest` | InfluxDB Database name | | `DB_USERNAME` | `admin` | InfluxDB Username | | `DB_PASSWORD` | `password` | InfluxDB Password | | `SPEEDTEST_SERVER_ID` | none | Specify a server from the server list using its ID | | `SPEEDTEST_HOSTNAME` | none | Specify a server, from the server list, using its host's fully qualified domain name | To get the available Server IDs, sponsors, and hostnames from speedtest run: ```bash curl -s https://cli.speedtest.net/api/cli/config | jq -r '.servers[] | "id: \(.id), sponsor: \(.sponsor), host: \(.host)"' ``` ## Grafana and InfluxDB ![Screenshot of a Grafana Dashboard with upload and download speed values](img/grafana.png) For a full visualization and long term tracking, I recommend InfluxDB as a time-series database and Grafana as a dashboard engine. Both come in Docker containers, so the whole setup can be achieved by starting a Docker Compose file. ```yaml version: "3" services: grafana: image: grafana/grafana:7.5.2 restart: always ports: - 3000:3000 volumes: - grafana:/var/lib/grafana depends_on: - influxdb influxdb: image: influxdb:1.8.3 restart: always volumes: - influxdb:/var/lib/influxdb ports: - 8083:8083 - 8086:8086 environment: - INFLUXDB_ADMIN_USER="admin" - INFLUXDB_ADMIN_PASSWORD="password" - INFLUXDB_DB="speedtest" speedtest: image: robinmanuelthiel/speedtest:latest restart: always environment: - LOOP=true - LOOP_DELAY=1800 - DB_SAVE=true - DB_HOST=http://influxdb:8086 - DB_NAME=speedtest - DB_USERNAME=admin - DB_PASSWORD=password privileged: true # Needed for 'sleep' in the loop depends_on: - influxdb volumes: grafana: influxdb: ``` To configure Grafana, we need to **add InfluxDB as a data source first** and then create a dashboard with the upload and download values. You can find a demo dashboard configuration in the [/demo](/demo) folder. > **Hint:** The speedtest outputs values as bytes per second. Make sure to divide all values by 125000 in your dashboard to get the Mbps values. ================================================ FILE: demo/dashboard.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": "-- Grafana --", "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "gnetId": null, "graphTooltip": 0, "id": 1, "links": [], "panels": [ { "aliasColors": {}, "bars": false, "cacheTimeout": null, "dashLength": 10, "dashes": false, "datasource": null, "fill": 1, "fillGradient": 2, "gridPos": { "h": 13, "w": 24, "x": 0, "y": 0 }, "hiddenSeries": false, "id": 2, "legend": { "alignAsTable": false, "avg": true, "current": false, "hideEmpty": false, "max": true, "min": true, "rightSide": false, "show": true, "total": false, "values": true }, "lines": true, "linewidth": 2, "links": [], "nullPointMode": "connected", "options": { "dataLinks": [] }, "percentage": false, "pluginVersion": "6.7.2", "pointradius": 2, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "alias": "download", "groupBy": [ { "params": [ "$__interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "hide": false, "measurement": "download", "orderByTime": "ASC", "policy": "default", "query": "SELECT mean(\"value\") FROM \"download\" WHERE $timeFilter GROUP BY time($__interval) fill(null)", "rawQuery": false, "refId": "A", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" }, { "params": [ " / 125000" ], "type": "math" } ] ], "tags": [] }, { "alias": "upload", "groupBy": [ { "params": [ "$__interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "measurement": "upload", "orderByTime": "ASC", "policy": "default", "refId": "B", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" }, { "params": [ "/125000" ], "type": "math" } ] ], "tags": [] } ], "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, "title": "m-Net", "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "Mbits", "label": null, "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": false } ], "yaxis": { "align": false, "alignLevel": null } } ], "refresh": "5m", "schemaVersion": 22, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { "from": "now-12h", "to": "now" }, "timepicker": { "refresh_intervals": [ "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ] }, "timezone": "", "title": "Bandwidth", "uid": "M27juGmgk", "variables": { "list": [] }, "version": 5 } ================================================ FILE: demo/docker-compose.yaml ================================================ version: "3" services: grafana: image: grafana/grafana:7.5.2 restart: always ports: - 3000:3000 volumes: - grafana:/var/lib/grafana depends_on: - influxdb influxdb: image: influxdb:1.8.3 restart: always volumes: - influxdb:/var/lib/influxdb ports: - 8083:8083 - 8086:8086 environment: - INFLUXDB_ADMIN_USER="admin" - INFLUXDB_ADMIN_PASSWORD="password" - INFLUXDB_DB="speedtest" speedtest: image: robinmanuelthiel/speedtest:latest build: context: ../ dockerfile: Dockerfile restart: always environment: - LOOP=true - LOOP_DELAY=300 - DB_SAVE=true - DB_HOST=http://influxdb:8086 - DB_NAME=speedtest - DB_USERNAME=admin - DB_PASSWORD=password privileged: true # Needed for 'sleep' in the loop depends_on: - influxdb volumes: grafana: influxdb: ================================================ FILE: speedtest.sh ================================================ #!/bin/sh # These values can be overwritten with env variables LOOP="${LOOP:-false}" LOOP_DELAY="${LOOP_DELAY:-60}" DB_SAVE="${DB_SAVE:-false}" DB_HOST="${DB_HOST:-http://localhost:8086}" DB_NAME="${DB_NAME:-speedtest}" DB_USERNAME="${DB_USERNAME:-admin}" DB_PASSWORD="${DB_PASSWORD:-password}" SPEEDTEST_HOSTNAME="${SPEEDTEST_HOSTNAME}" SPEEDTEST_SERVER_ID="${SPEEDTEST_SERVER_ID}" run_speedtest() { DATE=$(date +%s) HOSTNAME=$(hostname) START_TIME=$(date +%s) # Start speed test if [ -n "$SPEEDTEST_SERVER_ID" ]; then echo "Running a Speed Test with Server ID $SPEEDTEST_SERVER_ID... " JSON=$(speedtest --accept-license --accept-gdpr -f json -s $SPEEDTEST_SERVER_ID) || JSON="" elif [ -n "$SPEEDTEST_HOSTNAME" ]; then echo "Running a Speed Test with Hostname $SPEEDTEST_HOSTNAME... " JSON=$(speedtest --accept-license --accept-gdpr -f json -o $SPEEDTEST_HOSTNAME) || JSON="" else echo "Running a Speed Test with default host... " JSON=$(speedtest --accept-license --accept-gdpr -f json) || JSON="" fi END_TIME=$(date +%s) DURATION=$((END_TIME - START_TIME)) # If JSON is empty, then the speedtest command failed. # In this case set the values to 0. if [ -z "$JSON" ]; then DOWNLOAD=0 UPLOAD=0 PING=0 else # Fetch the values from the JSON output and convert them to the correct units (converts null to 0) DOWNLOAD="$(echo $JSON | jq -r '.download.bandwidth // 0')" UPLOAD="$(echo $JSON | jq -r '.upload.bandwidth // 0')" PING="$(echo $JSON | jq -r '.ping.latency // 0')" fi echo "Your download speed is $(($DOWNLOAD / 125000 )) Mbps ($DOWNLOAD Bytes/s)." echo "Your upload speed is $(($UPLOAD / 125000 )) Mbps ($UPLOAD Bytes/s)." echo "Your ping is $PING ms." echo "Speedtest took $DURATION seconds." # Save results in the database if $DB_SAVE; then echo "Saving values to database..." curl -s -S -XPOST "$DB_HOST/write?db=$DB_NAME&precision=s&u=$DB_USERNAME&p=$DB_PASSWORD" \ --data-binary "download,host=$HOSTNAME value=$DOWNLOAD $DATE" curl -s -S -XPOST "$DB_HOST/write?db=$DB_NAME&precision=s&u=$DB_USERNAME&p=$DB_PASSWORD" \ --data-binary "upload,host=$HOSTNAME value=$UPLOAD $DATE" curl -s -S -XPOST "$DB_HOST/write?db=$DB_NAME&precision=s&u=$DB_USERNAME&p=$DB_PASSWORD" \ --data-binary "ping,host=$HOSTNAME value=$PING $DATE" echo "Values saved." fi } # Check for input errors if [ -n "$SPEEDTEST_SERVER_ID" ] && [ -n "$SPEEDTEST_HOSTNAME" ]; then echo >&2 "[error] Only one server option can be specified, please use one of ['SPEEDTEST_SERVER_ID' or 'SPEEDTEST_HOSTNAME']" exit 1 fi if $LOOP; then echo "Running speedtest in a loop until stopped..." while : do run_speedtest echo "Running next test in ${LOOP_DELAY}s..." sleep $LOOP_DELAY done else run_speedtest fi