From 65f151befc9df46decbc984843f3ac9a35fafa22 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 13 Oct 2020 00:18:03 -0300 Subject: [PATCH 01/16] Update docker-build.yml --- .github/workflows/docker-build.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index ca6d061..9c4e31e 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -16,17 +16,22 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v2 - + + - name: Docker Lint + uses: luke142367/Docker-Lint-Action@v1.1.1 + with: + target: ./ISP-Checker/telegraf/Dockerfile + - name: Install Docker Buildx id: buildx uses: crazy-max/ghaction-docker-buildx@v1 with: - version: latest + buildx-version: latest - name: hub.docker.com Login run: | echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin - + - name: Build the multi-arch image run: | docker buildx build \ From b3655bfda9a93ecc7239ebe6f79a364f3991b206 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 13 Oct 2020 00:24:22 -0300 Subject: [PATCH 02/16] Update docker-build.yml --- .github/workflows/docker-build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 9c4e31e..ecc9cbe 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -16,11 +16,6 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v2 - - - name: Docker Lint - uses: luke142367/Docker-Lint-Action@v1.1.1 - with: - target: ./ISP-Checker/telegraf/Dockerfile - name: Install Docker Buildx id: buildx From d70408f29fbb91b0a2eb169582a3b124f7adefb2 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 13 Oct 2020 00:26:30 -0300 Subject: [PATCH 03/16] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1828388..cc5dbb9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ISP-Checker -![License](https://img.shields.io/github/license/fmdlc/ISP-Checker) ![LastCommit](https://img.shields.io/github/last-commit/fmdlc/ISP-Checker) ![Docker](https://img.shields.io/badge/Docker-19.03.13-blue) +![License](https://img.shields.io/github/license/fmdlc/ISP-Checker) ![LastCommit](https://img.shields.io/github/last-commit/fmdlc/ISP-Checker) ![Build](https://github.com/fmdlc/ISP-Checker/workflows/Build/badge.svg) ![Docker](https://img.shields.io/badge/Docker-19.03.13-blue) > A `docker-compose` stack to run a set of ISP controls and collect metrics on a [Raspberry Pi](https://www.raspberrypi.org/). From 21800dee39cd6262329a0ebb4d8a890ab68b47b2 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Sat, 17 Oct 2020 01:00:59 -0300 Subject: [PATCH 04/16] Fixing makefile to use the official cURL Docker image to execute API initialization. --- ISP-Checker/Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ISP-Checker/Makefile b/ISP-Checker/Makefile index 7c2fc90..b01a8c1 100755 --- a/ISP-Checker/Makefile +++ b/ISP-Checker/Makefile @@ -24,17 +24,17 @@ install: @sleep 10 $(info Make: Initializing datasources and dashboards) - @curl -s --location -X POST 'http://admin:admin@localhost:3000/api/datasources' \ - --header 'Content-Type: application/json' \ - -d @./grafana/datasource.json > /dev/null + @docker run --network host -v "$(PWD)/grafana:/app" --rm curlimages/curl:7.73.0 \ + -s --location -XPOST 'http://admin:admin@127.0.0.1:3000/api/datasources' \ + --header 'Content-Type: application/json' -d @/app/datasource.json > /dev/null - @curl -s --location -X POST 'http://admin:admin@localhost:3000/api/dashboards/db' \ - --header 'Content-Type: application/json' \ - -d @./grafana/network-dashboard.json > /dev/null + @docker run --network host -v "$(PWD)/grafana:/app" --rm curlimages/curl:7.73.0 \ + -s --location -XPOST 'http://admin:admin@127.0.0.1:3000/api/dashboards/db' \ + --header 'Content-Type: application/json' -d @/app/network-dashboard.json > /dev/null - @curl -s --location -X PUT 'http://admin:admin@localhost:3000/api/user/preferences' \ - --header 'Content-Type: application/json' \ - -d @./grafana/default.json > /dev/null + @docker run --network host -v "$(PWD)/grafana:/app" --rm curlimages/curl:7.73.0 \ + -s --location -XPUT 'http://admin:admin@127.0.0.1:3000/api/user/preferences' \ + --header 'Content-Type: application/json' -d @/app/default.json > /dev/null @echo "\nOpen your browser a points to http://127.0.0.1:3000/\n" @echo "Default Username: admin | Default Password: admin" From c94d2634c160a14812b755d64639a45aa980198c Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Sat, 17 Oct 2020 01:02:06 -0300 Subject: [PATCH 05/16] Updating documentation. --- CHANGELOG.md | 4 ++++ README.md | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae0fa19..a9443fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## October 16 2020 - v1.3 +* API configuration is now executed using `cURL` [Docker image](https://hub.docker.com/r/curlimages/curl). +* `README.md` updated (Solving [https://github.com/fmdlc/ISP-Checker/issues/14](https://github.com/fmdlc/ISP-Checker/issues/14)). + ## October 12 2020 - v1.2 * Enabling CI build a multi-architecture Docker image (Extending support to `linux/amd64`, `linux/arm/v7`, `linux/arm64`) * Fixing Docker images version on Docker Compose. diff --git a/README.md b/README.md index cc5dbb9..a85d90e 100644 --- a/README.md +++ b/README.md @@ -41,11 +41,23 @@ The following platforms are supported: You can easily import this dashboard into your current Grafana installation getting it from the Official's [Grafana repository](https://grafana.com/grafana/dashboards/13140). ### Configuration - Make sure you have the [Docker-CE](https://phoenixnap.com/kb/docker-on-raspberry-pi) and [cURL](https://curl.haxx.se/) installed on your *Raspberry Pi*. If you don't, install it using your prefer method. ```bash $ curl -fsSL https://get.docker.com -o get-docker.sh | bash - ``` + +#### docker-compose +You need to have `docker-compose` installed. To install it execute: + +```bash +$: sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +``` + +Alternatively you can install it using `pip`: +```bash +$: pip install docker-compose +``` + Configure the `./ISP-Checker/credentials.env` file. There are two users: * Read-Only user: Used by Grafana to acccess to metrics From 109a903f3f13f836448ff4236ef1b6b50b3bad79 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Sat, 17 Oct 2020 01:13:39 -0300 Subject: [PATCH 06/16] Including telegraf Docker user into the video group to fetch GPU metrics --- ISP-Checker/telegraf/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ISP-Checker/telegraf/Dockerfile b/ISP-Checker/telegraf/Dockerfile index b01b227..1e96a0d 100644 --- a/ISP-Checker/telegraf/Dockerfile +++ b/ISP-Checker/telegraf/Dockerfile @@ -12,7 +12,8 @@ COPY ./bin/entrypoint.sh /entrypoint.sh RUN apt-get update && apt-get install -y --no-install-recommends \ mtr \ speedtest-cli && \ - rm -rf /var/lib/apt/lists/* + rm -rf /var/lib/apt/lists/* && \ + usermod -G video telegraf EXPOSE 8125/udp 8092/udp 8094 From 944c8afc8707da7c2878a07f31e499b568f17320 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Mon, 19 Oct 2020 00:24:41 -0300 Subject: [PATCH 07/16] Initial k8s commit --- kubernetes/configmap.yaml | 133 ++ kubernetes/deployment.yaml | 140 ++ kubernetes/namespace.yaml | 4 + kubernetes/network-dashboard.yaml | 2985 +++++++++++++++++++++++++++++ kubernetes/pvc.yaml | 14 + kubernetes/secrets.yaml | 32 + 6 files changed, 3308 insertions(+) create mode 100644 kubernetes/configmap.yaml create mode 100644 kubernetes/deployment.yaml create mode 100644 kubernetes/namespace.yaml create mode 100644 kubernetes/network-dashboard.yaml create mode 100644 kubernetes/pvc.yaml create mode 100644 kubernetes/secrets.yaml diff --git a/kubernetes/configmap.yaml b/kubernetes/configmap.yaml new file mode 100644 index 0000000..a27aa33 --- /dev/null +++ b/kubernetes/configmap.yaml @@ -0,0 +1,133 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: network-dashboard-provisioner +data: + networking.yaml: |- + apiVersion: 1 + + providers: + - name: 'ISP-Checker' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: false + options: + path: /tmp/dashboard/network-dashboard.json + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: telegraf-config +data: + telegraf.conf: |+ + [global_tags] + [agent] + interval = "10s" + round_interval = true + metric_batch_size = 1000 + metric_buffer_limit = 10000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "" + hostname = "" + omit_hostname = false + debug = false + + [[outputs.influxdb]] + urls = ["http://influxdb:8086"] + database = "$INFLUXDB_DB" + timeout = "60s" + username = "$INFLUXDB_ADMIN_USER" + password = "$INFLUXDB_ADMIN_PASSWORD" + user_agent = "telegraf" + skip_database_creation = false + + [[inputs.cpu]] + percpu = true + totalcpu = true + collect_cpu_time = false + report_active = false + + [[inputs.disk]] + ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] + + [[inputs.diskio]] + + [[inputs.kernel]] + + [[inputs.mem]] + + [[inputs.processes]] + + [[inputs.swap]] + + [[inputs.system]] + + [[inputs.dns_query]] + servers = ["4.2.2.1", "8.8.8.8"] + domains = ["www.google.com", "www.twitter.com", "www.amazon.com", "www.wikipedia.org"] + record_type = "A" + port = 53 + timeout = 3 + + [[inputs.http_response]] + urls = [ + "http://www.google.com", + "http://www.twitter.com", + "http://www.amazon.com", + "http://www.yahoo.com" + ] + response_timeout = "5s" + method = "GET" + follow_redirects = true + + [[inputs.internal]] + collect_memstats = true + + [[inputs.linux_sysctl_fs]] + + [[inputs.net]] + + [[inputs.netstat]] + + [[inputs.ping]] + urls = [ + "google.com", + "twitter.com", + "amazon.com", + "yahoo.com" + ] + method = "exec" + count = 1 + ping_interval = 1.0 + timeout = 5.0 + deadline = 10 + binary = "ping" + ipv6 = false + + [[inputs.exec]] + commands=["mtr -C google.com s3-website.ap-northeast-2.amazonaws.com s3-website.eu-central-1.amazonaws.com s3-website.af-south-1.amazonaws.com"] + timeout = "3m" + interval = "7m" + data_format = "csv" + csv_skip_rows = 1 + csv_column_names=[ "", "", "status","dest","hop","ip","loss","snt","", "","avg","best","worst","stdev"] + name_override = "mtr" + csv_tag_columns = ["dest", "hop", "ip"] + + [[inputs.exec]] + commands = ["/usr/bin/speedtest-cli --csv --bytes"] + name_override = "speedtest" + timeout = "3m" + interval = "15m" + data_format = "csv" + csv_column_names = ['Server ID','Sponsor','Server Name','Timestamp','Distance','Ping','Download','Upload','Share','IP Address'] + csv_timestamp_column = "Timestamp" + csv_timestamp_format = "2006-01-02T15:04:05Z07:00" diff --git a/kubernetes/deployment.yaml b/kubernetes/deployment.yaml new file mode 100644 index 0000000..487aa62 --- /dev/null +++ b/kubernetes/deployment.yaml @@ -0,0 +1,140 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: monitoring + name: influxdb + labels: + app: influxdb +spec: + replicas: 1 + selector: + matchLabels: + app: influxdb + template: + metadata: + labels: + app: influxdb + spec: + containers: + - name: influxdb + image: influxdb:1.8.3 + imagePullPolicy: IfNotPresent + envFrom: + - secretRef: + name: isp-checker-secrets + ports: + - containerPort: 8086 + readinessProbe: + httpGet: + path: /ping + port: 8086 + volumeMounts: + - mountPath: /var/lib/influxdb + name: var-lib-influxdb + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: var-lib-influxdb + persistentVolumeClaim: + claimName: influxdb-pvc + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: monitoring + name: grafana + labels: + app: grafana +spec: + replicas: 1 + progressDeadlineSeconds: 600 + selector: + matchLabels: + app: grafana + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: grafana + spec: + containers: + - name: grafana + image: grafana/grafana:7.2.1 + imagePullPolicy: IfNotPresent + envFrom: + - secretRef: + name: isp-checker-secrets + volumeMounts: + - name: grafana-datasource-provisioner + mountPath: /etc/grafana/provisioning/datasources/datasource.yaml + subPath: datasource.yaml + readOnly: true + - name: grafana-dashboard-provisioner + mountPath: /etc/grafana/provisioning/dashboards/networking.yaml + subPath: networking.yaml + readOnly: true + - name: grafana-dashboard-data + mountPath: /tmp/dashboard/network-dashboard.json + subPath: network-dashboard.json + ports: + - containerPort: 3000 + env: + - name: GF_INSTALL_PLUGINS + value: https://grafana.com/api/plugins/gowee-traceroutemap-panel/versions/0.2.3/download;gowee-traceroutemap-panel + readinessProbe: + httpGet: + path: / + port: 3000 + volumes: + - name: grafana-datasource-provisioner + secret: + secretName: grafana-datasource + - name: grafana-dashboard-provisioner + configMap: + name: network-dashboard-provisioner + - name: grafana-dashboard-data + configMap: + name: network-dashboard-data +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: monitoring + name: telegraf + labels: + app: telegraf +spec: + replicas: 1 + selector: + matchLabels: + app: telegraf + template: + metadata: + labels: + app: telegraf + spec: + containers: + - name: telegraf + image: tty0/isp_telegraf:1.15.3 + envFrom: + - secretRef: + name: isp-checker-secrets + imagePullPolicy: IfNotPresent + volumeMounts: + - name: telegraf-config-volume + mountPath: /etc/telegraf/telegraf.conf + subPath: telegraf.conf + readOnly: true + volumes: + - name: telegraf-config-volume + configMap: + name: telegraf-config diff --git a/kubernetes/namespace.yaml b/kubernetes/namespace.yaml new file mode 100644 index 0000000..d325236 --- /dev/null +++ b/kubernetes/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: monitoring diff --git a/kubernetes/network-dashboard.yaml b/kubernetes/network-dashboard.yaml new file mode 100644 index 0000000..16d6556 --- /dev/null +++ b/kubernetes/network-dashboard.yaml @@ -0,0 +1,2985 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: network-dashboard-data +data: + network-dashboard.json: |- + { + "__inputs": [ + { + "name": "InfluxDB", + "label": "InfluxDB", + "description": "", + "type": "datasource", + "pluginId": "influxdb", + "pluginName": "InfluxDB" + } + ], + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar gauge", + "version": "" + }, + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "panel", + "id": "gowee-traceroutemap-panel", + "name": "Traceroute Map Panel", + "version": "0.2.3" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.2.1" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "panel", + "id": "heatmap", + "name": "Heatmap", + "version": "" + }, + { + "type": "datasource", + "id": "influxdb", + "name": "InfluxDB", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Network latencies and availability", + "editable": true, + "gnetId": 13140, + "graphTooltip": 0, + "id": null, + "iteration": 1602558532070, + "links": [], + "panels": [ + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "super-light-green", + "value": 0 + }, + { + "color": "light-green", + "value": 10 + }, + { + "color": "semi-dark-green", + "value": 20 + }, + { + "color": "semi-dark-green", + "value": 30 + }, + { + "color": "super-light-orange", + "value": 40 + }, + { + "color": "light-orange", + "value": 50 + }, + { + "color": "semi-dark-orange", + "value": 60 + }, + { + "color": "light-red", + "value": 70 + }, + { + "color": "semi-dark-red", + "value": 80 + }, + { + "color": "dark-red", + "value": 90 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 26, + "options": { + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "previous" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Ping" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Speedtest (Ping)", + "type": "gauge" + }, + { + "datasource": "InfluxDB", + "description": "Download bandwidth", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "light-red", + "value": 5 + }, + { + "color": "dark-orange", + "value": 10 + }, + { + "color": "super-light-orange", + "value": 20 + }, + { + "color": "super-light-green", + "value": 30 + }, + { + "color": "light-green", + "value": 40 + }, + { + "color": "semi-dark-green", + "value": 50 + } + ] + }, + "unit": "Mbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 6, + "y": 0 + }, + "id": 20, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Download" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Bandwidth (Download)", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 2, + "gridPos": { + "h": 11, + "w": 13, + "x": 11, + "y": 0 + }, + "hiddenSeries": false, + "id": 24, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "alias": "Download", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "previous" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Download" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + " / 1000000" + ], + "type": "math" + } + ] + ], + "tags": [] + }, + { + "alias": "Upload", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "previous" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Upload" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + " / 1000000" + ], + "type": "math" + } + ] + ], + "tags": [] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Speedtest TX/RX", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:401", + "format": "Mbits", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:402", + "decimals": 2, + "format": "MBs", + "label": "Download (MB/s)", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "InfluxDB", + "description": "Uptime bandwidth", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "red", + "value": 0.5 + }, + { + "color": "dark-orange", + "value": 1 + }, + { + "color": "light-yellow", + "value": 2 + }, + { + "color": "super-light-green", + "value": 3 + }, + { + "color": "light-green", + "value": 4 + }, + { + "color": "semi-dark-green", + "value": 5 + }, + { + "color": "dark-green", + "value": 6 + } + ] + }, + "unit": "Mbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 6, + "y": 6 + }, + "id": 22, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Upload" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Bandwidth (Upload)", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "ICMP echo requests average latency.", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 2, + "gridPos": { + "h": 11, + "w": 18, + "x": 0, + "y": 11 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "$tag_url", + "groupBy": [ + { + "params": [ + "url" + ], + "type": "tag" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "average_response_ms" + ], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "url", + "operator": "=~", + "value": "/^$PingURL$/" + } + ] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "$$hashKey": "object:150", + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "ICMP average response", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:100", + "format": "short", + "label": "average (ms)", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:101", + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "InfluxDB", + "description": "Average response time for External Ping in ms.", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "max": 1000, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "super-light-green", + "value": null + }, + { + "color": "light-green", + "value": 10 + }, + { + "color": "semi-dark-green", + "value": 20 + }, + { + "color": "dark-green", + "value": 30 + }, + { + "color": "super-light-orange", + "value": 40 + }, + { + "color": "light-orange", + "value": 50 + }, + { + "color": "semi-dark-orange", + "value": 70 + }, + { + "color": "dark-orange", + "value": 80 + }, + { + "color": "light-red", + "value": 90 + }, + { + "color": "dark-red", + "value": 100 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 11 + }, + "id": 4, + "options": { + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^ping\\.average_response_ms$/", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [], + "hide": false, + "limit": "", + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT \"average_response_ms\" FROM \"ping\" ", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "average_response_ms" + ], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "url", + "operator": "=~", + "value": "/^$PingURL$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Ping AVG response time", + "type": "gauge" + }, + { + "datasource": "InfluxDB", + "description": "DNS resolution average query time", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "max": 500, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "super-light-yellow", + "value": 200 + }, + { + "color": "semi-dark-orange", + "value": 300 + }, + { + "color": "dark-red", + "value": 400 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 0, + "y": 22 + }, + "id": 6, + "options": { + "displayMode": "gradient", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "alias": "$tag_domain", + "groupBy": [ + { + "params": [ + "domain" + ], + "type": "tag" + } + ], + "measurement": "dns_query", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "query_time_ms" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "DNS AVG Query time", + "type": "bargauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "Percentage of package lost ", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "percent" + }, + "overrides": [] + }, + "fill": 3, + "fillGradient": 2, + "gridPos": { + "h": 9, + "w": 13, + "x": 11, + "y": 22 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "connected", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:405" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "percent_packet_loss" + ], + "type": "field" + }, + { + "params": [ + 95 + ], + "type": "percentile" + } + ] + ], + "tags": [] + } + ], + "thresholds": [ + { + "$$hashKey": "object:387", + "colorMode": "critical", + "fill": false, + "line": true, + "op": "gt", + "value": 25, + "yaxis": "left" + }, + { + "$$hashKey": "object:393", + "colorMode": "warning", + "fill": false, + "line": true, + "op": "gt", + "value": 2, + "yaxis": "left" + }, + { + "$$hashKey": "object:399", + "colorMode": "ok", + "fill": true, + "line": false, + "op": "lt", + "value": 2, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "ICMP Packet loss (%)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:416", + "format": "percent", + "label": "Packet lost %", + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:417", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 31 + }, + "hiddenSeries": false, + "id": 87, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "HTTP", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "measurement": "http_response", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "response_time" + ], + "type": "field" + }, + { + "params": [], + "type": "stddev" + } + ] + ], + "tags": [ + { + "key": "host", + "operator": "=~", + "value": "/^$NetHost$/" + } + ] + }, + { + "alias": "ICMP", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "standard_deviation_ms" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [ + { + "key": "host", + "operator": "=~", + "value": "/^$NetHost$/" + } + ] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Jitter (ICMP/HTTP)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2579", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2580", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": "InfluxDB", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 34, + "panels": [], + "title": "Connectivity metrics", + "type": "row" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 2, + "mappings": [], + "min": 0, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "super-light-green", + "value": null + }, + { + "color": "semi-dark-green", + "value": 150 + }, + { + "color": "dark-green", + "value": 200 + }, + { + "color": "#EAB839", + "value": 250 + }, + { + "color": "semi-dark-orange", + "value": 300 + }, + { + "color": "dark-red", + "value": 350 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 41 + }, + "id": 42, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "repeat": "PingURL", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"average_response_ms\") FROM \"ping\" WHERE (\"url\" =~ /^$PingURL$/) AND $timeFilter GROUP BY time($__interval) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "$PingURL Ping", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "HTTP GET request response time (ms)", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 47 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "$tag_server", + "groupBy": [ + { + "params": [ + "server" + ], + "type": "tag" + } + ], + "measurement": "http_response", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "response_time" + ], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "method", + "operator": "=", + "value": "GET" + } + ] + } + ], + "thresholds": [ + { + "$$hashKey": "object:1216", + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + }, + { + "$$hashKey": "object:1222", + "colorMode": "warning", + "fill": false, + "line": false, + "op": "gt", + "value": 5, + "yaxis": "left" + }, + { + "$$hashKey": "object:1228", + "colorMode": "ok", + "fill": true, + "line": true, + "op": "lt", + "value": 2, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP GET response time (ms)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": true, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1019", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1020", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 1, + "gridPos": { + "h": 18, + "w": 12, + "x": 0, + "y": 57 + }, + "hiddenSeries": false, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "connected", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "$col", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "netstat", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"tcp_close\") AS \"tcp_close\", mean(\"tcp_close_wait\") AS \"tcp_close_wait\", mean(\"tcp_closing\") AS \"tcp_colsing\", mean(\"tcp_fin_wait1\") AS \"tcp_fin_wait1\", mean(\"tcp_fin_wait2\") AS \"tcp_fin_wait2\", mean(\"tcp_last_ack\") AS \"tcp_last_ack\", mean(\"tcp_syn_recv\") AS \"tcp_syn_recv\", mean(\"tcp_syn_sent\") AS \"tcp_syn_sent\", mean(\"tcp_listen\") AS \"tcp_listen\", mean(\"tcp_time_wait\") AS \"tcp_time_wait\", mean(\"udp_socket\") AS \"udp_socket\", mean(\"tcp_established\") AS \"tcp_established\" FROM \"netstat\" WHERE (\"host\" =~ /^$NetHost$/) AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "tcp_close" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + }, + { + "params": [ + "tcp_close" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "host", + "operator": "=~", + "value": "/^$NetHost$/" + } + ] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Netstat", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2208", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2209", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 18, + "w": 12, + "x": 12, + "y": 57 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": false + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx (Bytes)", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_sent" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Rx (Bytes)", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_recv" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Drop In", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "drop_in" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Drop Out", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "drop_out" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Error In", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "E", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "err_in" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Error Out", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "F", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "err_out" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + } + ], + "thresholds": [ + { + "$$hashKey": "object:1747", + "colorMode": "warning", + "fill": true, + "line": true, + "op": "lt", + "value": 0, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Interface: $NetInterface (Tx/Rx)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "series", + "name": null, + "show": true, + "values": [ + "total" + ] + }, + "yaxes": [ + { + "$$hashKey": "object:1468", + "format": "bytes", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1469", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": true, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": 1, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateInferno", + "exponent": 0.5, + "max": null, + "min": null, + "mode": "spectrum" + }, + "dataFormat": "timeseries", + "datasource": "InfluxDB", + "description": "Standard Deviation (ms) heatmap", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 75 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 10, + "legend": { + "show": true + }, + "pluginVersion": "7.2.0", + "reverseYBuckets": false, + "targets": [ + { + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "standard_deviation_ms" + ], + "type": "field" + }, + { + "params": [], + "type": "stddev" + } + ] + ], + "tags": [ + { + "key": "url", + "operator": "=~", + "value": "/^$PingURL$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Ping Standard Deviation (ms)", + "tooltip": { + "show": true, + "showHistogram": true + }, + "tooltipDecimals": 3, + "transparent": true, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": "10s", + "yAxis": { + "decimals": null, + "format": "ms", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": { + "align": "center", + "displayMode": "color-text", + "filterable": true + }, + "mappings": [], + "max": 500, + "min": 100, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "super-light-blue", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 100 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "unit", + "value": "dateTimeAsLocal" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "server" + }, + "properties": [ + { + "id": "custom.width", + "value": 300 + }, + { + "id": "mappings", + "value": [] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "last" + }, + "properties": [ + { + "id": "custom.width", + "value": 145 + }, + { + "id": "displayName", + "value": "Response code" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "light-red", + "value": null + }, + { + "color": "light-green", + "value": 100 + }, + { + "color": "light-green", + "value": 200 + }, + { + "color": "semi-dark-green", + "value": 300 + }, + { + "color": "super-light-yellow", + "value": 400 + }, + { + "color": "super-light-red", + "value": 500 + } + ] + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Response code" + }, + "properties": [ + { + "id": "custom.width", + "value": 145 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "method" + }, + "properties": [ + { + "id": "custom.width", + "value": 100 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Endpoint" + }, + "properties": [ + { + "id": "custom.width", + "value": 514 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP Method" + }, + "properties": [ + { + "id": "custom.width", + "value": 333 + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 75 + }, + "id": 14, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.2.1", + "repeat": null, + "repeatDirection": "h", + "targets": [ + { + "alias": "$tag_server", + "groupBy": [ + { + "params": [ + "server" + ], + "type": "tag" + }, + { + "params": [ + "method" + ], + "type": "tag" + } + ], + "measurement": "http_response", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "http_response_code" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "HTTP response codes", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "indexByName": { + "Time": 0, + "last": 3, + "method": 2, + "server": 1 + }, + "renameByName": { + "last": "Response code", + "method": "HTTP Method", + "server": "Endpoint" + } + } + } + ], + "transparent": true, + "type": "table" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 81 + }, + "id": 18, + "options": { + "geoIPProviders": { + "active": "ipsb", + "custom-api": { + "kind": "custom-api" + }, + "custom-function": { + "kind": "custom-function" + }, + "ipinfo": { + "kind": "ipinfo" + }, + "ipsb": { + "kind": "ipsb" + } + }, + "hostnameLabelWidth": 8, + "longitude360": false, + "mapClusterRadius": 15, + "simplifyHostname": false + }, + "pluginVersion": "7.2.0", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "orderByTime": "ASC", + "policy": "default", + "query": "select mean(avg) as rtt, mean(loss) as loss from mtr WHERE now() - 5m < time group by hop, ip, host, dest\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "TraceRoute", + "type": "gowee-traceroutemap-panel" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 3, + "mappings": [], + "min": 0, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(238, 238, 238)", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 87 + }, + "id": 65, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_recv" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + }, + { + "params": [ + "MiB Sent" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "MiB Received ($NetInterface)", + "type": "stat" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 3, + "mappings": [], + "min": 0, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-blue", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 87 + }, + "id": 56, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_sent" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + }, + { + "params": [ + "MiB Sent" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "MiB Sent ($NetInterface)", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "InfluxDB", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 93 + }, + "id": 32, + "panels": [], + "title": "Availability metrics", + "type": "row" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 4, + "mappings": [ + { + "from": "", + "id": 0, + "text": "N/A", + "to": "", + "type": 1, + "value": "null" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "semi-dark-orange", + "value": 95 + }, + { + "color": "semi-dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 94 + }, + "id": 36, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "repeat": "PingURL", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT 100 - mean(\"percent_packet_loss\") FROM \"ping\" WHERE \"url\" =~ /^$PingURL$/ AND $timeFilter GROUP BY time(1m) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "$PingURL Ping", + "type": "stat" + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [ + "networking", + "monitoring", + "telegraf", + "influxdb", + "isp", + "raspberry" + ], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "InfluxDB", + "definition": "", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "PingURL", + "options": [], + "query": "SHOW TAG VALUES FROM \"ping\" WITH KEY = \"url\"", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "InfluxDB", + "definition": "", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "NetInterface", + "options": [], + "query": "SHOW TAG VALUES FROM \"net\" WITH KEY = \"interface\" WHERE \"host\" =~ /$NetHost/", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "InfluxDB", + "definition": "", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "NetHost", + "options": [], + "query": "SHOW TAG VALUES FROM \"net\" WITH KEY = \"host\"", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Network", + "uid": "Is1f1nRgz", + "version": 2 + } diff --git a/kubernetes/pvc.yaml b/kubernetes/pvc.yaml new file mode 100644 index 0000000..3174def --- /dev/null +++ b/kubernetes/pvc.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: monitoring + labels: + app: influxdb + name: influxdb-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi diff --git a/kubernetes/secrets.yaml b/kubernetes/secrets.yaml new file mode 100644 index 0000000..e55991b --- /dev/null +++ b/kubernetes/secrets.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: monitoring + name: isp-checker-secrets +type: Opaque +stringData: + INFLUXDB_DB: telegraf + INFLUXDB_ADMIN_USER: admin + INFLUXDB_ADMIN_PASSWORD: VerySecurePassword + INFLUXDB_READ_USER: grafana + INFLUXDB_READ_PASSWORD: VerySecurePassword + +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: monitoring + name: grafana-datasource +stringData: + datasource.yaml: |- + datasources: + - name: InfluxDB + type: influxdb + access: proxy + orgId: 1 + url: http://influxdb:8086 + user: grafana + database: telegraf + isDefault: true + editable: true From 277a604ff584598c76e16fb33f3a242d2ca8d10e Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Mon, 19 Oct 2020 00:24:59 -0300 Subject: [PATCH 08/16] Initial k8s commit --- kubernetes/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 kubernetes/README.md diff --git a/kubernetes/README.md b/kubernetes/README.md new file mode 100644 index 0000000..e69de29 From 7edff21b65fb0d255bb014d3ed734cc29659066e Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Mon, 19 Oct 2020 18:38:02 -0300 Subject: [PATCH 09/16] adding resource limits --- kubernetes/deployment.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/kubernetes/deployment.yaml b/kubernetes/deployment.yaml index 487aa62..3468989 100644 --- a/kubernetes/deployment.yaml +++ b/kubernetes/deployment.yaml @@ -32,6 +32,13 @@ spec: volumeMounts: - mountPath: /var/lib/influxdb name: var-lib-influxdb + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1024Mi" + cpu: "500m" dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler @@ -94,6 +101,13 @@ spec: httpGet: path: / port: 3000 + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1024Mi" + cpu: "500m" volumes: - name: grafana-datasource-provisioner secret: @@ -134,6 +148,13 @@ spec: mountPath: /etc/telegraf/telegraf.conf subPath: telegraf.conf readOnly: true + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1024Mi" + cpu: "500m" volumes: - name: telegraf-config-volume configMap: From ddc814e9bc1a9a4e532498fd113c7d3707871c6c Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 20 Oct 2020 00:42:38 -0300 Subject: [PATCH 10/16] Enabling Kubernetes support --- kubernetes/ISP-Checker-deploy.yaml | 3403 ++++++++++++++++++++++++++++ kubernetes/README.md | 35 + kubernetes/configmap.yaml | 19 +- kubernetes/deployment.yaml | 77 +- kubernetes/network-dashboard.yaml | 3 +- kubernetes/pvc.yaml | 17 +- kubernetes/secrets.yaml | 14 +- kubernetes/services.yaml | 20 + 8 files changed, 3550 insertions(+), 38 deletions(-) create mode 100644 kubernetes/ISP-Checker-deploy.yaml create mode 100644 kubernetes/services.yaml diff --git a/kubernetes/ISP-Checker-deploy.yaml b/kubernetes/ISP-Checker-deploy.yaml new file mode 100644 index 0000000..a60327d --- /dev/null +++ b/kubernetes/ISP-Checker-deploy.yaml @@ -0,0 +1,3403 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: monitoring +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: monitoring + labels: + app: influxdb + name: influxdb-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: monitoring + labels: + app: grafana + name: grafana-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: monitoring + name: isp-checker-secrets-rw +type: Opaque +stringData: + INFLUXDB_DB: telegraf + INFLUXDB_ADMIN_USER: admin + INFLUXDB_ADMIN_PASSWORD: VerySecurePassword + +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: monitoring + name: isp-checker-secrets-ro +type: Opaque +stringData: + INFLUXDB_DB: telegraf + INFLUXDB_READ_USER: grafana + INFLUXDB_READ_PASSWORD: VerySecurePassword + +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: monitoring + name: grafana-datasource +stringData: + datasource.yaml: |- + datasources: + - name: InfluxDB + type: influxdb + access: proxy + orgId: 1 + url: http://influxdb-svc.monitoring.svc.cluster.local:8086/ + user: grafana + database: telegraf + isDefault: true + editable: true +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: grafana-user-config +data: + default.json: |- + { + "homeDashboardId": 1, + "theme": "dark", + "timezone": "browser" + } + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: network-dashboard-provisioner +data: + networking.yaml: |- + apiVersion: 1 + providers: + - name: 'ISP-Checker' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: false + options: + path: /tmp/config/network-dashboard.json + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: telegraf-config +data: + telegraf.conf: |+ + [global_tags] + [agent] + interval = "10s" + round_interval = true + metric_batch_size = 1000 + metric_buffer_limit = 10000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "" + hostname = "" + omit_hostname = false + debug = false + + [[outputs.influxdb]] + urls = ["http://grafana-svc.monitoring.svc.cluster.local:8086/"] + database = "$INFLUXDB_DB" + timeout = "60s" + username = "$INFLUXDB_ADMIN_USER" + password = "$INFLUXDB_ADMIN_PASSWORD" + user_agent = "telegraf" + skip_database_creation = false + + [[inputs.cpu]] + percpu = true + totalcpu = true + collect_cpu_time = false + report_active = false + + [[inputs.disk]] + ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] + + [[inputs.diskio]] + + [[inputs.kernel]] + + [[inputs.mem]] + + [[inputs.processes]] + + [[inputs.swap]] + + [[inputs.system]] + + [[inputs.dns_query]] + servers = ["4.2.2.1", "8.8.8.8"] + domains = ["www.google.com", "www.twitter.com", "www.amazon.com", "www.wikipedia.org"] + record_type = "A" + port = 53 + timeout = 3 + + [[inputs.http_response]] + urls = [ + "http://www.google.com", + "http://www.twitter.com", + "http://www.amazon.com", + "http://www.yahoo.com" + ] + response_timeout = "5s" + method = "GET" + follow_redirects = true + + [[inputs.internal]] + collect_memstats = true + + [[inputs.linux_sysctl_fs]] + + [[inputs.net]] + + [[inputs.netstat]] + + [[inputs.ping]] + urls = [ + "google.com", + "twitter.com", + "amazon.com", + "yahoo.com" + ] + method = "exec" + count = 1 + ping_interval = 1.0 + timeout = 5.0 + deadline = 10 + binary = "ping" + ipv6 = false + + [[inputs.exec]] + commands=["mtr -C google.com s3-website.ap-northeast-2.amazonaws.com s3-website.eu-central-1.amazonaws.com s3-website.af-south-1.amazonaws.com"] + timeout = "3m" + interval = "7m" + data_format = "csv" + csv_skip_rows = 1 + csv_column_names=[ "", "", "status","dest","hop","ip","loss","snt","", "","avg","best","worst","stdev"] + name_override = "mtr" + csv_tag_columns = ["dest", "hop", "ip"] + + [[inputs.exec]] + commands = ["/usr/bin/speedtest-cli --csv --bytes"] + name_override = "speedtest" + timeout = "3m" + interval = "15m" + data_format = "csv" + csv_column_names = ['Server ID','Sponsor','Server Name','Timestamp','Distance','Ping','Download','Upload','Share','IP Address'] + csv_timestamp_column = "Timestamp" + csv_timestamp_format = "2006-01-02T15:04:05Z07:00" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: grafana-dashboard +data: + network-dashboard.json: |- + { + "__inputs": [ + { + "name": "InfluxDB", + "label": "InfluxDB", + "description": "", + "type": "datasource", + "pluginId": "influxdb", + "pluginName": "InfluxDB" + } + ], + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar gauge", + "version": "" + }, + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "panel", + "id": "gowee-traceroutemap-panel", + "name": "Traceroute Map Panel", + "version": "0.2.3" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.2.1" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "panel", + "id": "heatmap", + "name": "Heatmap", + "version": "" + }, + { + "type": "datasource", + "id": "influxdb", + "name": "InfluxDB", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Network latencies and availability", + "editable": true, + "gnetId": 13140, + "graphTooltip": 0, + "id": null, + "iteration": 1602558532070, + "links": [], + "panels": [ + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "super-light-green", + "value": 0 + }, + { + "color": "light-green", + "value": 10 + }, + { + "color": "semi-dark-green", + "value": 20 + }, + { + "color": "semi-dark-green", + "value": 30 + }, + { + "color": "super-light-orange", + "value": 40 + }, + { + "color": "light-orange", + "value": 50 + }, + { + "color": "semi-dark-orange", + "value": 60 + }, + { + "color": "light-red", + "value": 70 + }, + { + "color": "semi-dark-red", + "value": 80 + }, + { + "color": "dark-red", + "value": 90 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 26, + "options": { + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "previous" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Ping" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Speedtest (Ping)", + "type": "gauge" + }, + { + "datasource": "InfluxDB", + "description": "Download bandwidth", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "light-red", + "value": 5 + }, + { + "color": "dark-orange", + "value": 10 + }, + { + "color": "super-light-orange", + "value": 20 + }, + { + "color": "super-light-green", + "value": 30 + }, + { + "color": "light-green", + "value": 40 + }, + { + "color": "semi-dark-green", + "value": 50 + } + ] + }, + "unit": "Mbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 6, + "y": 0 + }, + "id": 20, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Download" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Bandwidth (Download)", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 2, + "gridPos": { + "h": 11, + "w": 13, + "x": 11, + "y": 0 + }, + "hiddenSeries": false, + "id": 24, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "alias": "Download", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "previous" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Download" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + " / 1000000" + ], + "type": "math" + } + ] + ], + "tags": [] + }, + { + "alias": "Upload", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "previous" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Upload" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + " / 1000000" + ], + "type": "math" + } + ] + ], + "tags": [] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Speedtest TX/RX", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:401", + "format": "Mbits", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:402", + "decimals": 2, + "format": "MBs", + "label": "Download (MB/s)", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "InfluxDB", + "description": "Uptime bandwidth", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "red", + "value": 0.5 + }, + { + "color": "dark-orange", + "value": 1 + }, + { + "color": "light-yellow", + "value": 2 + }, + { + "color": "super-light-green", + "value": 3 + }, + { + "color": "light-green", + "value": 4 + }, + { + "color": "semi-dark-green", + "value": 5 + }, + { + "color": "dark-green", + "value": 6 + } + ] + }, + "unit": "Mbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 6, + "y": 6 + }, + "id": 22, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "speedtest", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "Upload" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Bandwidth (Upload)", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "ICMP echo requests average latency.", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 2, + "gridPos": { + "h": 11, + "w": 18, + "x": 0, + "y": 11 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": true, + "hideZero": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "$tag_url", + "groupBy": [ + { + "params": [ + "url" + ], + "type": "tag" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "average_response_ms" + ], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "url", + "operator": "=~", + "value": "/^$PingURL$/" + } + ] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "$$hashKey": "object:150", + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "ICMP average response", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:100", + "format": "short", + "label": "average (ms)", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:101", + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "InfluxDB", + "description": "Average response time for External Ping in ms.", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "max": 1000, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "super-light-green", + "value": null + }, + { + "color": "light-green", + "value": 10 + }, + { + "color": "semi-dark-green", + "value": 20 + }, + { + "color": "dark-green", + "value": 30 + }, + { + "color": "super-light-orange", + "value": 40 + }, + { + "color": "light-orange", + "value": 50 + }, + { + "color": "semi-dark-orange", + "value": 70 + }, + { + "color": "dark-orange", + "value": 80 + }, + { + "color": "light-red", + "value": 90 + }, + { + "color": "dark-red", + "value": 100 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 11 + }, + "id": 4, + "options": { + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^ping\\.average_response_ms$/", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [], + "hide": false, + "limit": "", + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT \"average_response_ms\" FROM \"ping\" ", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "average_response_ms" + ], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "url", + "operator": "=~", + "value": "/^$PingURL$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Ping AVG response time", + "type": "gauge" + }, + { + "datasource": "InfluxDB", + "description": "DNS resolution average query time", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "max": 500, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "super-light-yellow", + "value": 200 + }, + { + "color": "semi-dark-orange", + "value": 300 + }, + { + "color": "dark-red", + "value": 400 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 0, + "y": 22 + }, + "id": 6, + "options": { + "displayMode": "gradient", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "alias": "$tag_domain", + "groupBy": [ + { + "params": [ + "domain" + ], + "type": "tag" + } + ], + "measurement": "dns_query", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "query_time_ms" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "DNS AVG Query time", + "type": "bargauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "Percentage of package lost ", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "percent" + }, + "overrides": [] + }, + "fill": 3, + "fillGradient": 2, + "gridPos": { + "h": 9, + "w": 13, + "x": 11, + "y": 22 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "connected", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:405" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "percent_packet_loss" + ], + "type": "field" + }, + { + "params": [ + 95 + ], + "type": "percentile" + } + ] + ], + "tags": [] + } + ], + "thresholds": [ + { + "$$hashKey": "object:387", + "colorMode": "critical", + "fill": false, + "line": true, + "op": "gt", + "value": 25, + "yaxis": "left" + }, + { + "$$hashKey": "object:393", + "colorMode": "warning", + "fill": false, + "line": true, + "op": "gt", + "value": 2, + "yaxis": "left" + }, + { + "$$hashKey": "object:399", + "colorMode": "ok", + "fill": true, + "line": false, + "op": "lt", + "value": 2, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "ICMP Packet loss (%)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:416", + "format": "percent", + "label": "Packet lost %", + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:417", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 31 + }, + "hiddenSeries": false, + "id": 87, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "HTTP", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "measurement": "http_response", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "response_time" + ], + "type": "field" + }, + { + "params": [], + "type": "stddev" + } + ] + ], + "tags": [ + { + "key": "host", + "operator": "=~", + "value": "/^$NetHost$/" + } + ] + }, + { + "alias": "ICMP", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "standard_deviation_ms" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [ + { + "key": "host", + "operator": "=~", + "value": "/^$NetHost$/" + } + ] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Jitter (ICMP/HTTP)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2579", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2580", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": "InfluxDB", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 34, + "panels": [], + "title": "Connectivity metrics", + "type": "row" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 2, + "mappings": [], + "min": 0, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "super-light-green", + "value": null + }, + { + "color": "semi-dark-green", + "value": 150 + }, + { + "color": "dark-green", + "value": 200 + }, + { + "color": "#EAB839", + "value": 250 + }, + { + "color": "semi-dark-orange", + "value": 300 + }, + { + "color": "dark-red", + "value": 350 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 41 + }, + "id": 42, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "repeat": "PingURL", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"average_response_ms\") FROM \"ping\" WHERE (\"url\" =~ /^$PingURL$/) AND $timeFilter GROUP BY time($__interval) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "$PingURL Ping", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "description": "HTTP GET request response time (ms)", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 47 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "$tag_server", + "groupBy": [ + { + "params": [ + "server" + ], + "type": "tag" + } + ], + "measurement": "http_response", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "response_time" + ], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "method", + "operator": "=", + "value": "GET" + } + ] + } + ], + "thresholds": [ + { + "$$hashKey": "object:1216", + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + }, + { + "$$hashKey": "object:1222", + "colorMode": "warning", + "fill": false, + "line": false, + "op": "gt", + "value": 5, + "yaxis": "left" + }, + { + "$$hashKey": "object:1228", + "colorMode": "ok", + "fill": true, + "line": true, + "op": "lt", + "value": 2, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP GET response time (ms)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": true, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1019", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1020", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 1, + "gridPos": { + "h": 18, + "w": 12, + "x": 0, + "y": 57 + }, + "hiddenSeries": false, + "id": 85, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "connected", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "$col", + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "netstat", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"tcp_close\") AS \"tcp_close\", mean(\"tcp_close_wait\") AS \"tcp_close_wait\", mean(\"tcp_closing\") AS \"tcp_colsing\", mean(\"tcp_fin_wait1\") AS \"tcp_fin_wait1\", mean(\"tcp_fin_wait2\") AS \"tcp_fin_wait2\", mean(\"tcp_last_ack\") AS \"tcp_last_ack\", mean(\"tcp_syn_recv\") AS \"tcp_syn_recv\", mean(\"tcp_syn_sent\") AS \"tcp_syn_sent\", mean(\"tcp_listen\") AS \"tcp_listen\", mean(\"tcp_time_wait\") AS \"tcp_time_wait\", mean(\"udp_socket\") AS \"udp_socket\", mean(\"tcp_established\") AS \"tcp_established\" FROM \"netstat\" WHERE (\"host\" =~ /^$NetHost$/) AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "tcp_close" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + }, + { + "params": [ + "tcp_close" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "host", + "operator": "=~", + "value": "/^$NetHost$/" + } + ] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Netstat", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2208", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2209", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 18, + "w": 12, + "x": 12, + "y": 57 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": false + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx (Bytes)", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_sent" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Rx (Bytes)", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_recv" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Drop In", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "drop_in" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Drop Out", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "drop_out" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Error In", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "E", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "err_in" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + }, + { + "alias": "Error Out", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "F", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "err_out" + ], + "type": "field" + }, + { + "params": [], + "type": "integral" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + } + ], + "thresholds": [ + { + "$$hashKey": "object:1747", + "colorMode": "warning", + "fill": true, + "line": true, + "op": "lt", + "value": 0, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Interface: $NetInterface (Tx/Rx)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "series", + "name": null, + "show": true, + "values": [ + "total" + ] + }, + "yaxes": [ + { + "$$hashKey": "object:1468", + "format": "bytes", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1469", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": true, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": 1, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateInferno", + "exponent": 0.5, + "max": null, + "min": null, + "mode": "spectrum" + }, + "dataFormat": "timeseries", + "datasource": "InfluxDB", + "description": "Standard Deviation (ms) heatmap", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 75 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 10, + "legend": { + "show": true + }, + "pluginVersion": "7.2.0", + "reverseYBuckets": false, + "targets": [ + { + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + } + ], + "measurement": "ping", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "standard_deviation_ms" + ], + "type": "field" + }, + { + "params": [], + "type": "stddev" + } + ] + ], + "tags": [ + { + "key": "url", + "operator": "=~", + "value": "/^$PingURL$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Ping Standard Deviation (ms)", + "tooltip": { + "show": true, + "showHistogram": true + }, + "tooltipDecimals": 3, + "transparent": true, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": "10s", + "yAxis": { + "decimals": null, + "format": "ms", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": { + "align": "center", + "displayMode": "color-text", + "filterable": true + }, + "mappings": [], + "max": 500, + "min": 100, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "super-light-blue", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 100 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "unit", + "value": "dateTimeAsLocal" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "server" + }, + "properties": [ + { + "id": "custom.width", + "value": 300 + }, + { + "id": "mappings", + "value": [] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "last" + }, + "properties": [ + { + "id": "custom.width", + "value": 145 + }, + { + "id": "displayName", + "value": "Response code" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "light-red", + "value": null + }, + { + "color": "light-green", + "value": 100 + }, + { + "color": "light-green", + "value": 200 + }, + { + "color": "semi-dark-green", + "value": 300 + }, + { + "color": "super-light-yellow", + "value": 400 + }, + { + "color": "super-light-red", + "value": 500 + } + ] + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Response code" + }, + "properties": [ + { + "id": "custom.width", + "value": 145 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "method" + }, + "properties": [ + { + "id": "custom.width", + "value": 100 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Endpoint" + }, + "properties": [ + { + "id": "custom.width", + "value": 514 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP Method" + }, + "properties": [ + { + "id": "custom.width", + "value": 333 + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 75 + }, + "id": 14, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.2.1", + "repeat": null, + "repeatDirection": "h", + "targets": [ + { + "alias": "$tag_server", + "groupBy": [ + { + "params": [ + "server" + ], + "type": "tag" + }, + { + "params": [ + "method" + ], + "type": "tag" + } + ], + "measurement": "http_response", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "http_response_code" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "HTTP response codes", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "indexByName": { + "Time": 0, + "last": 3, + "method": 2, + "server": 1 + }, + "renameByName": { + "last": "Response code", + "method": "HTTP Method", + "server": "Endpoint" + } + } + } + ], + "transparent": true, + "type": "table" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 81 + }, + "id": 18, + "options": { + "geoIPProviders": { + "active": "ipsb", + "custom-api": { + "kind": "custom-api" + }, + "custom-function": { + "kind": "custom-function" + }, + "ipinfo": { + "kind": "ipinfo" + }, + "ipsb": { + "kind": "ipsb" + } + }, + "hostnameLabelWidth": 8, + "longitude360": false, + "mapClusterRadius": 15, + "simplifyHostname": false + }, + "pluginVersion": "7.2.0", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "orderByTime": "ASC", + "policy": "default", + "query": "select mean(avg) as rtt, mean(loss) as loss from mtr WHERE now() - 5m < time group by hop, ip, host, dest\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "TraceRoute", + "type": "gowee-traceroutemap-panel" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 3, + "mappings": [], + "min": 0, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(238, 238, 238)", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 87 + }, + "id": 65, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_recv" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + }, + { + "params": [ + "MiB Sent" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "MiB Received ($NetInterface)", + "type": "stat" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 3, + "mappings": [], + "min": 0, + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-blue", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 87 + }, + "id": 56, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "5m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "net", + "orderByTime": "ASC", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "bytes_sent" + ], + "type": "field" + }, + { + "params": [], + "type": "last" + }, + { + "params": [ + "/1048576" + ], + "type": "math" + }, + { + "params": [ + "MiB Sent" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "interface", + "operator": "=~", + "value": "/^$NetInterface$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "MiB Sent ($NetInterface)", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "InfluxDB", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 93 + }, + "id": 32, + "panels": [], + "title": "Availability metrics", + "type": "row" + }, + { + "datasource": "InfluxDB", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 4, + "mappings": [ + { + "from": "", + "id": 0, + "text": "N/A", + "to": "", + "type": 1, + "value": "null" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "semi-dark-orange", + "value": 95 + }, + { + "color": "semi-dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 94 + }, + "id": 36, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "repeat": "PingURL", + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT 100 - mean(\"percent_packet_loss\") FROM \"ping\" WHERE \"url\" =~ /^$PingURL$/ AND $timeFilter GROUP BY time(1m) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "$PingURL Ping", + "type": "stat" + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [ + "networking", + "monitoring", + "telegraf", + "influxdb", + "isp", + "raspberry" + ], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "InfluxDB", + "definition": "", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "PingURL", + "options": [], + "query": "SHOW TAG VALUES FROM \"ping\" WITH KEY = \"url\"", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "InfluxDB", + "definition": "", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "NetInterface", + "options": [], + "query": "SHOW TAG VALUES FROM \"net\" WITH KEY = \"interface\" WHERE \"host\" =~ /$NetHost/", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "InfluxDB", + "definition": "", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "NetHost", + "options": [], + "query": "SHOW TAG VALUES FROM \"net\" WITH KEY = \"host\"", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Network", + "uid": "Is1f1nRgz", + "version": 2 + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: monitoring + name: influxdb + labels: + app: influxdb +spec: + replicas: 1 + selector: + matchLabels: + app: influxdb + template: + metadata: + labels: + app: influxdb + spec: + containers: + - name: influxdb + image: influxdb:1.8.3 + imagePullPolicy: IfNotPresent + envFrom: + - secretRef: + name: isp-checker-secrets-rw + ports: + - containerPort: 8086 + readinessProbe: + httpGet: + path: /ping + port: 8086 + initialDelaySeconds: 30 + periodSeconds: 30 + livenessProbe: + httpGet: + path: /ping + port: 8086 + initialDelaySeconds: 30 + periodSeconds: 30 + volumeMounts: + - mountPath: /var/lib/influxdb + name: var-lib-influxdb + resources: + requests: + memory: "200Mi" + cpu: "0.2" + limits: + memory: "300Mi" + cpu: "0.5" + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: var-lib-influxdb + persistentVolumeClaim: + claimName: influxdb-pvc + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: monitoring + name: grafana + labels: + app: grafana +spec: + replicas: 1 + progressDeadlineSeconds: 600 + selector: + matchLabels: + app: grafana + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: grafana + spec: + serviceAccountName: grafana-sidecar + containers: + - name: grafana + image: grafana/grafana:7.2.1 + imagePullPolicy: IfNotPresent + envFrom: + - secretRef: + name: isp-checker-secrets-ro + volumeMounts: + - name: grafana-datasource-provisioner + mountPath: /etc/grafana/provisioning/datasources/ + - name: grafana-dashboard-provisioner + mountPath: /etc/grafana/provisioning/dashboards/ + - name: grafana-dashboard + mountPath: /tmp/config/ + ports: + - containerPort: 3000 + env: + - name: GF_INSTALL_PLUGINS + value: https://grafana.com/api/plugins/gowee-traceroutemap-panel/versions/0.2.3/download;gowee-traceroutemap-panel + readinessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 30 + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 30 + resources: + requests: + memory: "200Mi" + cpu: "0.25" + limits: + memory: "500Mi" + cpu: "0.4" + volumes: + - name: grafana-dashboard-provisioner + configMap: + name: network-dashboard-provisioner + - name: grafana-dashboard + configMap: + name: grafana-dashboard + - name: grafana-datasource-provisioner + secret: + secretName: grafana-datasource +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: monitoring + name: telegraf + labels: + app: telegraf +spec: + replicas: 1 + selector: + matchLabels: + app: telegraf + template: + metadata: + labels: + app: telegraf + spec: + containers: + - name: telegraf + image: tty0/isp_telegraf:1.15.3 + envFrom: + - secretRef: + name: isp-checker-secrets-rw + imagePullPolicy: IfNotPresent + volumeMounts: + - name: telegraf-config-volume + mountPath: /etc/telegraf/ + readOnly: true + resources: + requests: + memory: "100Mi" + cpu: "0.1" + limits: + memory: "200Mi" + cpu: "0.3" + initContainers: + - name: wait-for-influxdb + image: curlimages/curl:7.72.0 + command: ['sh', '-c', "until curl -XGET http://influxdb-svc.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local:8086/; do echo Waiting\ for\ InfluxDB; sleep 2; done"] + volumes: + - name: telegraf-config-volume + configMap: + name: telegraf-config +--- +apiversion: v1 +kind: service +metadata: + labels: + app: influxdb + name: influxdb-svc + namespace: monitoring +spec: + ports: + - port: 8086 + protocol: tcp + targetport: 8086 + selector: + app: influxdb + sessionaffinity: none + type: clusterip +status: + loadbalancer: {} + diff --git a/kubernetes/README.md b/kubernetes/README.md index e69de29..c448809 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -0,0 +1,35 @@ +## Kubernetes +The following configuration files are used to deploy this stack in Kubernetes. +You need to update them before running in your cluster. + +## Installing, the quick way: + +1) Apply the `ISP-Checker-deploy.yaml` +```bash +$: kubectl apply -f ISP-Checker-deploy.yaml +``` + +### Configuration + +1) Edit `secrets.yaml` in order to initialize your InfluxDB database. +2) Edit `configmap.yaml` to configure Telegraf. +3) Apply the other `YAML` files. +4) Expose your deployment or create a LoadBalancer/IngressRule to access Grafana. + +### Accesing +#### LoadBalancer +If you want to use a `LoadBalancer` to access Grafana, run: +```bash +$: kubectl expose deployments/grafana --type=LoadBalancer --name=grafana-svc +``` +#### port-foward +If you can't use a LoadBalancer, you can use a `ClusterIP` service and forward to your local port. +```` +$: kubectl expose deployments/grafana --type=ClusterIP --name=grafana-svc +``` +And finally use your IngressController to access the service or a `port-forward`: +``` +$: kubectl port-forward svc/grafana-svc 3000:3000 -n monitoring +``` + +> Kubernetes deployment is in *BETA* version. Don't worry if you see something wrong here. diff --git a/kubernetes/configmap.yaml b/kubernetes/configmap.yaml index a27aa33..d37578e 100644 --- a/kubernetes/configmap.yaml +++ b/kubernetes/configmap.yaml @@ -1,3 +1,17 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: monitoring + name: grafana-user-config +data: + default.json: |- + { + "homeDashboardId": 1, + "theme": "dark", + "timezone": "browser" + } + --- apiVersion: v1 kind: ConfigMap @@ -7,7 +21,6 @@ metadata: data: networking.yaml: |- apiVersion: 1 - providers: - name: 'ISP-Checker' orgId: 1 @@ -16,7 +29,7 @@ data: disableDeletion: true editable: false options: - path: /tmp/dashboard/network-dashboard.json + path: /tmp/config/network-dashboard.json --- apiVersion: v1 @@ -41,7 +54,7 @@ data: debug = false [[outputs.influxdb]] - urls = ["http://influxdb:8086"] + urls = ["http://grafana-svc.monitoring.svc.cluster.local:8086/"] database = "$INFLUXDB_DB" timeout = "60s" username = "$INFLUXDB_ADMIN_USER" diff --git a/kubernetes/deployment.yaml b/kubernetes/deployment.yaml index 3468989..a8b5fc8 100644 --- a/kubernetes/deployment.yaml +++ b/kubernetes/deployment.yaml @@ -22,23 +22,31 @@ spec: imagePullPolicy: IfNotPresent envFrom: - secretRef: - name: isp-checker-secrets + name: isp-checker-secrets-rw ports: - containerPort: 8086 readinessProbe: httpGet: path: /ping port: 8086 + initialDelaySeconds: 30 + periodSeconds: 30 + livenessProbe: + httpGet: + path: /ping + port: 8086 + initialDelaySeconds: 30 + periodSeconds: 30 volumeMounts: - mountPath: /var/lib/influxdb name: var-lib-influxdb resources: requests: - memory: "512Mi" - cpu: "250m" + memory: "200Mi" + cpu: "0.2" limits: - memory: "1024Mi" - cpu: "500m" + memory: "300Mi" + cpu: "0.5" dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler @@ -73,25 +81,21 @@ spec: labels: app: grafana spec: + serviceAccountName: grafana-sidecar containers: - name: grafana image: grafana/grafana:7.2.1 imagePullPolicy: IfNotPresent envFrom: - secretRef: - name: isp-checker-secrets + name: isp-checker-secrets-ro volumeMounts: - name: grafana-datasource-provisioner - mountPath: /etc/grafana/provisioning/datasources/datasource.yaml - subPath: datasource.yaml - readOnly: true + mountPath: /etc/grafana/provisioning/datasources/ - name: grafana-dashboard-provisioner - mountPath: /etc/grafana/provisioning/dashboards/networking.yaml - subPath: networking.yaml - readOnly: true - - name: grafana-dashboard-data - mountPath: /tmp/dashboard/network-dashboard.json - subPath: network-dashboard.json + mountPath: /etc/grafana/provisioning/dashboards/ + - name: grafana-dashboard + mountPath: /tmp/config/ ports: - containerPort: 3000 env: @@ -101,23 +105,31 @@ spec: httpGet: path: / port: 3000 + initialDelaySeconds: 30 + periodSeconds: 30 + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 30 resources: requests: - memory: "512Mi" - cpu: "250m" + memory: "200Mi" + cpu: "0.25" limits: - memory: "1024Mi" - cpu: "500m" + memory: "500Mi" + cpu: "0.4" volumes: - - name: grafana-datasource-provisioner - secret: - secretName: grafana-datasource - name: grafana-dashboard-provisioner configMap: name: network-dashboard-provisioner - - name: grafana-dashboard-data + - name: grafana-dashboard configMap: - name: network-dashboard-data + name: grafana-dashboard + - name: grafana-datasource-provisioner + secret: + secretName: grafana-datasource --- apiVersion: apps/v1 kind: Deployment @@ -141,20 +153,23 @@ spec: image: tty0/isp_telegraf:1.15.3 envFrom: - secretRef: - name: isp-checker-secrets + name: isp-checker-secrets-rw imagePullPolicy: IfNotPresent volumeMounts: - name: telegraf-config-volume - mountPath: /etc/telegraf/telegraf.conf - subPath: telegraf.conf + mountPath: /etc/telegraf/ readOnly: true resources: requests: - memory: "512Mi" - cpu: "250m" + memory: "100Mi" + cpu: "0.1" limits: - memory: "1024Mi" - cpu: "500m" + memory: "200Mi" + cpu: "0.3" + initContainers: + - name: wait-for-influxdb + image: curlimages/curl:7.72.0 + command: ['sh', '-c', "until curl -XGET http://influxdb-svc.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local:8086/; do echo Waiting\ for\ InfluxDB; sleep 2; done"] volumes: - name: telegraf-config-volume configMap: diff --git a/kubernetes/network-dashboard.yaml b/kubernetes/network-dashboard.yaml index 16d6556..3865645 100644 --- a/kubernetes/network-dashboard.yaml +++ b/kubernetes/network-dashboard.yaml @@ -1,8 +1,9 @@ +--- apiVersion: v1 kind: ConfigMap metadata: namespace: monitoring - name: network-dashboard-data + name: grafana-dashboard data: network-dashboard.json: |- { diff --git a/kubernetes/pvc.yaml b/kubernetes/pvc.yaml index 3174def..7ebec98 100644 --- a/kubernetes/pvc.yaml +++ b/kubernetes/pvc.yaml @@ -11,4 +11,19 @@ spec: - ReadWriteOnce resources: requests: - storage: 10Gi + storage: 5Gi + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: monitoring + labels: + app: grafana + name: grafana-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi diff --git a/kubernetes/secrets.yaml b/kubernetes/secrets.yaml index e55991b..a6df743 100644 --- a/kubernetes/secrets.yaml +++ b/kubernetes/secrets.yaml @@ -3,12 +3,22 @@ apiVersion: v1 kind: Secret metadata: namespace: monitoring - name: isp-checker-secrets + name: isp-checker-secrets-rw type: Opaque stringData: INFLUXDB_DB: telegraf INFLUXDB_ADMIN_USER: admin INFLUXDB_ADMIN_PASSWORD: VerySecurePassword + +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: monitoring + name: isp-checker-secrets-ro +type: Opaque +stringData: + INFLUXDB_DB: telegraf INFLUXDB_READ_USER: grafana INFLUXDB_READ_PASSWORD: VerySecurePassword @@ -25,7 +35,7 @@ stringData: type: influxdb access: proxy orgId: 1 - url: http://influxdb:8086 + url: http://influxdb-svc.monitoring.svc.cluster.local:8086/ user: grafana database: telegraf isDefault: true diff --git a/kubernetes/services.yaml b/kubernetes/services.yaml new file mode 100644 index 0000000..7fe4ecd --- /dev/null +++ b/kubernetes/services.yaml @@ -0,0 +1,20 @@ +--- +apiversion: v1 +kind: service +metadata: + labels: + app: influxdb + name: influxdb-svc + namespace: monitoring +spec: + ports: + - port: 8086 + protocol: tcp + targetport: 8086 + selector: + app: influxdb + sessionaffinity: none + type: clusterip +status: + loadbalancer: {} + From c7b8006f532e2d0691c46ff1a496f1de57964ef3 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 20 Oct 2020 00:44:46 -0300 Subject: [PATCH 11/16] Update README.md --- kubernetes/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kubernetes/README.md b/kubernetes/README.md index c448809..0bdf53e 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -2,6 +2,8 @@ The following configuration files are used to deploy this stack in Kubernetes. You need to update them before running in your cluster. +> ***NOTE***: The Kubernetes deployment is in *BETA* version. Don't worry if you see something wrong here. + ## Installing, the quick way: 1) Apply the `ISP-Checker-deploy.yaml` @@ -24,12 +26,13 @@ $: kubectl expose deployments/grafana --type=LoadBalancer --name=grafana-svc ``` #### port-foward If you can't use a LoadBalancer, you can use a `ClusterIP` service and forward to your local port. -```` + +``` $: kubectl expose deployments/grafana --type=ClusterIP --name=grafana-svc ``` + And finally use your IngressController to access the service or a `port-forward`: + ``` $: kubectl port-forward svc/grafana-svc 3000:3000 -n monitoring ``` - -> Kubernetes deployment is in *BETA* version. Don't worry if you see something wrong here. From 3e76cc137efd76df6e49b76eaf82af35c46fc5c4 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 20 Oct 2020 01:16:33 -0300 Subject: [PATCH 12/16] Beta v0.4 --- CHANGELOG.md | 4 ++ img/cluster.jpeg | Bin 0 -> 122447 bytes kubernetes/ISP-Checker-deploy.yaml | 58 +++++++++++------------------ kubernetes/README.md | 10 +++-- kubernetes/configmap.yaml | 2 +- kubernetes/deployment.yaml | 3 +- kubernetes/pvc.yaml | 14 ------- kubernetes/services.yaml | 15 ++++---- 8 files changed, 41 insertions(+), 65 deletions(-) create mode 100644 img/cluster.jpeg diff --git a/CHANGELOG.md b/CHANGELOG.md index a9443fa..e333b65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## October 20 2020 - v1.4 (Beta) +* Created initial Kubernetes YAML files. +* Updated documentation. + ## October 16 2020 - v1.3 * API configuration is now executed using `cURL` [Docker image](https://hub.docker.com/r/curlimages/curl). * `README.md` updated (Solving [https://github.com/fmdlc/ISP-Checker/issues/14](https://github.com/fmdlc/ISP-Checker/issues/14)). diff --git a/img/cluster.jpeg b/img/cluster.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..602b0241434e66e81b130c09b0b2471af34d3173 GIT binary patch literal 122447 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 literal 0 HcmV?d00001 diff --git a/kubernetes/ISP-Checker-deploy.yaml b/kubernetes/ISP-Checker-deploy.yaml index a60327d..473f45d 100644 --- a/kubernetes/ISP-Checker-deploy.yaml +++ b/kubernetes/ISP-Checker-deploy.yaml @@ -19,20 +19,6 @@ spec: --- apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - namespace: monitoring - labels: - app: grafana - name: grafana-pvc -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi ---- -apiVersion: v1 kind: Secret metadata: namespace: monitoring @@ -129,7 +115,7 @@ data: debug = false [[outputs.influxdb]] - urls = ["http://grafana-svc.monitoring.svc.cluster.local:8086/"] + urls = ["http://influxdb-svc.monitoring.svc.cluster.local:8086/"] database = "$INFLUXDB_DB" timeout = "60s" username = "$INFLUXDB_ADMIN_USER" @@ -3206,6 +3192,25 @@ data: "version": 2 } --- +apiVersion: v1 +kind: Service +metadata: + labels: + app: influxdb + name: influxdb-svc + namespace: monitoring +spec: + clusterIP: 10.43.37.100 + ports: + - port: 8086 + protocol: TCP + targetPort: 8086 + selector: + app: influxdb + sessionAffinity: None + type: ClusterIP + +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -3288,7 +3293,6 @@ spec: labels: app: grafana spec: - serviceAccountName: grafana-sidecar containers: - name: grafana image: grafana/grafana:7.2.1 @@ -3302,7 +3306,7 @@ spec: - name: grafana-dashboard-provisioner mountPath: /etc/grafana/provisioning/dashboards/ - name: grafana-dashboard - mountPath: /tmp/config/ + mountPath: /tmp/config/network-dashboard.json ports: - containerPort: 3000 env: @@ -3381,23 +3385,3 @@ spec: - name: telegraf-config-volume configMap: name: telegraf-config ---- -apiversion: v1 -kind: service -metadata: - labels: - app: influxdb - name: influxdb-svc - namespace: monitoring -spec: - ports: - - port: 8086 - protocol: tcp - targetport: 8086 - selector: - app: influxdb - sessionaffinity: none - type: clusterip -status: - loadbalancer: {} - diff --git a/kubernetes/README.md b/kubernetes/README.md index c448809..5afee58 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -1,6 +1,10 @@ -## Kubernetes -The following configuration files are used to deploy this stack in Kubernetes. -You need to update them before running in your cluster. +# ISP-Checker on Kubernetes +`ISP-Checker` was ported to run in Kubernetes (`1.18.0`) in a Raspberry Pi cluster (_It's all I have_). +![RB-Cluster](../img/cluster.jpeg) + +The following configuration files are used to deploy this stack in Kubernetes, they can widely improved but so far let's say it works. + +> You need to update them before running in your cluster. ## Installing, the quick way: diff --git a/kubernetes/configmap.yaml b/kubernetes/configmap.yaml index d37578e..63f0d20 100644 --- a/kubernetes/configmap.yaml +++ b/kubernetes/configmap.yaml @@ -54,7 +54,7 @@ data: debug = false [[outputs.influxdb]] - urls = ["http://grafana-svc.monitoring.svc.cluster.local:8086/"] + urls = ["http://influxdb-svc.monitoring.svc.cluster.local:8086/"] database = "$INFLUXDB_DB" timeout = "60s" username = "$INFLUXDB_ADMIN_USER" diff --git a/kubernetes/deployment.yaml b/kubernetes/deployment.yaml index a8b5fc8..79eefbe 100644 --- a/kubernetes/deployment.yaml +++ b/kubernetes/deployment.yaml @@ -81,7 +81,6 @@ spec: labels: app: grafana spec: - serviceAccountName: grafana-sidecar containers: - name: grafana image: grafana/grafana:7.2.1 @@ -95,7 +94,7 @@ spec: - name: grafana-dashboard-provisioner mountPath: /etc/grafana/provisioning/dashboards/ - name: grafana-dashboard - mountPath: /tmp/config/ + mountPath: /tmp/config/network-dashboard.json ports: - containerPort: 3000 env: diff --git a/kubernetes/pvc.yaml b/kubernetes/pvc.yaml index 7ebec98..531882f 100644 --- a/kubernetes/pvc.yaml +++ b/kubernetes/pvc.yaml @@ -13,17 +13,3 @@ spec: requests: storage: 5Gi ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - namespace: monitoring - labels: - app: grafana - name: grafana-pvc -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi diff --git a/kubernetes/services.yaml b/kubernetes/services.yaml index 7fe4ecd..82d349e 100644 --- a/kubernetes/services.yaml +++ b/kubernetes/services.yaml @@ -1,20 +1,19 @@ --- -apiversion: v1 -kind: service +apiVersion: v1 +kind: Service metadata: labels: app: influxdb name: influxdb-svc namespace: monitoring spec: + clusterIP: 10.43.37.100 ports: - port: 8086 - protocol: tcp - targetport: 8086 + protocol: TCP + targetPort: 8086 selector: app: influxdb - sessionaffinity: none - type: clusterip -status: - loadbalancer: {} + sessionAffinity: None + type: ClusterIP From bc8839cd2fd3422e96d5a93fcdc88d119fadb3d7 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 20 Oct 2020 01:23:34 -0300 Subject: [PATCH 13/16] Update README.md --- kubernetes/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kubernetes/README.md b/kubernetes/README.md index ddc4472..f4faa76 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -1,6 +1,9 @@ # ISP-Checker on Kubernetes `ISP-Checker` was ported to run in Kubernetes (`1.18.0`) in a Raspberry Pi cluster (_It's all I have_). -![RB-Cluster](../img/cluster.jpeg) + +

+ +
The following configuration files are used to deploy this stack in Kubernetes, they can widely improved but so far let's say it works. From 94570c58574a6d8f027e2e1251ce8b4b11fbcb15 Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 20 Oct 2020 01:24:35 -0300 Subject: [PATCH 14/16] Update README.md --- kubernetes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes/README.md b/kubernetes/README.md index f4faa76..68689da 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -15,7 +15,7 @@ The following configuration files are used to deploy this stack in Kubernetes, t 1) Apply the `ISP-Checker-deploy.yaml` ```bash -$: kubectl apply -f ISP-Checker-deploy.yaml +$: kubectl apply -f https://raw.githubusercontent.com/fmdlc/ISP-Checker/master/kubernetes/ISP-Checker-deploy.yaml ``` ### Configuration From 1fcc56e4ce6ef15dd2a20af32529bb700518fa3d Mon Sep 17 00:00:00 2001 From: Facu de la Cruz Date: Tue, 20 Oct 2020 01:29:13 -0300 Subject: [PATCH 15/16] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a85d90e..01f4634 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ISP-Checker ![License](https://img.shields.io/github/license/fmdlc/ISP-Checker) ![LastCommit](https://img.shields.io/github/last-commit/fmdlc/ISP-Checker) ![Build](https://github.com/fmdlc/ISP-Checker/workflows/Build/badge.svg) ![Docker](https://img.shields.io/badge/Docker-19.03.13-blue) -> A `docker-compose` stack to run a set of ISP controls and collect metrics on a [Raspberry Pi](https://www.raspberrypi.org/). +> A `docker-compose` and `kubernetes` stack to run a set of ISP controls and collect metrics on a [Raspberry Pi](https://www.raspberrypi.org/). [![Linkedin](https://i.stack.imgur.com/gVE0j.png) LinkedIn](https://www.linkedin.com/in/fmdlc) [![GitHub](https://i.stack.imgur.com/tskMh.png) GitHub](https://github.com/fmdlc) @@ -45,6 +45,12 @@ Make sure you have the [Docker-CE](https://phoenixnap.com/kb/docker-on-raspberry ```bash $ curl -fsSL https://get.docker.com -o get-docker.sh | bash - ``` +#### kubernetes +Kubernetes is in `beta` version. To install just run: +```bash +$: kubectl apply -f https://raw.githubusercontent.com/fmdlc/ISP-Checker/master/kubernetes/ISP-Checker-deploy.yaml +``` +You need to expose the `grafana` service to get access. You can do it by creating a `LoadBalancer` service type or by using an `IngressController`. #### docker-compose You need to have `docker-compose` installed. To install it execute: From 65cfcceb939cd110480689222c33664528528653 Mon Sep 17 00:00:00 2001 From: Fernando Date: Tue, 20 Oct 2020 13:52:05 +0100 Subject: [PATCH 16/16] Instructions assume you have speedtest-cli installed Realized after installing on a clean raspbian image that the instructions don't include the installation of speedtest-cli. A quick installation via pip, even on runtime, fixed it. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 01f4634..40a23ec 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,12 @@ Make sure you have the [Docker-CE](https://phoenixnap.com/kb/docker-on-raspberry ```bash $ curl -fsSL https://get.docker.com -o get-docker.sh | bash - ``` + +As well as [speedtest-cli](https://github.com/sivel/speedtest-cli) which is required to measure bandwidth. +```bash +$ pip install speedtest-cli +``` + #### kubernetes Kubernetes is in `beta` version. To install just run: ```bash