├── .gitignore ├── LICENSE ├── README.md ├── ansible ├── hosts ├── raspberry.yml └── roles │ ├── grafana │ ├── defaults │ │ └── main.yml │ ├── files │ │ ├── dashboard-config.yaml │ │ └── prometheus.yaml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── raspberry-metrics-dashboard.json.j2 │ ├── node-exporter │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── node.exporter.init.script.j2 │ ├── prometheus │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── prometheus.init.script.j2 │ │ └── prometheus.yml.j2 │ └── rpi-exporter │ ├── defaults │ └── main.yml │ ├── handlers │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ └── rpi.exporter.init.script.j2 └── preview ├── Screen_Shot.jpg ├── dashboard.png ├── dataSource.jpg ├── instance.jpg ├── raspberry_IP_config.jpg └── scheme.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | 3 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Raspberry metrics 2 | ================= 3 | 4 | Expose raspberry PI metrics using [Grafana](https://grafana.com/) and [Prometheus](https://prometheus.io/): 5 | 6 | ![Example](./preview/Screen_Shot.jpg) 7 | 8 | ## About ## 9 | 10 | Visualise your Raspberry PI metrics. Very easy installation - requires single command. Supports multiple Raspberries. 11 | 12 | This project contains [Ansible](https://www.ansible.com/) playbook which installs four services on Raspberry: 13 | 14 | * [Node exporter](https://github.com/prometheus/node_exporter) - exposes Raspberry metrics 15 | * [rpi_exporter](https://github.com/lukasmalkmus/rpi_exporter) - exposes CPU temperature as Prometheus metric 16 | * [Prometheus](https://prometheus.io/) - collects and stores metrics 17 | * [Grafana](https://grafana.com/) - metrics visualization 18 | 19 | ![Scheme](./preview/scheme.jpg) 20 | 21 | ### Prerequisites ### 22 | 23 | * Raspberry PI with ARMv7 processor (Tested on [Raspberry PI 2 Model B](https://www.raspberrypi.org/products/raspberry-pi-2-model-b/)) 24 | * Debian installed on Raspberry (like [Raspbian Buster](https://www.raspberrypi.org/downloads/raspbian/)) 25 | * open port 22 on raspberry (SSH) 26 | * open port 3000 on raspberry (Grafana) 27 | 28 | ## How to install ## 29 | 30 | [Ansible](https://www.ansible.com/) is required for installation. 31 | 32 | If you don't have Ansible installed, see [Ansible installation](http://docs.ansible.com/ansible/intro_installation.html). 33 | 34 | ### 1. Configure Raspberry IP address ### 35 | 36 | Checkout project. 37 | 38 | Edit file **ansible/hosts** and set Raspberry IP address in **main-raspberry** group. 39 | 40 | If you have more than one Raspberry PI, configure additional Raspberry PI addresses in **additional-raspberries** group. 41 | 42 | Here is the example: 43 | 44 | ![IP_config](./preview/raspberry_IP_config.jpg) 45 | 46 | The "main" Raspberry will have all 4 services installed (Prometheus, Node exporter, rpi_exporter and Grafana), 47 | and the "additional Raspberries" will have Node exporter and rpi_exporter service installed. 48 | 49 | The main Raspberry will collect metrics from all additional Raspberries (if configured), so they must be accessible from the main Raspberry. 50 | 51 | ### 2. Run ansible playbook ### 52 | 53 | Run Ansible playbook with password authentication: 54 | ``` 55 | cd ansible/ 56 | ansible-playbook raspberry.yml -i hosts -u pi -D -k -K 57 | ``` 58 | 59 | Or run Ansible playbook with SSH keys authentication: 60 | 61 | ``` 62 | cd ansible/ 63 | ansible-playbook raspberry.yml -i hosts -u pi -D 64 | ``` 65 | 66 | ### 3. Check metrics ### 67 | 68 | * Go to Grafana URL http://raspberry:3000/ 69 | * login with admin/admin 70 | * Click on "Raspberry metrics" dashboard: 71 | 72 | ![Dashboard](./preview/dashboard.png) 73 | 74 | * If you have more than one Raspberry configured, you can select another Raspberry using **instance** dropdown: 75 | 76 | ![Instance](./preview/instance.jpg) 77 | -------------------------------------------------------------------------------- /ansible/hosts: -------------------------------------------------------------------------------- 1 | [main-raspberry] 2 | 192.168.0.36 3 | 4 | [additional-raspberries] 5 | -------------------------------------------------------------------------------- /ansible/raspberry.yml: -------------------------------------------------------------------------------- 1 | - name: Install Node exporter service 2 | hosts: all 3 | roles: 4 | - { role: 'node-exporter', tags: 'node-exporter' } 5 | 6 | - name: Install RPi exporter service 7 | hosts: all 8 | roles: 9 | - { role: 'rpi-exporter', tags: 'rpi-exporter' } 10 | 11 | - name: Install Prometheus service 12 | hosts: main-raspberry 13 | roles: 14 | - { role: 'prometheus', tags: 'prometheus' } 15 | 16 | - name: Install Grafana service 17 | hosts: main-raspberry 18 | roles: 19 | - { role: 'grafana', tags: 'grafana' } 20 | -------------------------------------------------------------------------------- /ansible/roles/grafana/defaults/main.yml: -------------------------------------------------------------------------------- 1 | grafana_download_url: https://dl.grafana.com/oss/release/grafana_7.3.5_armhf.deb 2 | grafana_filename: grafana_7.3.5_armhf.deb 3 | grafana_install_path: /opt/grafana 4 | -------------------------------------------------------------------------------- /ansible/roles/grafana/files/dashboard-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'default' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | options: 10 | path: /var/lib/grafana/dashboards 11 | -------------------------------------------------------------------------------- /ansible/roles/grafana/files/prometheus.yaml: -------------------------------------------------------------------------------- 1 | # config file version 2 | apiVersion: 1 3 | 4 | # list of datasources that should be deleted from the database 5 | deleteDatasources: 6 | - name: Prometheus 7 | orgId: 1 8 | 9 | # list of datasources to insert/update depending 10 | # whats available in the database 11 | datasources: 12 | # name of the datasource. Required 13 | - name: raspberry-prometheus 14 | # datasource type. Required 15 | type: prometheus 16 | # access mode. direct or proxy. Required 17 | access: proxy 18 | # org id. will default to orgId 1 if not specified 19 | orgId: 1 20 | # url 21 | url: http://localhost:9090 22 | # database password, if used 23 | password: 24 | # database user, if used 25 | user: 26 | # database name, if used 27 | database: 28 | # enable/disable basic auth 29 | basicAuth: 30 | # basic auth username 31 | basicAuthUser: 32 | # basic auth password 33 | basicAuthPassword: 34 | # enable/disable with credentials headers 35 | withCredentials: 36 | # mark as default datasource. Max one per org 37 | isDefault: 38 | # fields that will be converted to json and stored in json_data 39 | jsonData: 40 | graphiteVersion: "1.1" 41 | tlsAuth: false 42 | tlsAuthWithCACert: false 43 | # json object of data that will be encrypted. 44 | secureJsonData: 45 | tlsCACert: "..." 46 | tlsClientCert: "..." 47 | tlsClientKey: "..." 48 | version: 1 49 | # allow users to edit datasources from the UI. 50 | editable: true -------------------------------------------------------------------------------- /ansible/roles/grafana/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: Grafana Deamon reload 2 | become: true 3 | systemd: 4 | name: grafana-server 5 | daemon_reload: yes 6 | 7 | - name: Restart Grafana 8 | become: true 9 | service: 10 | name: grafana-server 11 | state: restarted -------------------------------------------------------------------------------- /ansible/roles/grafana/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install package "adduser" 2 | become: true 3 | apt: 4 | name: adduser 5 | state: present 6 | 7 | - name: Install package "libfontconfig" 8 | become: true 9 | apt: 10 | name: libfontconfig 11 | state: present 12 | 13 | - name: Creates directory Grafana service 14 | become: true 15 | file: 16 | path: "{{ grafana_install_path }}" 17 | state: directory 18 | owner: pi 19 | group: pi 20 | mode: 0775 21 | 22 | - stat: 23 | path: "{{ grafana_install_path }}/{{ grafana_filename }}" 24 | register: dest 25 | 26 | - name: Download Grafana package 27 | get_url: 28 | url: "{{ grafana_download_url }}" 29 | dest: "{{ grafana_install_path }}/" 30 | when: not dest.stat.exists 31 | 32 | - name: Install Grafana package 33 | become: true 34 | apt: 35 | deb: "{{ grafana_install_path }}/{{ grafana_filename }}" 36 | notify: 37 | - Grafana Deamon reload 38 | - Restart Grafana 39 | 40 | - name: Configure prometheus data source 41 | become: true 42 | copy: 43 | src: "{{ item }}" 44 | dest: /etc/grafana/provisioning/datasources/{{ item }} 45 | with_items: 46 | - prometheus.yaml 47 | notify: Restart Grafana 48 | 49 | - name: Creates directory for example dashboard 50 | become: true 51 | file: 52 | path: /var/lib/grafana/dashboards 53 | state: directory 54 | owner: grafana 55 | group: grafana 56 | mode: 0744 57 | 58 | - name: Copy example Dashboard 59 | become: true 60 | template: 61 | src: raspberry-metrics-dashboard.json.j2 62 | dest: /var/lib/grafana/dashboards/raspberry-metrics-dashboard.json 63 | owner: grafana 64 | group: grafana 65 | notify: Restart Grafana 66 | 67 | - name: Configure dashoard provisioning 68 | become: true 69 | copy: 70 | src: "{{ item }}" 71 | dest: /etc/grafana/provisioning/dashboards/{{ item }} 72 | with_items: 73 | - dashboard-config.yaml 74 | notify: Restart Grafana 75 | 76 | - meta: flush_handlers 77 | 78 | - name: Start Grafana 79 | become: true 80 | systemd: 81 | name: grafana-server 82 | enabled: yes 83 | state: started -------------------------------------------------------------------------------- /ansible/roles/grafana/templates/raspberry-metrics-dashboard.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "type": "dashboard" 12 | } 13 | ] 14 | }, 15 | "editable": true, 16 | "gnetId": null, 17 | "graphTooltip": 0, 18 | "iteration": 1583677508264, 19 | "links": [], 20 | "panels": [ 21 | { 22 | "collapsed": false, 23 | "datasource": null, 24 | "gridPos": { 25 | "h": 1, 26 | "w": 24, 27 | "x": 0, 28 | "y": 0 29 | }, 30 | "id": 27, 31 | "panels": [], 32 | "title": "CPU", 33 | "type": "row" 34 | }, 35 | { 36 | "datasource": "raspberry-prometheus", 37 | "gridPos": { 38 | "h": 6, 39 | "w": 6, 40 | "x": 0, 41 | "y": 1 42 | }, 43 | "id": 25, 44 | "options": { 45 | "fieldOptions": { 46 | "calcs": [ 47 | "last" 48 | ], 49 | "defaults": { 50 | "color": { 51 | "mode": "thresholds" 52 | }, 53 | "mappings": [], 54 | "max": 100, 55 | "min": 0, 56 | "thresholds": { 57 | "mode": "absolute", 58 | "steps": [ 59 | { 60 | "color": "green", 61 | "value": null 62 | }, 63 | { 64 | "color": "red", 65 | "value": 80 66 | } 67 | ] 68 | }, 69 | "unit": "celsius" 70 | }, 71 | "overrides": [], 72 | "values": false 73 | }, 74 | "orientation": "auto", 75 | "showThresholdLabels": false, 76 | "showThresholdMarkers": true 77 | }, 78 | "pluginVersion": "7.3.5", 79 | "targets": [ 80 | { 81 | "expr": "rpi_cpu_temperature_celsius{instance=~\"$instance.*\"}", 82 | "format": "time_series", 83 | "instant": false, 84 | "intervalFactor": 3, 85 | "refId": "A" 86 | } 87 | ], 88 | "timeFrom": null, 89 | "timeShift": null, 90 | "title": "CPU temperature", 91 | "type": "gauge" 92 | }, 93 | { 94 | "cacheTimeout": null, 95 | "colorBackground": false, 96 | "colorValue": false, 97 | "colors": [ 98 | "#96D98D", 99 | "rgba(237, 129, 40, 0.89)", 100 | "#d44a3a" 101 | ], 102 | "datasource": "raspberry-prometheus", 103 | "decimals": 1, 104 | "format": "hertz", 105 | "gauge": { 106 | "maxValue": 100, 107 | "minValue": 0, 108 | "show": false, 109 | "thresholdLabels": false, 110 | "thresholdMarkers": true 111 | }, 112 | "gridPos": { 113 | "h": 3, 114 | "w": 3, 115 | "x": 6, 116 | "y": 1 117 | }, 118 | "id": 29, 119 | "interval": null, 120 | "links": [], 121 | "mappingType": 1, 122 | "mappingTypes": [ 123 | { 124 | "name": "value to text", 125 | "value": 1 126 | }, 127 | { 128 | "name": "range to text", 129 | "value": 2 130 | } 131 | ], 132 | "maxDataPoints": 100, 133 | "nullPointMode": "connected", 134 | "nullText": null, 135 | "options": {}, 136 | "postfix": "", 137 | "postfixFontSize": "50%", 138 | "prefix": "", 139 | "prefixFontSize": "50%", 140 | "rangeMaps": [ 141 | { 142 | "from": "null", 143 | "text": "N/A", 144 | "to": "null" 145 | } 146 | ], 147 | "sparkline": { 148 | "fillColor": "rgba(31, 118, 189, 0.18)", 149 | "full": false, 150 | "lineColor": "rgb(31, 120, 193)", 151 | "show": false, 152 | "ymax": null, 153 | "ymin": null 154 | }, 155 | "tableColumn": "", 156 | "targets": [ 157 | { 158 | "expr": "node_cpu_frequency_max_hertz{instance=~\"$instance.*\", cpu=\"0\"}", 159 | "refId": "A" 160 | } 161 | ], 162 | "thresholds": "", 163 | "timeFrom": null, 164 | "timeShift": null, 165 | "title": "CPU frequency", 166 | "type": "singlestat", 167 | "valueFontSize": "80%", 168 | "valueMaps": [ 169 | { 170 | "op": "=", 171 | "text": "N/A", 172 | "value": "null" 173 | } 174 | ], 175 | "valueName": "current" 176 | }, 177 | { 178 | "cacheTimeout": null, 179 | "colorBackground": false, 180 | "colorValue": true, 181 | "colors": [ 182 | "#E02F44", 183 | "#F2CC0C", 184 | "#96D98D" 185 | ], 186 | "datasource": "raspberry-prometheus", 187 | "decimals": 2, 188 | "format": "decbytes", 189 | "gauge": { 190 | "maxValue": 100, 191 | "minValue": 0, 192 | "show": false, 193 | "thresholdLabels": false, 194 | "thresholdMarkers": true 195 | }, 196 | "gridPos": { 197 | "h": 3, 198 | "w": 4, 199 | "x": 9, 200 | "y": 1 201 | }, 202 | "id": 20, 203 | "interval": null, 204 | "links": [], 205 | "mappingType": 1, 206 | "mappingTypes": [ 207 | { 208 | "name": "value to text", 209 | "value": 1 210 | }, 211 | { 212 | "name": "range to text", 213 | "value": 2 214 | } 215 | ], 216 | "maxDataPoints": 100, 217 | "nullPointMode": "connected", 218 | "nullText": null, 219 | "options": {}, 220 | "pluginVersion": "7.3.5", 221 | "postfix": "", 222 | "postfixFontSize": "50%", 223 | "prefix": "", 224 | "prefixFontSize": "50%", 225 | "rangeMaps": [ 226 | { 227 | "from": "null", 228 | "text": "N/A", 229 | "to": "null" 230 | } 231 | ], 232 | "sparkline": { 233 | "fillColor": "rgba(31, 118, 189, 0.18)", 234 | "full": false, 235 | "lineColor": "rgb(31, 120, 193)", 236 | "show": false, 237 | "ymax": null, 238 | "ymin": null 239 | }, 240 | "tableColumn": "", 241 | "targets": [ 242 | { 243 | "expr": "node_memory_MemAvailable_bytes{instance=~\"$instance.*\"}", 244 | "format": "time_series", 245 | "intervalFactor": 2, 246 | "legendFormat": "", 247 | "refId": "A", 248 | "step": 600 249 | } 250 | ], 251 | "thresholds": "104857600,209715200", 252 | "title": "Available memory", 253 | "type": "singlestat", 254 | "valueFontSize": "110%", 255 | "valueMaps": [ 256 | { 257 | "op": "=", 258 | "text": "N/A", 259 | "value": "null" 260 | } 261 | ], 262 | "valueName": "current" 263 | }, 264 | { 265 | "aliasColors": {}, 266 | "bars": false, 267 | "dashLength": 10, 268 | "dashes": false, 269 | "datasource": "raspberry-prometheus", 270 | "fill": 1, 271 | "fillGradient": 0, 272 | "gridPos": { 273 | "h": 6, 274 | "w": 11, 275 | "x": 13, 276 | "y": 1 277 | }, 278 | "hiddenSeries": false, 279 | "id": 10, 280 | "legend": { 281 | "alignAsTable": false, 282 | "avg": false, 283 | "current": false, 284 | "max": false, 285 | "min": false, 286 | "rightSide": false, 287 | "show": false, 288 | "total": false, 289 | "values": false 290 | }, 291 | "lines": true, 292 | "linewidth": 1, 293 | "links": [], 294 | "nullPointMode": "null as zero", 295 | "options": { 296 | "dataLinks": [] 297 | }, 298 | "percentage": false, 299 | "pointradius": 5, 300 | "points": false, 301 | "renderer": "flot", 302 | "seriesOverrides": [], 303 | "spaceLength": 10, 304 | "stack": false, 305 | "steppedLine": false, 306 | "targets": [ 307 | { 308 | "expr": "node_load1{instance=~\"$instance.*\"}", 309 | "format": "time_series", 310 | "intervalFactor": 2, 311 | "legendFormat": "Load", 312 | "refId": "A", 313 | "step": 40 314 | } 315 | ], 316 | "thresholds": [], 317 | "timeFrom": null, 318 | "timeRegions": [], 319 | "timeShift": null, 320 | "title": "CPU Load", 321 | "tooltip": { 322 | "shared": true, 323 | "sort": 2, 324 | "value_type": "individual" 325 | }, 326 | "type": "graph", 327 | "xaxis": { 328 | "buckets": null, 329 | "mode": "time", 330 | "name": null, 331 | "show": true, 332 | "values": [] 333 | }, 334 | "yaxes": [ 335 | { 336 | "decimals": 0, 337 | "format": "percentunit", 338 | "label": null, 339 | "logBase": 1, 340 | "max": null, 341 | "min": "0", 342 | "show": true 343 | }, 344 | { 345 | "format": "short", 346 | "label": null, 347 | "logBase": 1, 348 | "max": null, 349 | "min": null, 350 | "show": false 351 | } 352 | ], 353 | "yaxis": { 354 | "align": false, 355 | "alignLevel": null 356 | } 357 | }, 358 | { 359 | "cacheTimeout": null, 360 | "colorBackground": false, 361 | "colorValue": false, 362 | "colors": [ 363 | "#299c46", 364 | "rgba(237, 129, 40, 0.89)", 365 | "#d44a3a" 366 | ], 367 | "datasource": "raspberry-prometheus", 368 | "format": "none", 369 | "gauge": { 370 | "maxValue": 100, 371 | "minValue": 0, 372 | "show": false, 373 | "thresholdLabels": false, 374 | "thresholdMarkers": true 375 | }, 376 | "gridPos": { 377 | "h": 3, 378 | "w": 3, 379 | "x": 6, 380 | "y": 4 381 | }, 382 | "id": 31, 383 | "interval": null, 384 | "links": [], 385 | "mappingType": 1, 386 | "mappingTypes": [ 387 | { 388 | "name": "value to text", 389 | "value": 1 390 | }, 391 | { 392 | "name": "range to text", 393 | "value": 2 394 | } 395 | ], 396 | "maxDataPoints": 100, 397 | "nullPointMode": "connected", 398 | "nullText": null, 399 | "options": {}, 400 | "postfix": "", 401 | "postfixFontSize": "50%", 402 | "prefix": "", 403 | "prefixFontSize": "50%", 404 | "rangeMaps": [ 405 | { 406 | "from": "null", 407 | "text": "N/A", 408 | "to": "null" 409 | } 410 | ], 411 | "sparkline": { 412 | "fillColor": "rgba(31, 118, 189, 0.18)", 413 | "full": false, 414 | "lineColor": "rgb(31, 120, 193)", 415 | "show": false, 416 | "ymax": null, 417 | "ymin": null 418 | }, 419 | "tableColumn": "", 420 | "targets": [ 421 | { 422 | "expr": "count(node_cpu_frequency_max_hertz{instance=~\"$instance.*\"})", 423 | "refId": "A" 424 | } 425 | ], 426 | "thresholds": "", 427 | "timeFrom": null, 428 | "timeShift": null, 429 | "title": "CPU cores", 430 | "type": "singlestat", 431 | "valueFontSize": "80%", 432 | "valueMaps": [ 433 | { 434 | "op": "=", 435 | "text": "N/A", 436 | "value": "null" 437 | } 438 | ], 439 | "valueName": "current" 440 | }, 441 | { 442 | "cacheTimeout": null, 443 | "colorBackground": false, 444 | "colorValue": true, 445 | "colors": [ 446 | "#E02F44", 447 | "#F2CC0C", 448 | "#96D98D" 449 | ], 450 | "datasource": "raspberry-prometheus", 451 | "decimals": null, 452 | "format": "decbytes", 453 | "gauge": { 454 | "maxValue": 100, 455 | "minValue": 0, 456 | "show": false, 457 | "thresholdLabels": false, 458 | "thresholdMarkers": true 459 | }, 460 | "gridPos": { 461 | "h": 3, 462 | "w": 4, 463 | "x": 9, 464 | "y": 4 465 | }, 466 | "id": 15, 467 | "interval": null, 468 | "links": [], 469 | "mappingType": 1, 470 | "mappingTypes": [ 471 | { 472 | "name": "value to text", 473 | "value": 1 474 | }, 475 | { 476 | "name": "range to text", 477 | "value": 2 478 | } 479 | ], 480 | "maxDataPoints": 100, 481 | "nullPointMode": "connected", 482 | "nullText": null, 483 | "options": {}, 484 | "pluginVersion": "7.3.5", 485 | "postfix": "", 486 | "postfixFontSize": "50%", 487 | "prefix": "", 488 | "prefixFontSize": "50%", 489 | "rangeMaps": [ 490 | { 491 | "from": "null", 492 | "text": "N/A", 493 | "to": "null" 494 | } 495 | ], 496 | "sparkline": { 497 | "fillColor": "rgba(31, 118, 189, 0.18)", 498 | "full": false, 499 | "lineColor": "rgb(31, 120, 193)", 500 | "show": false, 501 | "ymax": null, 502 | "ymin": null 503 | }, 504 | "tableColumn": "", 505 | "targets": [ 506 | { 507 | "expr": "node_filesystem_avail_bytes{device=\"/dev/root\", instance=~\"$instance.*\"}", 508 | "format": "time_series", 509 | "intervalFactor": 2, 510 | "legendFormat": "", 511 | "refId": "A", 512 | "step": 600 513 | } 514 | ], 515 | "thresholds": "209715200,1073741824", 516 | "title": "Available disk space", 517 | "type": "singlestat", 518 | "valueFontSize": "110%", 519 | "valueMaps": [ 520 | { 521 | "op": "=", 522 | "text": "N/A", 523 | "value": "null" 524 | } 525 | ], 526 | "valueName": "current" 527 | }, 528 | { 529 | "collapsed": false, 530 | "datasource": null, 531 | "gridPos": { 532 | "h": 1, 533 | "w": 24, 534 | "x": 0, 535 | "y": 7 536 | }, 537 | "id": 21, 538 | "panels": [], 539 | "repeat": null, 540 | "title": "Memory", 541 | "type": "row" 542 | }, 543 | { 544 | "aliasColors": { 545 | "Total memory": "#BF1B00", 546 | "Used memory": "#B7DBAB", 547 | "home-service": "#EF843C" 548 | }, 549 | "bars": false, 550 | "dashLength": 10, 551 | "dashes": false, 552 | "datasource": "raspberry-prometheus", 553 | "fill": 1, 554 | "fillGradient": 0, 555 | "gridPos": { 556 | "h": 7, 557 | "w": 12, 558 | "x": 0, 559 | "y": 8 560 | }, 561 | "hiddenSeries": false, 562 | "id": 12, 563 | "legend": { 564 | "alignAsTable": false, 565 | "avg": false, 566 | "current": false, 567 | "hideEmpty": false, 568 | "hideZero": false, 569 | "max": false, 570 | "min": false, 571 | "rightSide": false, 572 | "show": true, 573 | "total": false, 574 | "values": false 575 | }, 576 | "lines": true, 577 | "linewidth": 2, 578 | "links": [], 579 | "maxPerRow": 3, 580 | "nullPointMode": "null as zero", 581 | "options": { 582 | "dataLinks": [] 583 | }, 584 | "percentage": false, 585 | "pointradius": 5, 586 | "points": false, 587 | "renderer": "flot", 588 | "seriesOverrides": [], 589 | "spaceLength": 10, 590 | "stack": false, 591 | "steppedLine": false, 592 | "targets": [ 593 | { 594 | "expr": "node_memory_MemTotal_bytes{instance=~\"$instance.*\"}", 595 | "format": "time_series", 596 | "intervalFactor": 2, 597 | "legendFormat": "Total memory", 598 | "metric": "", 599 | "refId": "B", 600 | "step": 60 601 | }, 602 | { 603 | "expr": "node_memory_MemTotal_bytes{instance=~\"$instance.*\"} - node_memory_MemAvailable_bytes{instance=~\"$instance.*\"}", 604 | "format": "time_series", 605 | "intervalFactor": 2, 606 | "legendFormat": "Used memory", 607 | "metric": "", 608 | "refId": "C", 609 | "step": 60 610 | } 611 | ], 612 | "thresholds": [], 613 | "timeFrom": null, 614 | "timeRegions": [], 615 | "timeShift": null, 616 | "title": "Memory usage", 617 | "tooltip": { 618 | "shared": true, 619 | "sort": 2, 620 | "value_type": "individual" 621 | }, 622 | "type": "graph", 623 | "xaxis": { 624 | "buckets": null, 625 | "mode": "time", 626 | "name": null, 627 | "show": true, 628 | "values": [] 629 | }, 630 | "yaxes": [ 631 | { 632 | "decimals": 0, 633 | "format": "decbytes", 634 | "label": "", 635 | "logBase": 1, 636 | "max": null, 637 | "min": "0", 638 | "show": true 639 | }, 640 | { 641 | "format": "short", 642 | "label": null, 643 | "logBase": 1, 644 | "max": null, 645 | "min": null, 646 | "show": false 647 | } 648 | ], 649 | "yaxis": { 650 | "align": false, 651 | "alignLevel": null 652 | } 653 | }, 654 | { 655 | "aliasColors": { 656 | "home-service": "#EF843C" 657 | }, 658 | "bars": false, 659 | "dashLength": 10, 660 | "dashes": false, 661 | "datasource": "raspberry-prometheus", 662 | "fill": 1, 663 | "fillGradient": 0, 664 | "gridPos": { 665 | "h": 7, 666 | "w": 12, 667 | "x": 12, 668 | "y": 8 669 | }, 670 | "hiddenSeries": false, 671 | "id": 13, 672 | "legend": { 673 | "alignAsTable": false, 674 | "avg": false, 675 | "current": false, 676 | "hideEmpty": false, 677 | "hideZero": false, 678 | "max": false, 679 | "min": false, 680 | "rightSide": false, 681 | "show": true, 682 | "total": false, 683 | "values": false 684 | }, 685 | "lines": true, 686 | "linewidth": 2, 687 | "links": [], 688 | "maxPerRow": 3, 689 | "nullPointMode": "null as zero", 690 | "options": { 691 | "dataLinks": [] 692 | }, 693 | "percentage": false, 694 | "pointradius": 5, 695 | "points": false, 696 | "renderer": "flot", 697 | "seriesOverrides": [], 698 | "spaceLength": 10, 699 | "stack": false, 700 | "steppedLine": false, 701 | "targets": [ 702 | { 703 | "expr": "node_memory_SwapFree_bytes{instance=~\"$instance.*\"}", 704 | "format": "time_series", 705 | "intervalFactor": 2, 706 | "legendFormat": "Free", 707 | "metric": "", 708 | "refId": "A", 709 | "step": 120 710 | } 711 | ], 712 | "thresholds": [], 713 | "timeFrom": null, 714 | "timeRegions": [], 715 | "timeShift": null, 716 | "title": "Free Swap", 717 | "tooltip": { 718 | "shared": true, 719 | "sort": 0, 720 | "value_type": "individual" 721 | }, 722 | "type": "graph", 723 | "xaxis": { 724 | "buckets": null, 725 | "mode": "time", 726 | "name": null, 727 | "show": true, 728 | "values": [] 729 | }, 730 | "yaxes": [ 731 | { 732 | "format": "decbytes", 733 | "label": "", 734 | "logBase": 1, 735 | "max": null, 736 | "min": "0", 737 | "show": true 738 | }, 739 | { 740 | "format": "short", 741 | "label": null, 742 | "logBase": 1, 743 | "max": null, 744 | "min": null, 745 | "show": true 746 | } 747 | ], 748 | "yaxis": { 749 | "align": false, 750 | "alignLevel": null 751 | } 752 | }, 753 | { 754 | "collapsed": false, 755 | "datasource": null, 756 | "gridPos": { 757 | "h": 1, 758 | "w": 24, 759 | "x": 0, 760 | "y": 15 761 | }, 762 | "id": 22, 763 | "panels": [], 764 | "repeat": null, 765 | "title": "Disk", 766 | "type": "row" 767 | }, 768 | { 769 | "aliasColors": { 770 | "Size": "#bf1b00", 771 | "Total memory": "#BF1B00", 772 | "Used": "#629e51", 773 | "Used memory": "#508642" 774 | }, 775 | "bars": false, 776 | "dashLength": 10, 777 | "dashes": false, 778 | "datasource": "raspberry-prometheus", 779 | "fill": 1, 780 | "fillGradient": 0, 781 | "gridPos": { 782 | "h": 7, 783 | "w": 12, 784 | "x": 0, 785 | "y": 16 786 | }, 787 | "hiddenSeries": false, 788 | "id": 7, 789 | "legend": { 790 | "alignAsTable": false, 791 | "avg": false, 792 | "current": false, 793 | "max": false, 794 | "min": false, 795 | "rightSide": false, 796 | "show": true, 797 | "total": false, 798 | "values": false 799 | }, 800 | "lines": true, 801 | "linewidth": 2, 802 | "links": [], 803 | "nullPointMode": "null as zero", 804 | "options": { 805 | "dataLinks": [] 806 | }, 807 | "percentage": false, 808 | "pointradius": 5, 809 | "points": false, 810 | "renderer": "flot", 811 | "seriesOverrides": [], 812 | "spaceLength": 10, 813 | "stack": false, 814 | "steppedLine": false, 815 | "targets": [ 816 | { 817 | "expr": "node_filesystem_size_bytes{device=\"/dev/root\", instance=~\"$instance.*\"}", 818 | "format": "time_series", 819 | "hide": false, 820 | "intervalFactor": 2, 821 | "legendFormat": "Size", 822 | "refId": "A", 823 | "step": 60 824 | }, 825 | { 826 | "expr": "node_filesystem_size_bytes{device=\"/dev/root\", instance=~\"$instance.*\"} - node_filesystem_avail_bytes{device=\"/dev/root\", instance=~\"$instance.*\"}", 827 | "format": "time_series", 828 | "hide": false, 829 | "intervalFactor": 2, 830 | "legendFormat": "Used", 831 | "refId": "B", 832 | "step": 60 833 | } 834 | ], 835 | "thresholds": [], 836 | "timeFrom": null, 837 | "timeRegions": [], 838 | "timeShift": null, 839 | "title": "Disk space usage", 840 | "tooltip": { 841 | "shared": true, 842 | "sort": 2, 843 | "value_type": "individual" 844 | }, 845 | "type": "graph", 846 | "xaxis": { 847 | "buckets": null, 848 | "mode": "time", 849 | "name": null, 850 | "show": true, 851 | "values": [] 852 | }, 853 | "yaxes": [ 854 | { 855 | "format": "decbytes", 856 | "label": null, 857 | "logBase": 1, 858 | "max": null, 859 | "min": "0", 860 | "show": true 861 | }, 862 | { 863 | "format": "short", 864 | "label": null, 865 | "logBase": 1, 866 | "max": null, 867 | "min": null, 868 | "show": true 869 | } 870 | ], 871 | "yaxis": { 872 | "align": false, 873 | "alignLevel": null 874 | } 875 | }, 876 | { 877 | "aliasColors": { 878 | "Size": "#bf1b00", 879 | "Total memory": "#BF1B00", 880 | "Used": "#629e51", 881 | "Used memory": "#508642", 882 | "read rate": "dark-orange", 883 | "write rate": "light-yellow" 884 | }, 885 | "bars": false, 886 | "dashLength": 10, 887 | "dashes": false, 888 | "datasource": "raspberry-prometheus", 889 | "fill": 1, 890 | "fillGradient": 0, 891 | "gridPos": { 892 | "h": 7, 893 | "w": 12, 894 | "x": 12, 895 | "y": 16 896 | }, 897 | "hiddenSeries": false, 898 | "id": 32, 899 | "legend": { 900 | "alignAsTable": false, 901 | "avg": false, 902 | "current": false, 903 | "max": false, 904 | "min": false, 905 | "rightSide": false, 906 | "show": true, 907 | "total": false, 908 | "values": false 909 | }, 910 | "lines": true, 911 | "linewidth": 1, 912 | "links": [], 913 | "nullPointMode": "null as zero", 914 | "options": { 915 | "dataLinks": [] 916 | }, 917 | "percentage": false, 918 | "pointradius": 5, 919 | "points": false, 920 | "renderer": "flot", 921 | "seriesOverrides": [ 922 | { 923 | "alias": "write", 924 | "yaxis": 2 925 | } 926 | ], 927 | "spaceLength": 10, 928 | "stack": false, 929 | "steppedLine": false, 930 | "targets": [ 931 | { 932 | "expr": "sum(rate(node_disk_written_bytes_total{instance=~\"$instance.*\"}[1m]))", 933 | "format": "time_series", 934 | "hide": false, 935 | "intervalFactor": 2, 936 | "legendFormat": "write", 937 | "refId": "A", 938 | "step": 60 939 | }, 940 | { 941 | "expr": "sum(rate(node_disk_read_bytes_total{instance=~\"$instance.*\"}[1m]))", 942 | "format": "time_series", 943 | "hide": false, 944 | "intervalFactor": 2, 945 | "legendFormat": "read", 946 | "refId": "B", 947 | "step": 60 948 | } 949 | ], 950 | "thresholds": [], 951 | "timeFrom": null, 952 | "timeRegions": [], 953 | "timeShift": null, 954 | "title": "Disk read/write rate", 955 | "tooltip": { 956 | "shared": true, 957 | "sort": 2, 958 | "value_type": "individual" 959 | }, 960 | "type": "graph", 961 | "xaxis": { 962 | "buckets": null, 963 | "mode": "time", 964 | "name": null, 965 | "show": true, 966 | "values": [] 967 | }, 968 | "yaxes": [ 969 | { 970 | "format": "decbytes", 971 | "label": null, 972 | "logBase": 1, 973 | "max": null, 974 | "min": "0", 975 | "show": true 976 | }, 977 | { 978 | "format": "decbytes", 979 | "label": null, 980 | "logBase": 1, 981 | "max": null, 982 | "min": null, 983 | "show": true 984 | } 985 | ], 986 | "yaxis": { 987 | "align": false, 988 | "alignLevel": null 989 | } 990 | }, 991 | { 992 | "collapsed": false, 993 | "datasource": null, 994 | "gridPos": { 995 | "h": 1, 996 | "w": 24, 997 | "x": 0, 998 | "y": 23 999 | }, 1000 | "id": 23, 1001 | "panels": [], 1002 | "repeat": null, 1003 | "title": "Network", 1004 | "type": "row" 1005 | }, 1006 | { 1007 | "aliasColors": { 1008 | "receive rate": "dark-orange", 1009 | "transmit rate": "light-yellow" 1010 | }, 1011 | "bars": false, 1012 | "dashLength": 10, 1013 | "dashes": false, 1014 | "datasource": "raspberry-prometheus", 1015 | "fill": 1, 1016 | "fillGradient": 0, 1017 | "gridPos": { 1018 | "h": 7, 1019 | "w": 12, 1020 | "x": 0, 1021 | "y": 24 1022 | }, 1023 | "hiddenSeries": false, 1024 | "id": 9, 1025 | "legend": { 1026 | "avg": false, 1027 | "current": false, 1028 | "max": false, 1029 | "min": false, 1030 | "show": true, 1031 | "total": false, 1032 | "values": false 1033 | }, 1034 | "lines": true, 1035 | "linewidth": 1, 1036 | "links": [], 1037 | "nullPointMode": "null as zero", 1038 | "options": { 1039 | "dataLinks": [] 1040 | }, 1041 | "percentage": false, 1042 | "pointradius": 5, 1043 | "points": false, 1044 | "renderer": "flot", 1045 | "seriesOverrides": [ 1046 | { 1047 | "alias": "transmit", 1048 | "yaxis": 2 1049 | } 1050 | ], 1051 | "spaceLength": 10, 1052 | "stack": false, 1053 | "steppedLine": false, 1054 | "targets": [ 1055 | { 1056 | "expr": "sum(rate(node_network_receive_bytes_total{instance=~\"$instance.*\"}[1m]))", 1057 | "format": "time_series", 1058 | "intervalFactor": 2, 1059 | "legendFormat": "receive", 1060 | "refId": "A", 1061 | "step": 120 1062 | }, 1063 | { 1064 | "expr": "sum(rate(node_network_transmit_bytes_total{instance=~\"$instance.*\"}[1m]))", 1065 | "format": "time_series", 1066 | "intervalFactor": 2, 1067 | "legendFormat": "transmit", 1068 | "refId": "B", 1069 | "step": 120 1070 | } 1071 | ], 1072 | "thresholds": [], 1073 | "timeFrom": null, 1074 | "timeRegions": [], 1075 | "timeShift": null, 1076 | "title": "Network receive/transmit rate", 1077 | "tooltip": { 1078 | "shared": true, 1079 | "sort": 2, 1080 | "value_type": "individual" 1081 | }, 1082 | "type": "graph", 1083 | "xaxis": { 1084 | "buckets": null, 1085 | "mode": "time", 1086 | "name": null, 1087 | "show": true, 1088 | "values": [] 1089 | }, 1090 | "yaxes": [ 1091 | { 1092 | "format": "decbytes", 1093 | "label": null, 1094 | "logBase": 1, 1095 | "max": null, 1096 | "min": "0", 1097 | "show": true 1098 | }, 1099 | { 1100 | "format": "decbytes", 1101 | "label": null, 1102 | "logBase": 1, 1103 | "max": null, 1104 | "min": null, 1105 | "show": true 1106 | } 1107 | ], 1108 | "yaxis": { 1109 | "align": false, 1110 | "alignLevel": null 1111 | } 1112 | }, 1113 | { 1114 | "aliasColors": {}, 1115 | "bars": false, 1116 | "dashLength": 10, 1117 | "dashes": false, 1118 | "datasource": "raspberry-prometheus", 1119 | "fill": 1, 1120 | "fillGradient": 0, 1121 | "gridPos": { 1122 | "h": 7, 1123 | "w": 12, 1124 | "x": 12, 1125 | "y": 24 1126 | }, 1127 | "hiddenSeries": false, 1128 | "id": 33, 1129 | "legend": { 1130 | "avg": false, 1131 | "current": false, 1132 | "max": false, 1133 | "min": false, 1134 | "show": false, 1135 | "total": false, 1136 | "values": false 1137 | }, 1138 | "lines": true, 1139 | "linewidth": 1, 1140 | "links": [], 1141 | "nullPointMode": "null as zero", 1142 | "options": { 1143 | "dataLinks": [] 1144 | }, 1145 | "percentage": false, 1146 | "pointradius": 5, 1147 | "points": false, 1148 | "renderer": "flot", 1149 | "seriesOverrides": [], 1150 | "spaceLength": 10, 1151 | "stack": false, 1152 | "steppedLine": false, 1153 | "targets": [ 1154 | { 1155 | "expr": "node_netstat_Tcp_CurrEstab{instance=~\"$instance.*\"}", 1156 | "format": "time_series", 1157 | "intervalFactor": 2, 1158 | "legendFormat": "Count", 1159 | "refId": "A", 1160 | "step": 120 1161 | } 1162 | ], 1163 | "thresholds": [], 1164 | "timeFrom": null, 1165 | "timeRegions": [], 1166 | "timeShift": null, 1167 | "title": "Number of TCP Connections", 1168 | "tooltip": { 1169 | "shared": true, 1170 | "sort": 2, 1171 | "value_type": "individual" 1172 | }, 1173 | "type": "graph", 1174 | "xaxis": { 1175 | "buckets": null, 1176 | "mode": "time", 1177 | "name": null, 1178 | "show": true, 1179 | "values": [] 1180 | }, 1181 | "yaxes": [ 1182 | { 1183 | "format": "none", 1184 | "label": null, 1185 | "logBase": 1, 1186 | "max": null, 1187 | "min": "0", 1188 | "show": true 1189 | }, 1190 | { 1191 | "format": "short", 1192 | "label": null, 1193 | "logBase": 1, 1194 | "max": null, 1195 | "min": null, 1196 | "show": true 1197 | } 1198 | ], 1199 | "yaxis": { 1200 | "align": false, 1201 | "alignLevel": null 1202 | } 1203 | } 1204 | ], 1205 | "refresh": "10s", 1206 | "schemaVersion": 22, 1207 | "style": "dark", 1208 | "tags": [], 1209 | "templating": { 1210 | "list": [ 1211 | { 1212 | "allValue": null, 1213 | "current": { 1214 | "text": "{{ groups['main-raspberry'][0] }}", 1215 | "value": "{{ groups['main-raspberry'][0] }}" 1216 | }, 1217 | "datasource": "raspberry-prometheus", 1218 | "definition": "", 1219 | "hide": 0, 1220 | "includeAll": false, 1221 | "label": null, 1222 | "multi": false, 1223 | "name": "instance", 1224 | "options": [], 1225 | "query": "label_values(instance)", 1226 | "refresh": 1, 1227 | "regex": "(.*):.*", 1228 | "skipUrlSync": false, 1229 | "sort": 0, 1230 | "tagValuesQuery": "", 1231 | "tags": [], 1232 | "tagsQuery": "", 1233 | "type": "query", 1234 | "useTags": false 1235 | } 1236 | ] 1237 | }, 1238 | "time": { 1239 | "from": "now-1h", 1240 | "to": "now" 1241 | }, 1242 | "timepicker": { 1243 | "refresh_intervals": [ 1244 | "5s", 1245 | "10s", 1246 | "30s", 1247 | "1m", 1248 | "5m", 1249 | "15m", 1250 | "30m", 1251 | "1h", 1252 | "2h", 1253 | "1d" 1254 | ], 1255 | "time_options": [ 1256 | "5m", 1257 | "15m", 1258 | "1h", 1259 | "6h", 1260 | "12h", 1261 | "24h", 1262 | "2d", 1263 | "7d", 1264 | "30d" 1265 | ] 1266 | }, 1267 | "timezone": "browser", 1268 | "title": "Raspberry Metrics", 1269 | "uid": "wc_mbnZRz", 1270 | "version": 1 1271 | } -------------------------------------------------------------------------------- /ansible/roles/node-exporter/defaults/main.yml: -------------------------------------------------------------------------------- 1 | node_exporter_download_url: https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-armv7.tar.gz 2 | node_exporter_filename: node_exporter-1.0.1.linux-armv7 3 | node_exporter_install_path: /opt/node-exporter 4 | node_exporter_service_name: node-exporter 5 | node_exporter_log_path: /var/log/node_exporter 6 | -------------------------------------------------------------------------------- /ansible/roles/node-exporter/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: update-rc.d node_exporter 2 | become: true 3 | command: update-rc.d {{ node_exporter_service_name }} defaults 4 | 5 | - name: restart node_exporter 6 | become: true 7 | service: 8 | name: node-exporter 9 | state: restarted 10 | 11 | -------------------------------------------------------------------------------- /ansible/roles/node-exporter/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Creates directory Node exporter service 2 | become: true 3 | file: 4 | path: "{{ node_exporter_install_path }}" 5 | state: directory 6 | owner: pi 7 | group: pi 8 | mode: 0775 9 | 10 | - name: Creates log directory 11 | become: true 12 | file: 13 | path: "{{ node_exporter_log_path }}" 14 | state: directory 15 | owner: pi 16 | group: pi 17 | mode: 0775 18 | 19 | - stat: 20 | path: "{{ node_exporter_install_path }}/{{ node_exporter_filename }}" 21 | register: dest 22 | 23 | - name: Download and unarchive Node exporter package 24 | unarchive: 25 | src: "{{ node_exporter_download_url }}" 26 | remote_src: True 27 | dest: "{{ node_exporter_install_path }}/" 28 | when: not dest.stat.exists 29 | 30 | - name: Copy Node exporter init script 31 | become: true 32 | template: 33 | src: node.exporter.init.script.j2 34 | dest: /etc/init.d/{{ node_exporter_service_name }} 35 | mode: 0755 36 | notify: 37 | - update-rc.d node_exporter 38 | - restart node_exporter 39 | 40 | - meta: flush_handlers 41 | 42 | - name: Start Node exporter 43 | become: true 44 | service: 45 | name: node-exporter 46 | state: started 47 | -------------------------------------------------------------------------------- /ansible/roles/node-exporter/templates/node.exporter.init.script.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: node-exporter 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Start daemon at boot time 9 | # Description: Enable service provided by daemon. 10 | ### END INIT INFO 11 | 12 | dir="{{ node_exporter_install_path }}/{{ node_exporter_filename }}/" 13 | cmd="./node_exporter" 14 | user="root" 15 | 16 | name="{{ node_exporter_service_name }}" 17 | pid_file="/var/run/$name.pid" 18 | stdout_log="{{ node_exporter_log_path }}/$name.log" 19 | stderr_log="{{ node_exporter_log_path }}/$name.err" 20 | 21 | get_pid() { 22 | cat "$pid_file" 23 | } 24 | 25 | is_running() { 26 | [ -f "$pid_file" ] && ps -p `get_pid` > /dev/null 2>&1 27 | } 28 | 29 | case "$1" in 30 | start) 31 | if is_running; then 32 | echo "Already started" 33 | else 34 | echo "Starting $name" 35 | cd "$dir" 36 | if [ -z "$user" ]; then 37 | sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & 38 | else 39 | sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & 40 | fi 41 | echo $! > "$pid_file" 42 | if ! is_running; then 43 | echo "Unable to start, see $stdout_log and $stderr_log" 44 | exit 1 45 | fi 46 | fi 47 | ;; 48 | stop) 49 | if is_running; then 50 | echo -n "Stopping $name.." 51 | kill `get_pid` 52 | for i in 1 2 3 4 5 6 7 8 9 10 53 | # for i in `seq 10` 54 | do 55 | if ! is_running; then 56 | break 57 | fi 58 | 59 | echo -n "." 60 | sleep 1 61 | done 62 | echo 63 | 64 | if is_running; then 65 | echo "Not stopped; may still be shutting down or shutdown may have failed" 66 | exit 1 67 | else 68 | echo "Stopped" 69 | if [ -f "$pid_file" ]; then 70 | rm "$pid_file" 71 | fi 72 | fi 73 | else 74 | echo "Not running" 75 | fi 76 | ;; 77 | restart) 78 | $0 stop 79 | if is_running; then 80 | echo "Unable to stop, will not attempt to start" 81 | exit 1 82 | fi 83 | $0 start 84 | ;; 85 | status) 86 | if is_running; then 87 | echo "Running" 88 | else 89 | echo "Stopped" 90 | exit 1 91 | fi 92 | ;; 93 | *) 94 | echo "Usage: $0 {start|stop|restart|status}" 95 | exit 1 96 | ;; 97 | esac 98 | 99 | exit 0 -------------------------------------------------------------------------------- /ansible/roles/prometheus/defaults/main.yml: -------------------------------------------------------------------------------- 1 | prometheus_download_url: https://github.com/prometheus/prometheus/releases/download/v2.23.0/prometheus-2.23.0.linux-armv7.tar.gz 2 | prometheus_filename: prometheus-2.23.0.linux-armv7 3 | prometheus_install_path: /opt/prometheus 4 | prometheus_service_name: prometheus 5 | prometheus_log_path: /var/log/prometheus 6 | -------------------------------------------------------------------------------- /ansible/roles/prometheus/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: update-rc.d prometheus 2 | become: true 3 | command: update-rc.d {{ prometheus_service_name }} defaults 4 | 5 | - name: restart prometheus 6 | become: true 7 | service: 8 | name: prometheus 9 | state: restarted 10 | 11 | -------------------------------------------------------------------------------- /ansible/roles/prometheus/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Creates directory Prometheus service 2 | become: true 3 | file: 4 | path: "{{ prometheus_install_path }}" 5 | state: directory 6 | owner: pi 7 | group: pi 8 | mode: 0775 9 | 10 | - name: Creates log directory 11 | become: true 12 | file: 13 | path: "{{ prometheus_log_path }}" 14 | state: directory 15 | owner: pi 16 | group: pi 17 | mode: 0775 18 | 19 | - stat: 20 | path: "{{ prometheus_install_path }}/{{ prometheus_filename }}" 21 | register: dest 22 | 23 | - name: Download and unarchive Prometheus package 24 | unarchive: 25 | src: "{{ prometheus_download_url }}" 26 | remote_src: True 27 | dest: "{{ prometheus_install_path }}/" 28 | when: not dest.stat.exists 29 | 30 | - name: Copy Prometheus config file 31 | template: 32 | src: prometheus.yml.j2 33 | dest: "{{ prometheus_install_path }}/{{ prometheus_filename }}/prometheus.yml" 34 | mode: 0755 35 | notify: restart prometheus 36 | 37 | - name: Copy Prometheus init script 38 | become: true 39 | template: 40 | src: prometheus.init.script.j2 41 | dest: /etc/init.d/{{ prometheus_service_name }} 42 | mode: 0755 43 | notify: 44 | - update-rc.d prometheus 45 | - restart prometheus 46 | 47 | - meta: flush_handlers 48 | 49 | - name: Start Prometheus 50 | become: true 51 | service: 52 | name: prometheus 53 | state: started 54 | -------------------------------------------------------------------------------- /ansible/roles/prometheus/templates/prometheus.init.script.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: prometheus 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Start daemon at boot time 9 | # Description: Enable service provided by daemon. 10 | ### END INIT INFO 11 | 12 | dir="{{ prometheus_install_path }}/{{ prometheus_filename }}/" 13 | cmd="./prometheus" 14 | user="pi" 15 | 16 | name="{{ prometheus_service_name }}" 17 | pid_file="/var/run/$name.pid" 18 | stdout_log="{{ prometheus_log_path }}/$name.log" 19 | stderr_log="{{ prometheus_log_path }}/$name.err" 20 | 21 | get_pid() { 22 | cat "$pid_file" 23 | } 24 | 25 | is_running() { 26 | [ -f "$pid_file" ] && ps -p `get_pid` > /dev/null 2>&1 27 | } 28 | 29 | case "$1" in 30 | start) 31 | if is_running; then 32 | echo "Already started" 33 | else 34 | echo "Starting $name" 35 | cd "$dir" 36 | if [ -z "$user" ]; then 37 | sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & 38 | else 39 | sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & 40 | fi 41 | echo $! > "$pid_file" 42 | if ! is_running; then 43 | echo "Unable to start, see $stdout_log and $stderr_log" 44 | exit 1 45 | fi 46 | fi 47 | ;; 48 | stop) 49 | if is_running; then 50 | echo -n "Stopping $name.." 51 | kill `get_pid` 52 | for i in 1 2 3 4 5 6 7 8 9 10 53 | # for i in `seq 10` 54 | do 55 | if ! is_running; then 56 | break 57 | fi 58 | 59 | echo -n "." 60 | sleep 1 61 | done 62 | echo 63 | 64 | if is_running; then 65 | echo "Not stopped; may still be shutting down or shutdown may have failed" 66 | exit 1 67 | else 68 | echo "Stopped" 69 | if [ -f "$pid_file" ]; then 70 | rm "$pid_file" 71 | fi 72 | fi 73 | else 74 | echo "Not running" 75 | fi 76 | ;; 77 | restart) 78 | $0 stop 79 | if is_running; then 80 | echo "Unable to stop, will not attempt to start" 81 | exit 1 82 | fi 83 | $0 start 84 | ;; 85 | status) 86 | if is_running; then 87 | echo "Running" 88 | else 89 | echo "Stopped" 90 | exit 1 91 | fi 92 | ;; 93 | *) 94 | echo "Usage: $0 {start|stop|restart|status}" 95 | exit 1 96 | ;; 97 | esac 98 | 99 | exit 0 -------------------------------------------------------------------------------- /ansible/roles/prometheus/templates/prometheus.yml.j2: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 30s 3 | evaluation_interval: 1m 4 | 5 | scrape_configs: 6 | 7 | - job_name: 'prometheus' 8 | static_configs: 9 | - targets: [ 10 | '{{ groups['main-raspberry'][0] }}:9090' 11 | ] 12 | 13 | - job_name: 'node_exporter' 14 | static_configs: 15 | - targets: [ 16 | {% for host in groups['all'] %} 17 | '{{ host }}:9100'{% if not loop.last %},{% endif %} 18 | 19 | {% endfor %} 20 | ] 21 | 22 | - job_name: 'rpi_exporter' 23 | static_configs: 24 | - targets: [ 25 | {% for host in groups['all'] %} 26 | '{{ host }}:9243'{% if not loop.last %},{% endif %} 27 | 28 | {% endfor %} 29 | ] 30 | -------------------------------------------------------------------------------- /ansible/roles/rpi-exporter/defaults/main.yml: -------------------------------------------------------------------------------- 1 | rpi_exporter_download_url: https://github.com/lukasmalkmus/rpi_exporter/releases/download/v0.6.0/rpi_exporter-0.6.0.linux-armv7.tar.gz 2 | rpi_exporter_filename: rpi_exporter-0.6.0.linux-armv7 3 | rpi_exporter_install_path: /opt/rpi-exporter 4 | rpi_exporter_service_name: rpi-exporter 5 | rpi_exporter_log_path: /var/log/rpi_exporter 6 | -------------------------------------------------------------------------------- /ansible/roles/rpi-exporter/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: update-rc.d rpi_exporter 2 | become: true 3 | command: update-rc.d {{ rpi_exporter_service_name }} defaults 4 | 5 | - name: restart rpi_exporter 6 | become: true 7 | service: 8 | name: rpi-exporter 9 | state: restarted 10 | 11 | -------------------------------------------------------------------------------- /ansible/roles/rpi-exporter/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Creates directory RPi exporter service 2 | become: true 3 | file: 4 | path: "{{ rpi_exporter_install_path }}" 5 | state: directory 6 | owner: pi 7 | group: pi 8 | mode: 0775 9 | 10 | - name: Creates RPi exporter log directory 11 | become: true 12 | file: 13 | path: "{{ rpi_exporter_log_path }}" 14 | state: directory 15 | owner: pi 16 | group: pi 17 | mode: 0775 18 | 19 | - stat: 20 | path: "{{ rpi_exporter_install_path }}/{{ rpi_exporter_filename }}" 21 | register: dest 22 | 23 | - name: Download and unarchive RPi exporter package 24 | unarchive: 25 | src: "{{ rpi_exporter_download_url }}" 26 | remote_src: True 27 | dest: "{{ rpi_exporter_install_path }}/" 28 | when: not dest.stat.exists 29 | 30 | - name: Copy RPi exporter init script 31 | become: true 32 | template: 33 | src: rpi.exporter.init.script.j2 34 | dest: /etc/init.d/{{ rpi_exporter_service_name }} 35 | mode: 0755 36 | notify: 37 | - update-rc.d rpi_exporter 38 | - restart rpi_exporter 39 | 40 | - meta: flush_handlers 41 | 42 | - name: Start RPi exporter 43 | become: true 44 | service: 45 | name: rpi-exporter 46 | state: started 47 | -------------------------------------------------------------------------------- /ansible/roles/rpi-exporter/templates/rpi.exporter.init.script.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: rpi-exporter 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Start daemon at boot time 9 | # Description: Enable service provided by daemon. 10 | ### END INIT INFO 11 | 12 | dir="{{ rpi_exporter_install_path }}/{{ rpi_exporter_filename }}/" 13 | cmd="./rpi_exporter" 14 | user="root" 15 | 16 | name="{{ rpi_exporter_service_name }}" 17 | pid_file="/var/run/$name.pid" 18 | stdout_log="{{ rpi_exporter_log_path }}/$name.log" 19 | stderr_log="{{ rpi_exporter_log_path }}/$name.err" 20 | 21 | get_pid() { 22 | cat "$pid_file" 23 | } 24 | 25 | is_running() { 26 | [ -f "$pid_file" ] && ps -p `get_pid` > /dev/null 2>&1 27 | } 28 | 29 | case "$1" in 30 | start) 31 | if is_running; then 32 | echo "Already started" 33 | else 34 | echo "Starting $name" 35 | cd "$dir" 36 | if [ -z "$user" ]; then 37 | sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & 38 | else 39 | sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & 40 | fi 41 | echo $! > "$pid_file" 42 | if ! is_running; then 43 | echo "Unable to start, see $stdout_log and $stderr_log" 44 | exit 1 45 | fi 46 | fi 47 | ;; 48 | stop) 49 | if is_running; then 50 | echo -n "Stopping $name.." 51 | kill `get_pid` 52 | for i in 1 2 3 4 5 6 7 8 9 10 53 | # for i in `seq 10` 54 | do 55 | if ! is_running; then 56 | break 57 | fi 58 | 59 | echo -n "." 60 | sleep 1 61 | done 62 | echo 63 | 64 | if is_running; then 65 | echo "Not stopped; may still be shutting down or shutdown may have failed" 66 | exit 1 67 | else 68 | echo "Stopped" 69 | if [ -f "$pid_file" ]; then 70 | rm "$pid_file" 71 | fi 72 | fi 73 | else 74 | echo "Not running" 75 | fi 76 | ;; 77 | restart) 78 | $0 stop 79 | if is_running; then 80 | echo "Unable to stop, will not attempt to start" 81 | exit 1 82 | fi 83 | $0 start 84 | ;; 85 | status) 86 | if is_running; then 87 | echo "Running" 88 | else 89 | echo "Stopped" 90 | exit 1 91 | fi 92 | ;; 93 | *) 94 | echo "Usage: $0 {start|stop|restart|status}" 95 | exit 1 96 | ;; 97 | esac 98 | 99 | exit 0 100 | -------------------------------------------------------------------------------- /preview/Screen_Shot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhemar/raspberry-metrics/7867d8f90250f02aa3ce431237d5a3f75c871b9d/preview/Screen_Shot.jpg -------------------------------------------------------------------------------- /preview/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhemar/raspberry-metrics/7867d8f90250f02aa3ce431237d5a3f75c871b9d/preview/dashboard.png -------------------------------------------------------------------------------- /preview/dataSource.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhemar/raspberry-metrics/7867d8f90250f02aa3ce431237d5a3f75c871b9d/preview/dataSource.jpg -------------------------------------------------------------------------------- /preview/instance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhemar/raspberry-metrics/7867d8f90250f02aa3ce431237d5a3f75c871b9d/preview/instance.jpg -------------------------------------------------------------------------------- /preview/raspberry_IP_config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhemar/raspberry-metrics/7867d8f90250f02aa3ce431237d5a3f75c871b9d/preview/raspberry_IP_config.jpg -------------------------------------------------------------------------------- /preview/scheme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhemar/raspberry-metrics/7867d8f90250f02aa3ce431237d5a3f75c871b9d/preview/scheme.jpg --------------------------------------------------------------------------------