Browse Source

initial commit

master
Sebastian Denz 4 years ago
commit
52ead63ca7
  1. 163
      Readme.md
  2. 732
      architecture.graphml
  3. BIN
      architecture.png
  4. 120
      devnotes.md
  5. 21
      docker-compose/LICENSE
  6. 352
      docker-compose/README.md
  7. 21
      docker-compose/alertmanager/config.yml
  8. 39
      docker-compose/caddy/Caddyfile
  9. 3
      docker-compose/config
  10. BIN
      docker-compose/db/ca.pem
  11. BIN
      docker-compose/db/client-cert.pem
  12. BIN
      docker-compose/db/public_key.pem
  13. BIN
      docker-compose/db/server-cert.pem
  14. 238
      docker-compose/docker-compose.yml
  15. 12
      docker-compose/grafana/provisioning/dashboards/dashboard.yml
  16. 1270
      docker-compose/grafana/provisioning/dashboards/docker_containers.json
  17. 1441
      docker-compose/grafana/provisioning/dashboards/docker_host.json
  18. 3412
      docker-compose/grafana/provisioning/dashboards/monitor_services.json
  19. 398
      docker-compose/grafana/provisioning/dashboards/nginx_container.json
  20. 11
      docker-compose/grafana/provisioning/datasources/datasource.yml
  21. 70
      docker-compose/prometheus/alert.rules
  22. 67
      docker-compose/prometheus/prometheus.yml
  23. BIN
      docker-compose/screens/Grafana_Docker_Containers.png
  24. BIN
      docker-compose/screens/Grafana_Docker_Host.png
  25. BIN
      docker-compose/screens/Grafana_Prometheus.png
  26. BIN
      docker-compose/screens/Slack_Notifications.png
  27. 21
      misc/bandswitcher.sh
  28. 9628
      misc/dashboards/prometheus.json
  29. 6
      misc/import_csv.sql
  30. BIN
      screenshots/screenshot.png

163
Readme.md

@ -0,0 +1,163 @@
# what is it?
![alt text](screenshot.png "Logo Title Text 1")
* a set of tools to export your personal WSJT-X
* live reception data into prometheus or mysql
* historical reception data (since 2019) into mysql
* a tool to poll pskreporter and export your personal
* live transmission data to prometheus or mysql
every time your wsjtx is/was running you are/were generating nice data points, which can be queried using grafana.
this can be very interesting to evaluate your **personal** hf conditions from the current and the past.
some use cases:
* choose dxcc, band, mode and timeframe in all combinations so see when your were successfully able to receive and transmit, to plan future activities
* compare input of different station like for antenna comparions
* have nice wallboard for contesting or dxpedition
* display rx/tx data on a map with custom timespan and filters
* compare hf propagration forecasts to your personal situation
* use grafana alerts or prometheus alertmanager to send yourself alerts when your stations receives something interesting
* impress other people with cool dashboards ;)
* ...
sample dashboards are provided.
have fun!
73 de DL3SD
## prerequisites
* ALL.TXT with interesting data and/or running hamradio station to provide live data
* grafana
* prometheus or mysql
## repository overview
* **pskreporter-exporter**
* polls pskreporter.info for your callsign
* supports prometheus and mysql
* **wsjtx-exporter**
* follows live traffice in ALL.txt
* supports prometheus and mysql
* **alltxt2csv**
* reads whole ALL.txt since 2019 and creates csv-file which can be imported into mysql
* **misc**
* sample dashboards
* sql table definitions
* shell script to hop between bands via rigctl to allow fractional monitoring of multiple bands
* ready to launch docker-compose setup based on https://github.com/stefanprodan/dockprom
## things you should be aware of..
### what about prometheus and mysql?
#### prometheus
pro
* you get nicer graphs with counters and bars if you ask me
* you can use alertmanager if you want advanced alerting
* optimized for timeseries data
con
* not designed for keeping long timeseries (weeks or some months are fine)
* not possible to import historical data
* not as flexible as mysql regarding complex queries
#### mysql
pro:
* you can import your ALL.txt since 2019
* you can store a lot of data over a long timeframe
* you can propably build more complex queries in sql
con:
* in comparison to prometheus not optimized for timeseries, but still good enough
* gauge based graphs are not as nice as counter/bars if you ask me
* you can use *only* graphanas internal alerting
both allow distributed setups with multiple wsjtx instances submitting their data to a central prometheus or mysql service.
you can as well run both in parallel and use prometheus for a live overview and mysql for historical evaluations.
### pskreporter-exporter vs other access/polling of pskreporter like GridTracker
### can it read my whole ALL.txt since from the beginning?
not really. only lines in 'new' format are accepted because the old format doesnt contain the frequency and the date.
lines in incompatible format are silently ignored.
with alltxt2csv you can prepare your ALL.TXT from ca. mid 2019 for import to mysql.
for prometheus it is not possible to import historical data for obivous reasons.
### there are a lot of "cant parse callsign messages" during importing my ALL.txt
thats quite normal as there are propably a lot of strange/broken messages in your ALL.txt ;)
### does it work with wsjt-x 2.3.0.RC2?
no, there is a bug which prevents the ALL.TXT to contain them timestamp, but it should be fixed in the next release.
### what is the state of this?
this works fine for me! but it was just build from boredom during covid lockdown and it is not to be considered as perfect.
performance obviously depends on your hardware and the complexity of your queries.
feedback and pull requests are highly appreciated!
here be dragons:
* callsign lookup performance done with regex in one goroutine is unneccessary slow for alltxt2csv
* grids for ALL.TXT entries are always looked up by dxcc instead if being tracked in the file
* there is a lot of space for improvements for the dashboards
### grafana says too many rows for mysql datasource
try choosing a bigger interval
### how long does it take to import my data into mysql?
* my ALL.TXT (new format start july 2019) contains ~ 13.7 mio lines and has ~ 850M
* converting to csv takes ~ 12min on i7-4750HQ (2015) and the result has ~ 1.2G
* currently this is done using a module which uses regular expressions which is not optimial for this use case
* importing the csv to mysql takes ~ 3.5min
* querying the whole time (~ 1.5 years) in grafana takes some seconds
### does this need a lot of ressource on my machine?
well, it depends on your queries, but propably yes! ;)
### when i count the decoded messages in wsjt-x manually it doesnt match the prometheus graphs
that is correct! due to the way prometheus get the results it doesnt know the corret timestamp of the event.
rounding/rating does the rest.
..in mysql it is correct!
## howtos
there is no special howto on integrating the components into your existing grafana/prometheus/mysql infrastructure,
as you should propably know how to deal with these kind of stuff.. ;)
### howto build binaries/containers
build binaries:
```
go get github.com/denzs/wsjtx_dashboards
```
build docker containers:
```
docker build Dockerfile.wsjtx-exporter .
docker build Dockerfile.pskreporter-exporter .
```
### to be done...
debian/win10@wsl2 + docker-compose (historical data with mysql + alltxt2csv)
raspbian + docker-compose (live data with prometheus only)
alerting
## Kudos to
* WSJT-X, Grafana, Prometheus, MySQL Teams
* Philip Gladstone for pskreporter.info
* Todd Neal for his nice work on https://github.com/tzneal/ham-go
* Stefan Prodan for his nice work on https://github.com/stefanprodan/dockprom

732
architecture.graphml

@ -0,0 +1,732 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.20.1-->
<key attr.name="Beschreibung" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key for="graphml" id="d7" yfiles.type="resources"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d0"/>
<node id="n0" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="554.921875" width="941.2936507936508" x="0.0" y="338.70656967163086"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="2.0"/>
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="941.2936507936508" x="0.0" xml:space="preserve" y="0.0">. linux server</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="15" bottomF="15.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="567.0" y="321.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="50.0" x="0.0" xml:space="preserve" y="0.0">1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n0:">
<node id="n0::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="880.7560430263475" x="45.537454677302435" y="375.16750717163086"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.263671875" x="421.74618557567374" xml:space="preserve" y="6.015625">nginx<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="412.4609375" width="771.2936507936508" x="15.0" y="451.16750717163086"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="2.0"/>
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="771.2936507936508" x="0.0" xml:space="preserve" y="0.0">. docker-compose</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="749.0" y="611.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="50.0" x="0.0" xml:space="preserve" y="0.0">1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n0::n1:">
<node id="n0::n1::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="180.0" x="496.04365079365084" y="487.62844467163086"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="50.44140625" x="64.779296875" xml:space="preserve" y="6.015625">grafana<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="147.0" x="401.7936507936508" y="738.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="39.994140625" x="53.5029296875" xml:space="preserve" y="6.015625">mysql<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1::n2">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="40.0" width="100.0" x="671.2936507936508" y="564.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="74.21875" x="12.890625" xml:space="preserve" y="11.015625">vol_grafana<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n0::n1::n3">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="147.0" x="397.7936507936508" y="569.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.515625" x="35.2421875" xml:space="preserve" y="6.015625">prometheus<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1::n4">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="180.0" x="30.0" y="651.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="90.935546875" x="44.5322265625" xml:space="preserve" y="6.015625">wsjtx-exporter<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1::n5">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="180.0" x="430.29365079365084" y="651.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="129.77734375" x="25.111328125" xml:space="preserve" y="6.015625">pskreporter-exporter<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n0::n1::n6">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="40.0" width="100.0" x="270.1468253968254" y="646.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="100.29296875" x="-0.146484375" xml:space="preserve" y="11.015625">vol_prometheus<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n0::n1::n7">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="40.0" width="100.0" x="425.2936507936508" y="808.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.771484375" x="18.1142578125" xml:space="preserve" y="11.015625">vol_mysql<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
</y:GenericNode>
</data>
</node>
</graph>
</node>
<node id="n0::n2">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.document">
<y:Geometry height="50.0" width="80.0" x="846.2936507936508" y="477.62844467163086"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.74609375" x="11.626953125" xml:space="preserve" y="16.015625">htaccess<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
</y:GenericNode>
</data>
</node>
</graph>
</node>
<node id="n1">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.cloud">
<y:Geometry height="50.0" width="100.0" x="1001.2936507936508" y="728.6284446716309"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="75.953125" x="12.0234375" xml:space="preserve" y="16.015625">PSKreporter<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n2" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="191.70656967163086" width="285.0" x="368.16547619047617" y="86.0"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="2.0"/>
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="285.0" x="0.0" xml:space="preserve" y="0.0">. station</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="589.5" y="-245.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="50.0" x="0.0" xml:space="preserve" y="0.0">1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n2:">
<node id="n2::n0">
<data key="d5"/>
<data key="d6">
<y:SVGNode>
<y:Geometry height="53.24563217163086" width="65.0260009765625" x="453.4024757021949" y="122.4609375"/>
<y:Fill color="#CCCCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="30.51300048828125" y="57.24563217163086">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="-0.5" nodeRatioX="0.0" nodeRatioY="0.5" offsetX="0.0" offsetY="4.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:SVGNodeProperties usingVisualBounds="true"/>
<y:SVGModel svgBoundsPolicy="0">
<y:SVGContent refid="1"/>
</y:SVGModel>
</y:SVGNode>
</data>
</node>
<node id="n2::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="147.0" x="491.16547619047617" y="215.70656967163086"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="66.80078125" x="40.099609375" xml:space="preserve" y="6.015625">alltxt2http<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2::n2">
<data key="d5"/>
<data key="d6">
<y:ImageNode>
<y:Geometry height="48.0" width="48.0" x="383.16547619047617" y="206.70656967163086"/>
<y:Fill color="#CCCCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="s" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="22.0" y="52.0"/>
<y:Image alphaImage="true" refid="2"/>
</y:ImageNode>
</data>
</node>
</graph>
</node>
<node id="n3">
<data key="d5"/>
<data key="d6">
<y:ImageNode>
<y:Geometry height="48.0" width="48.0" x="461.91547619047617" y="0.0"/>
<y:Fill color="#CCCCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="s" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="22.0" y="52.0"/>
<y:Image alphaImage="true" refid="3"/>
</y:ImageNode>
</data>
</node>
<edge id="n0::n1::e0" source="n0::n1::n1" target="n0::n1::n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="15.0" tx="0.0" ty="-20.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e1" source="n0::n1::n3" target="n0::n1::n6">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="15.0" tx="0.0" ty="-20.0">
<y:Point x="471.2936507936508" y="631.1284446716309"/>
<y:Point x="320.1468253968254" y="631.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e2" source="n0::n1::n0" target="n0::n1::n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="60.0" sy="15.0" tx="0.0" ty="-20.0">
<y:Point x="646.0436507936508" y="533.1284446716309"/>
<y:Point x="721.2936507936508" y="533.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e0" source="n0::n0" target="n0::n1::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="15.0" tx="0.0" ty="-15.0">
<y:Point x="485.91547619047617" y="420.66750717163086"/>
<y:Point x="586.0436507936508" y="420.66750717163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e1" source="n0::n0" target="n0::n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="293.58534767544916" sy="15.0" tx="0.0" ty="-25.0">
<y:Point x="779.5008238659253" y="420.66750717163086"/>
<y:Point x="886.2936507936508" y="420.66750717163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e0" source="n0::n1::n5" target="n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="45.0" sy="15.0" tx="0.0" ty="-25.0">
<y:Point x="565.2936507936508" y="713.1284446716309"/>
<y:Point x="1051.2936507936508" y="713.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n2::e0" source="n2::n0" target="n2::n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-16.256500244140625" sy="26.62281608581543" tx="0.0" ty="-24.0">
<y:Point x="469.65897594633554" y="191.20656967163086"/>
<y:Point x="407.16547619047617" y="191.20656967163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n2::e1" source="n2::n0" target="n2::n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="16.256500244140625" sy="26.62281608581543" tx="0.0" ty="-15.0">
<y:Point x="502.1719764346168" y="191.20656967163086"/>
<y:Point x="564.6654761904762" y="191.20656967163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n3" target="n2::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="24.0" tx="0.0" ty="-26.62281608581543"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e3" source="n0::n1::n0" target="n0::n1::n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-60.0" sy="15.0" tx="0.0" ty="-15.0">
<y:Point x="526.0436507936508" y="533.1284446716309"/>
<y:Point x="471.2936507936508" y="533.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e4" source="n0::n1::n5" target="n0::n1::n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-45.0" sy="15.0" tx="0.0" ty="-15.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e5" source="n0::n1::n3" target="n0::n1::n4">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-49.0" sy="15.0" tx="45.0" ty="-15.0">
<y:Point x="422.2936507936508" y="615.1284446716309"/>
<y:Point x="165.0" y="615.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e6" source="n0::n1::n3" target="n0::n1::n5">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="49.0" sy="15.0" tx="0.0" ty="-15.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e7" source="n0::n1::n4" target="n0::n1::n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="15.0" tx="-49.0" ty="-15.0">
<y:Point x="120.0" y="702.1284446716309"/>
<y:Point x="426.2936507936508" y="702.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::n1::e8" source="n0::n1::n0" target="n0::n1::n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="15.0" tx="49.00000000000006" ty="-15.0">
<y:Point x="586.0436507936508" y="549.1284446716309"/>
<y:Point x="640.7936507936508" y="549.1284446716309"/>
<y:Point x="640.7936507936508" y="697.1284446716309"/>
<y:Point x="524.2936507936508" y="697.1284446716309"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n2::n2" target="n0::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="24.0" tx="-220.18901075658687" ty="-15.0">
<y:Point x="407.16547619047617" y="308.20656967163086"/>
<y:Point x="265.7264654338893" y="308.20656967163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="75.49609375" x="-179.18704774970445" xml:space="preserve" y="46.3665149944602">use grafana<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="0.0" distance="10.0" distanceToCenter="false" position="center" ratio="0.034937974866856535" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n0::e2" source="n0::n0" target="n0::n1::n4">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-293.58534767544916" sy="15.0" tx="-45.0" ty="-15.0">
<y:Point x="192.330128515027" y="420.66750717163086"/>
<y:Point x="75.0" y="420.66750717163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n2::n1" target="n0::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="15.0" tx="220.18901075658687" ty="-15.0">
<y:Point x="564.6654761904762" y="308.20656967163086"/>
<y:Point x="706.104486947063" y="308.20656967163086"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.923828125" x="-61.96190418061758" xml:space="preserve" y="29.257808685302734">push data<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources>
<y:Resource id="1" xml:space="preserve">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" width="65px" height="53px" viewBox="-0.811 -0.063 65 53" enable-background="new -0.811 -0.063 65 53"
xml:space="preserve"&gt;
&lt;defs&gt;
&lt;/defs&gt;
&lt;linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="220.9624" y1="824.415" x2="220.9624" y2="801.0922" gradientTransform="matrix(1 0 0 1 -195.2002 -770.8008)"&gt;
&lt;stop offset="0.0319" style="stop-color:#808080"/&gt;
&lt;stop offset="0.1229" style="stop-color:#939393"/&gt;
&lt;stop offset="0.2702" style="stop-color:#ABABAB"/&gt;
&lt;stop offset="0.4266" style="stop-color:#BCBCBC"/&gt;
&lt;stop offset="0.5968" style="stop-color:#C6C6C6"/&gt;
&lt;stop offset="0.8061" style="stop-color:#C9C9C9"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_1_)" d="M51.333,51.918c0.195,0.459-0.053,0.836-0.553,0.836H0.58c-0.5,0-0.604-0.272-0.232-0.605
l8.023-7.191c0.373-0.334,1.086-0.605,1.586-0.605h37.255c0.498,0,1.065,0.377,1.265,0.836L51.333,51.918z"/&gt;
&lt;path fill="none" stroke="#8D8D8D" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M51.333,51.918c0.195,0.459-0.053,0.836-0.553,0.836H0.58c-0.5,0-0.604-0.272-0.232-0.605l8.023-7.191
c0.373-0.334,1.086-0.605,1.586-0.605h37.255c0.498,0,1.065,0.377,1.265,0.836L51.333,51.918z"/&gt;
&lt;path fill="#717171" d="M32.117,50.571c0,0.25-0.205,0.454-0.455,0.454H4.024c-0.25,0-0.304-0.139-0.119-0.307l5.638-5.154
c0.184-0.17,0.539-0.309,0.789-0.309h21.332c0.25,0,0.454,0.205,0.454,0.455L32.117,50.571L32.117,50.571z"/&gt;
&lt;path fill="#717171" d="M40.738,50.598c0.086,0.236,0.359,0.428,0.609,0.428h7.465c0.25,0,0.375-0.188,0.279-0.42l-2.049-4.93
c-0.098-0.229-0.379-0.42-0.629-0.42h-7.17c-0.25,0-0.386,0.191-0.301,0.428L40.738,50.598z"/&gt;
&lt;path fill="#717171" d="M32.89,50.571c0,0.25,0.205,0.454,0.455,0.454h6.135c0.25,0,0.382-0.189,0.293-0.426l-0.156-0.409
c-0.089-0.233-0.365-0.421-0.615-0.416l-1.045,0.021c-0.25,0.004-0.509-0.189-0.574-0.432l-0.021-0.082
c-0.065-0.242-0.321-0.439-0.571-0.439h-1.316c-0.25,0-0.444,0.205-0.432,0.453l0.002,0.059c0.016,0.25-0.181,0.455-0.431,0.459
l-1.269,0.021c-0.25,0.006-0.454,0.211-0.454,0.461L32.89,50.571L32.89,50.571z"/&gt;
&lt;path fill="#717171" d="M32.89,47.004c0,0.25,0.205,0.455,0.455,0.455h4.845c0.25,0,0.396-0.195,0.323-0.437l-0.402-1.333
c-0.07-0.238-0.335-0.438-0.585-0.438h-4.181c-0.25,0-0.455,0.205-0.455,0.455V47.004z"/&gt;
&lt;linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="535.2017" y1="-1418.6563" x2="511.4634" y2="-1418.6563" gradientTransform="matrix(1 0 0 -1 -488 -1376.627)"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_2_)" d="M47.048,40.514c0,0.965-6.758,1.404-12.371,1.404c-3.889,0-10.914-0.348-11.367-1.267
c0,0.446,0,1.502,0,1.661c0,0.725,4.803,1.234,11.357,1.234c6.554,0,12.381-0.643,12.381-1.361
C47.048,42.028,47.048,40.977,47.048,40.514z"/&gt;
&lt;path fill="#808080" d="M35.179,39.307c6.556,0,11.869,0.584,11.869,1.307c0,0.721-5.313,1.42-11.869,1.42
c-6.701,0-11.869-0.697-11.869-1.42S28.625,39.307,35.179,39.307z"/&gt;
&lt;linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="525.7661" y1="-1412.6865" x2="520.77" y2="-1412.6865" gradientTransform="matrix(1 0 0 -1 -488 -1376.627)"&gt;
&lt;stop offset="0" style="stop-color:#999999"/&gt;
&lt;stop offset="0.0417" style="stop-color:#8D8D8D"/&gt;
&lt;stop offset="0.1617" style="stop-color:#717171"/&gt;
&lt;stop offset="0.2821" style="stop-color:#5D5D5D"/&gt;
&lt;stop offset="0.4021" style="stop-color:#515151"/&gt;
&lt;stop offset="0.5212" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="0.6202" style="stop-color:#565656"/&gt;
&lt;stop offset="0.7817" style="stop-color:#6E6E6E"/&gt;
&lt;stop offset="0.9844" style="stop-color:#969696"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_3_)" d="M37.734,40.896c0,0-1.477,0.096-2.498,0.096s-2.499-0.096-2.499-0.096v-9.768h4.997V40.896z"/&gt;
&lt;radialGradient id="SVGID_4_" cx="415.8687" cy="-1386.5146" r="24.0778" gradientTransform="matrix(1.15 0 0 -1 -453.4719 -1376.627)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#F2F2F2"/&gt;
&lt;stop offset="1" style="stop-color:#666666"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_4_)" d="M9.453,2.122c0-1.1,0.9-2,2-2h48.246c1.1,0,2,0.9,2,2v30.073c0,1.101-0.9,2-2,2H11.453
c-1.1,0-2-0.899-2-2V2.122z"/&gt;
&lt;path fill="none" stroke="#666666" stroke-width="0.2436" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M9.453,2.122c0-1.1,0.9-2,2-2h48.246c1.1,0,2,0.9,2,2v30.073c0,1.101-0.9,2-2,2H11.453c-1.1,0-2-0.899-2-2V2.122z"/&gt;
&lt;radialGradient id="SVGID_5_" cx="402.1509" cy="-1378.3662" r="53.3339" fx="444.1083" fy="-1385.5538" gradientTransform="matrix(1.1935 0 0 -1 -443.5655 -1376.627)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_5_)" d="M10.475,3.143c0-1.1,0.9-2,2-2h46.429c1.1,0,2,0.9,2,2v27.805c0,1.1-0.9,2-2,2H12.475
c-1.1,0-2-0.9-2-2V3.143z"/&gt;
&lt;radialGradient id="SVGID_6_" cx="402.939" cy="-1378.4502" r="34.1874" gradientTransform="matrix(1.1923 0 0 -1 -443.8286 -1376.627)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#9CD7FF"/&gt;
&lt;stop offset="1" style="stop-color:#3C89C9"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_6_)" d="M11.043,3.598c0-1.1,0.9-2,2-2h45.294c1.1,0,2,0.9,2,2v26.895c0,1.1-0.9,2-2,2H13.043
c-1.1,0-2-0.9-2-2V3.598z"/&gt;
&lt;path opacity="0.24" fill="#F2F2F2" d="M11.043,24.936V3.598c0-1.1,0.9-2,2-2h45.294c1.1,0,2,0.9,2,2v13.539l-23.164,4.94
c-1.064,0.273-2.836,0.547-3.935,0.609L11.043,24.936z"/&gt;
&lt;path fill="#C9C9C9" d="M58.777,46.596c-0.003-0.002-0.005-0.002-0.007,0c-0.188-0.061-0.429-0.254-0.702-0.482
C58.335,46.268,58.578,46.436,58.777,46.596z"/&gt;
&lt;radialGradient id="SVGID_7_" cx="450.8638" cy="1259.1514" r="6.3766" gradientTransform="matrix(1 0 0 1 -390.4004 -1211.6016)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0.1939" style="stop-color:#C9C9C9"/&gt;
&lt;stop offset="0.3299" style="stop-color:#C6C6C6"/&gt;
&lt;stop offset="0.4405" style="stop-color:#BCBCBC"/&gt;
&lt;stop offset="0.5421" style="stop-color:#ABABAB"/&gt;
&lt;stop offset="0.6378" style="stop-color:#939393"/&gt;
&lt;stop offset="0.697" style="stop-color:#808080"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_7_)" d="M58.77,46.596c0.005,0.002,0.007,0.002,0.009,0.002c0.002,0.004,0.006,0.004,0.006,0.004
c0.084,0.023,0.153,0.021,0.213-0.021c0.017-0.008,0.026-0.021,0.037-0.041c0.604-0.119,1.329-0.154,2.197-0.086
c2.032,1.545,3.77,4.625,1.18,5.801c-2.048,0.929-3.543,0.783-4.722-0.485c-0.624-0.675-1.239-1.47-1.729-2.265
C56.226,48.369,56.855,47.061,58.77,46.596z"/&gt;
&lt;radialGradient id="SVGID_8_" cx="603.5698" cy="1426.6348" r="3.8245" gradientTransform="matrix(0.9761 0.2173 -0.1478 0.6641 -320.0412 -1031.1759)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0.1939" style="stop-color:#C9C9C9"/&gt;
&lt;stop offset="0.3739" style="stop-color:#C6C6C6"/&gt;
&lt;stop offset="0.5203" style="stop-color:#BCBCBC"/&gt;
&lt;stop offset="0.6549" style="stop-color:#ABABAB"/&gt;
&lt;stop offset="0.7816" style="stop-color:#939494"/&gt;
&lt;stop offset="0.8364" style="stop-color:#868787"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_8_)" d="M55.96,49.504c-0.893-1.438-1.355-2.869-0.664-3.533c0.244-0.236,0.539-0.422,0.863-0.559
c0.6,0.051,1.307,0.346,1.9,0.691c0.002,0,0.005,0.004,0.007,0.006c0.272,0.229,0.519,0.426,0.702,0.482
C56.855,47.061,56.226,48.369,55.96,49.504z"/&gt;
&lt;radialGradient id="SVGID_9_" cx="448.7241" cy="1259.3271" r="3.928" gradientTransform="matrix(1 0 0 1 -390.4004 -1211.6016)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0.1939" style="stop-color:#C9C9C9"/&gt;
&lt;stop offset="0.3496" style="stop-color:#C6C6C6"/&gt;
&lt;stop offset="0.4761" style="stop-color:#BCBCBC"/&gt;
&lt;stop offset="0.5925" style="stop-color:#ABABAB"/&gt;
&lt;stop offset="0.702" style="stop-color:#939393"/&gt;
&lt;stop offset="0.7697" style="stop-color:#808080"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_9_)" d="M56.16,45.414c1.353-0.564,3.287-0.266,4.963,0.955c0.035,0.025,0.073,0.055,0.109,0.084
c-0.867-0.068-1.818-0.033-2.427,0.086c-0.109-0.105-0.763-0.449-0.744-0.434C57.464,45.758,56.757,45.463,56.16,45.414z"/&gt;
&lt;path fill="none" stroke="#717171" stroke-width="0.1136" stroke-linecap="round" stroke-miterlimit="10" d="M56.16,45.414
c1.353-0.564,3.287-0.266,4.963,0.955c0.035,0.025,0.073,0.055,0.109,0.084c2.032,1.545,3.77,4.627,1.18,5.801
c-2.048,0.93-3.543,0.785-4.722-0.484c-0.624-0.676-1.239-1.471-1.729-2.264c-0.892-1.438-1.354-2.871-0.664-3.533
C55.541,45.734,55.835,45.549,56.16,45.414z"/&gt;
&lt;path fill="none" stroke="#717171" stroke-width="0.1136" stroke-linecap="round" stroke-miterlimit="10" d="M58.777,46.596
c0.083-0.021,0.168-0.041,0.258-0.057c0.604-0.119,1.329-0.154,2.197-0.086"/&gt;
&lt;path fill="none" stroke="#717171" stroke-width="0.1136" stroke-linecap="round" stroke-miterlimit="10" d="M55.958,49.516
c0-0.002,0.002-0.008,0.002-0.012c0.269-1.135,0.896-2.441,2.813-2.908"/&gt;
&lt;path fill="none" stroke="#717171" stroke-width="0.1136" stroke-linecap="round" stroke-miterlimit="10" d="M58.061,46.105
c-0.597-0.35-1.306-0.643-1.901-0.693"/&gt;
&lt;path fill="none" stroke="#717171" stroke-width="0.1136" stroke-linecap="round" stroke-miterlimit="10" d="M58.779,46.598
c0-0.002,0-0.002-0.002-0.002c-0.199-0.16-0.441-0.328-0.709-0.482"/&gt;
&lt;path fill="#4D4D4D" d="M58.259,45.936c0.354,0.264,0.438,0.48,0.548,0.604c-0.091,0.018-0.173,0.033-0.259,0.059
c-0.355-0.393-0.996-0.666-0.955-0.727C57.688,45.729,58.063,45.791,58.259,45.936z"/&gt;
&lt;/svg&gt;
</y:Resource>
<y:Resource id="2" type="java.awt.image.BufferedImage" xml:space="preserve">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</y:Resource>
<y:Resource id="3" type="java.awt.image.BufferedImage" xml:space="preserve">iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAC8ElEQVR4Xu2Xy2vUUBTGx/oAEcV3&#13;
a807t05Lcm87jqLu6h8griq+QFHEtRs37twoioqCSle6dScIiqIoRTeCG3FbRFpBN660U5Sq3xWD&#13;
ky/pzHRsJynkBwem93zn3O9MkttMqVSQMZYvRxG/FmmMloIgWIEPr1KSOQ/1Unv/cxUcJ+jB4sek&#13;
KLcxqT3HbiWjT+1CYjpFnLeY1l5j5iNsEZ5MKbjDulbgPpxvBdTd5T62F55gXQwMcYuLEGdZ1wzu&#13;
wflmmL46xz1g/ibrElSr1eUQj1HxjCnkftY2gjfnfCMsER5AzU/qMaa9sTYV1w27UTARa+DJr05f&#13;
MMTa2Wh3AEeo3dDXqH7C99Vm1jbE9IKdaY1se2ALa9NoZwDHGXKg/Uy1NdMLd7C2JUxfHmcjiNeG&#13;
sWclaxmu4zwjhFgD3Tuuw3l/jLVzAk1uJJvKe0gtYW09XMP5GMPDy3CLPuYamL/O0rmD5rYvX3Bz&#13;
XJ3zLK2H9ZyvB0Zvsx79n+u9WdsWPaKyCQ0/8Ca2Jw+zNoK1nI/A6XaGtXovvSdr/wtbqO1oPkWb&#13;
1fSpwVoNm+K8xhJqH3IzpJ3Se7F2XsD5fJSNIT657oCd0DYZwBaDFX00sw5X9Qhr5xVLyGu8KeJt&#13;
uVxeHdM1GMAsV3qxNskamL9ar1sgRpZis2e8OW6HB0h2RSrOR+vdSq3C3284j3iqe0e6BaV3W3Uj&#13;
NnzPJvANXok0nPu73IXP9zmne23t798Q1XYE/VphJR9qHad0ntf1mh6Q1xHfXDcYjHfvEKanDqUY&#13;
+m77wd6U9dMpazgy1UHu21Fg4DKbQnxJWfuRXAsvcb8MwEMt5JOkuSaBV4eOPbTNMIxgPUyNJ0zO&#13;
HuOWJddxn0wx3FCl/WNKBDSGJyXX5wLcSiMJwxzQcF2ugMGLCdP/zF9gfR7pwuv3IzaPN8yHOsfi&#13;
XKIfUB4APxfXsi7X8ACczz3FAFlTDJA1xQBZUwyQNcUAWVMMkDXFAFmz6AcoaJHfCrEzh4NQv0oA&#13;
AAAASUVORK5CYII=</y:Resource>
</y:Resources>
</data>
</graphml>

BIN
architecture.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

120
devnotes.md

@ -0,0 +1,120 @@
# assumptions/ideas
* keep client as stupid/uncomplicated as possible
* noch parsing/resolving, just postition finding
# before release
* multiple repos?
* alltxt2http & http-exporter (inkl. shared/httpstuff und shared/wsjtx)
* pskreporter-exporter
* dashboards n docs
* alltxt2csv kann weg oder?
* resolving lokal nur bedingt sinnvoll
* profitiert nicht von ggf. vorhandenen grids im cache
* wenn station und identifier bekannt sind, kann das auch der admin tun
* think about:
* logische trennung in zeilenverarbeitung und 'spotting' (spot in mysql&prometheus exportieren)
* take line -> parse line -> resolve grid and lotw for line ?
* http-exporter
* errorhandling
* db down
* who keeps track of the missing lines???
* lookup error (not an error)
* add instance field to mysql
* what happes if no instance is provided?
* improve logging
* counter vs histogram in prometheus?
* http 200 OK synchron warten lassen bis alles fertig ist?
* scheint tendenziell eine gute idee zu sein.. wenn db problem hat, kann client aufhoeren..
* pskreporter-exporter
* implement multi callsign support
* test different queries vs rate limit
* timeout und errorhandling?
* steht wenn offline sehr lange bevor er stirbt
* alltxt2http
* what about readall/batchmode?
* systemd restart problematik (failed too fast oder soo ;)
* Username=Station (=~ Callsign) right?!?
* call und password in htacces und fertig!
* auth. user kann seine instance selber waehlen
* kollisionen ausgeschlossen ;)
* wsjtx/http-exporter kombinieren?
* http oder file input?
* fix FIXMEs
* provide dashboards to grafana
* vendoring
* add howto for ubuntu/win10
* push images to dockerhub
* doc
* german docs..
* fix single tool readmes
* hints for using grafana
* intervals
* fold row which are not needed
* use filters!
* server und/oder skript/readme zum aufsetzen
# later
* http-exporter
* alltxt2http
* smarter move um sich als windows dienst einzutragen
* dashboards
* build combined rx/tx panels (at least map and diagram.. maybe lotw/cqrlog) :)
* research how to reduce result set effectively in mysql
* primary key um band ergaenzen?
* maybe add https://github.com/grafana/grafana/issues/8341 ?
* endpoint
* no db -> client should wait!
* add cache!
* store grids
* lookup qrz
* lookup lotw
* mod_gzip nach vorne raus?
* alltxt2http support?
* nginx config
* LOGBOOK
* integrate cqrlog db
* make some awesome queries!! :D
* userfriendly cqrlog backup import
* upload folder via webdav via user auth erreichbar
* logbook import job via systemd timer oder event oder so?
* create table aus binaries nehmen?
* kontrolle sollte beim db admin liegen?
* liegt sie ja auch weiterhin aufgrund von permissions ;)
* der client kann es ja versuchen.. wenn er genug rechte hat, sprich ja nichts dagegen!
* dafuer check ob table vorhanden!
* prometheus timestamp feature researchen
* fix dashboards
* umgang mit refresh der variablen??
* Mail an PSKReporter
* Query to bunde multiple callsigns?
* How are the queries counted? rate per src ip or per query?

21
docker-compose/LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Stefan Prodan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

352
docker-compose/README.md

@ -0,0 +1,352 @@
dockprom
========
A monitoring solution for Docker hosts and containers with [Prometheus](https://prometheus.io/), [Grafana](http://grafana.org/), [cAdvisor](https://github.com/google/cadvisor),
[NodeExporter](https://github.com/prometheus/node_exporter) and alerting with [AlertManager](https://github.com/prometheus/alertmanager).
***If you're looking for the Docker Swarm version please go to [stefanprodan/swarmprom](https://github.com/stefanprodan/swarmprom)***
## Install
Clone this repository on your Docker host, cd into dockprom directory and run compose up:
```bash
git clone https://github.com/stefanprodan/dockprom
cd dockprom
ADMIN_USER=admin ADMIN_PASSWORD=admin docker-compose up -d
```
Prerequisites:
* Docker Engine >= 1.13
* Docker Compose >= 1.11
Containers:
* Prometheus (metrics database) `http://<host-ip>:9090`
* Prometheus-Pushgateway (push acceptor for ephemeral and batch jobs) `http://<host-ip>:9091`
* AlertManager (alerts management) `http://<host-ip>:9093`
* Grafana (visualize metrics) `http://<host-ip>:3000`
* NodeExporter (host metrics collector)
* cAdvisor (containers metrics collector)
* Caddy (reverse proxy and basic auth provider for prometheus and alertmanager)
## Setup Grafana
Navigate to `http://<host-ip>:3000` and login with user ***admin*** password ***admin***. You can change the credentials in the compose file or by supplying the `ADMIN_USER` and `ADMIN_PASSWORD` environment variables on compose up. The config file can be added directly in grafana part like this
```
grafana:
image: grafana/grafana:7.2.0
env_file:
- config
```
and the config file format should have this content
```
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=changeme
GF_USERS_ALLOW_SIGN_UP=false
```
If you want to change the password, you have to remove this entry, otherwise the change will not take effect
```
- grafana_data:/var/lib/grafana
```
Grafana is preconfigured with dashboards and Prometheus as the default data source:
* Name: Prometheus
* Type: Prometheus
* Url: http://prometheus:9090
* Access: proxy
***Docker Host Dashboard***
![Host](https://raw.githubusercontent.com/stefanprodan/dockprom/master/screens/Grafana_Docker_Host.png)
The Docker Host Dashboard shows key metrics for monitoring the resource usage of your server:
* Server uptime, CPU idle percent, number of CPU cores, available memory, swap and storage
* System load average graph, running and blocked by IO processes graph, interrupts graph
* CPU usage graph by mode (guest, idle, iowait, irq, nice, softirq, steal, system, user)
* Memory usage graph by distribution (used, free, buffers, cached)
* IO usage graph (read Bps, read Bps and IO time)
* Network usage graph by device (inbound Bps, Outbound Bps)
* Swap usage and activity graphs
For storage and particularly Free Storage graph, you have to specify the fstype in grafana graph request.
You can find it in `grafana/dashboards/docker_host.json`, at line 480 :
"expr": "sum(node_filesystem_free_bytes{fstype=\"btrfs\"})",
I work on BTRFS, so i need to change `aufs` to `btrfs`.
You can find right value for your system in Prometheus `http://<host-ip>:9090` launching this request :
node_filesystem_free_bytes
***Docker Containers Dashboard***
![Containers](https://raw.githubusercontent.com/stefanprodan/dockprom/master/screens/Grafana_Docker_Containers.png)
The Docker Containers Dashboard shows key metrics for monitoring running containers:
* Total containers CPU load, memory and storage usage
* Running containers graph, system load graph, IO usage graph
* Container CPU usage graph
* Container memory usage graph
* Container cached memory usage graph
* Container network inbound usage graph
* Container network outbound usage graph
Note that this dashboard doesn't show the containers that are part of the monitoring stack.
***Monitor Services Dashboard***
![Monitor Services](https://raw.githubusercontent.com/stefanprodan/dockprom/master/screens/Grafana_Prometheus.png)
The Monitor Services Dashboard shows key metrics for monitoring the containers that make up the monitoring stack:
* Prometheus container uptime, monitoring stack total memory usage, Prometheus local storage memory chunks and series
* Container CPU usage graph
* Container memory usage graph
* Prometheus chunks to persist and persistence urgency graphs
* Prometheus chunks ops and checkpoint duration graphs
* Prometheus samples ingested rate, target scrapes and scrape duration graphs
* Prometheus HTTP requests graph
* Prometheus alerts graph
## Define alerts
Three alert groups have been setup within the [alert.rules](https://github.com/stefanprodan/dockprom/blob/master/prometheus/alert.rules) configuration file:
* Monitoring services alerts [targets](https://github.com/stefanprodan/dockprom/blob/master/prometheus/alert.rules#L2-L11)
* Docker Host alerts [host](https://github.com/stefanprodan/dockprom/blob/master/prometheus/alert.rules#L13-L40)
* Docker Containers alerts [containers](https://github.com/stefanprodan/dockprom/blob/master/prometheus/alert.rules#L42-L69)
You can modify the alert rules and reload them by making a HTTP POST call to Prometheus:
```
curl -X POST http://admin:admin@<host-ip>:9090/-/reload
```
***Monitoring services alerts***
Trigger an alert if any of the monitoring targets (node-exporter and cAdvisor) are down for more than 30 seconds:
```yaml
- alert: monitor_service_down
expr: up == 0
for: 30s
labels:
severity: critical
annotations:
summary: "Monitor service non-operational"
description: "Service {{ $labels.instance }} is down."
```
***Docker Host alerts***
Trigger an alert if the Docker host CPU is under high load for more than 30 seconds:
```yaml
- alert: high_cpu_load
expr: node_load1 > 1.5
for: 30s
labels:
severity: warning
annotations:
summary: "Server under high load"
description: "Docker host is under high load, the avg load 1m is at {{ $value}}. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
```
Modify the load threshold based on your CPU cores.
Trigger an alert if the Docker host memory is almost full:
```yaml
- alert: high_memory_load
expr: (sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) ) / sum(node_memory_MemTotal_bytes) * 100 > 85
for: 30s
labels:
severity: warning
annotations:
summary: "Server memory is almost full"
description: "Docker host memory usage is {{ humanize $value}}%. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
```
Trigger an alert if the Docker host storage is almost full:
```yaml
- alert: high_storage_load
expr: (node_filesystem_size_bytes{fstype="aufs"} - node_filesystem_free_bytes{fstype="aufs"}) / node_filesystem_size_bytes{fstype="aufs"} * 100 > 85
for: 30s
labels:
severity: warning
annotations:
summary: "Server storage is almost full"
description: "Docker host storage usage is {{ humanize $value}}%. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
```
***Docker Containers alerts***
Trigger an alert if a container is down for more than 30 seconds:
```yaml
- alert: jenkins_down
expr: absent(container_memory_usage_bytes{name="jenkins"})
for: 30s
labels:
severity: critical
annotations:
summary: "Jenkins down"
description: "Jenkins container is down for more than 30 seconds."
```
Trigger an alert if a container is using more than 10% of total CPU cores for more than 30 seconds:
```yaml
- alert: jenkins_high_cpu
expr: sum(rate(container_cpu_usage_seconds_total{name="jenkins"}[1m])) / count(node_cpu_seconds_total{mode="system"}) * 100 > 10
for: 30s
labels:
severity: warning
annotations:
summary: "Jenkins high CPU usage"
description: "Jenkins CPU usage is {{ humanize $value}}%."
```
Trigger an alert if a container is using more than 1.2GB of RAM for more than 30 seconds:
```yaml
- alert: jenkins_high_memory
expr: sum(container_memory_usage_bytes{name="jenkins"}) > 1200000000
for: 30s
labels:
severity: warning
annotations:
summary: "Jenkins high memory usage"
description: "Jenkins memory consumption is at {{ humanize $value}}."
```
## Setup alerting
The AlertManager service is responsible for handling alerts sent by Prometheus server.
AlertManager can send notifications via email, Pushover, Slack, HipChat or any other system that exposes a webhook interface.
A complete list of integrations can be found [here](https://prometheus.io/docs/alerting/configuration).
You can view and silence notifications by accessing `http://<host-ip>:9093`.
The notification receivers can be configured in [alertmanager/config.yml](https://github.com/stefanprodan/dockprom/blob/master/alertmanager/config.yml) file.
To receive alerts via Slack you need to make a custom integration by choose ***incoming web hooks*** in your Slack team app page.
You can find more details on setting up Slack integration [here](http://www.robustperception.io/using-slack-with-the-alertmanager/).
Copy the Slack Webhook URL into the ***api_url*** field and specify a Slack ***channel***.
```yaml
route:
receiver: 'slack'
receivers:
- name: 'slack'
slack_configs:
- send_resolved: true
text: "{{ .CommonAnnotations.description }}"
username: 'Prometheus'
channel: '#<channel>'
api_url: 'https://hooks.slack.com/services/<webhook-id>'
```
![Slack Notifications](https://raw.githubusercontent.com/stefanprodan/dockprom/master/screens/Slack_Notifications.png)
## Sending metrics to the Pushgateway
The [pushgateway](https://github.com/prometheus/pushgateway) is used to collect data from batch jobs or from services.
To push data, simply execute:
echo "some_metric 3.14" | curl --data-binary @- http://user:password@localhost:9091/metrics/job/some_job
Please replace the `user:password` part with your user and password set in the initial configuration (default: `admin:admin`).
## Updating Grafana to v5.2.2
[In Grafana versions >= 5.1 the id of the grafana user has been changed](http://docs.grafana.org/installation/docker/#migration-from-a-previous-version-of-the-docker-container-to-5-1-or-later). Unfortunately this means that files created prior to 5.1 won’t have the correct permissions for later versions.
| Version | User | User ID |
|:-------:|:-------:|:-------:|
| < 5.1 | grafana | 104 |
| \>= 5.1 | grafana | 472 |
There are two possible solutions to this problem.
- Change ownership from 104 to 472
- Start the upgraded container as user 104
##### Specifying a user in docker-compose.yml
To change ownership of the files run your grafana container as root and modify the permissions.
First perform a `docker-compose down` then modify your docker-compose.yml to include the `user: root` option:
```
grafana:
image: grafana/grafana:5.2.2
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/datasources:/etc/grafana/datasources
- ./grafana/dashboards:/etc/grafana/dashboards
- ./grafana/setup.sh:/setup.sh
entrypoint: /setup.sh
user: root
environment:
- GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
restart: unless-stopped
expose:
- 3000
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
```
Perform a `docker-compose up -d` and then issue the following commands:
```
docker exec -it --user root grafana bash
# in the container you just started:
chown -R root:root /etc/grafana && \
chmod -R a+r /etc/grafana && \
chown -R grafana:grafana /var/lib/grafana && \
chown -R grafana:grafana /usr/share/grafana
```
To run the grafana container as `user: 104` change your `docker-compose.yml` like such:
```
grafana:
image: grafana/grafana:5.2.2
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/datasources:/etc/grafana/datasources
- ./grafana/dashboards:/etc/grafana/dashboards
- ./grafana/setup.sh:/setup.sh
entrypoint: /setup.sh
user: "104"
environment:
- GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
restart: unless-stopped
expose:
- 3000
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
```

21
docker-compose/alertmanager/config.yml

@ -0,0 +1,21 @@
#route:
# receiver: 'slack'
#
#receivers:
# - name: 'slack'
# slack_configs:
# - send_resolved: true
# text: "{{ .CommonAnnotations.description }}"
# username: 'Prometheus'
# channel: '#<channel-name>'
# api_url: 'https://hooks.slack.com/services/<webhook-id>'
#route:
# receiver: 'alertmanager webhook'
route:
receiver: 'alertmanager-notifier-webhook'
receivers:
- name: 'alertmanager-notifier-webhook'
webhook_configs:
- url: http://alertmanager-notifier:8899/alert

39
docker-compose/caddy/Caddyfile

@ -0,0 +1,39 @@
:9090 {
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
proxy / prometheus:9090 {
transparent
}
errors stderr
tls off
}
:9093 {
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
proxy / alertmanager:9093 {
transparent
}
errors stderr
tls off
}
:9091 {
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
proxy / pushgateway:9091 {
transparent
}
errors stderr
tls off
}
:3000 {
proxy / grafana:3000 {
transparent
websocket
}
errors stderr
tls off
}

3
docker-compose/config

@ -0,0 +1,3 @@
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=changeme
GF_USERS_ALLOW_SIGN_UP=false

BIN
docker-compose/db/ca.pem

Binary file not shown.

BIN
docker-compose/db/client-cert.pem

Binary file not shown.

BIN
docker-compose/db/public_key.pem

Binary file not shown.

BIN
docker-compose/db/server-cert.pem

Binary file not shown.

238
docker-compose/docker-compose.yml

@ -0,0 +1,238 @@
version: '2.2'
networks:
monitor-net:
driver: bridge
volumes:
prometheus_data: {}
grafana_data: {}
# db_data: {}
services:
prometheus:
image: prom/prometheus:v2.22.1
container_name: prometheus
volumes:
- ./prometheus:/etc/prometheus
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=14d'
- '--web.enable-lifecycle'
restart: unless-stopped
expose:
- 9090
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
alertmanager:
image: prom/alertmanager:v0.21.0
container_name: alertmanager
volumes:
- ./alertmanager:/etc/alertmanager
command:
- '--config.file=/etc/alertmanager/config.yml'
- '--storage.path=/alertmanager'
restart: unless-stopped
expose:
- 9093
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
nodeexporter:
image: prom/node-exporter:v1.0.1
container_name: nodeexporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
restart: unless-stopped
expose:
- 9100
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.38.0
container_name: cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
#- /cgroup:/cgroup:ro #doesn't work on MacOS only for Linux
restart: unless-stopped
expose:
- 8080
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
grafana:
image: grafana/grafana:7.3.1
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
environment:
- GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_RENDERING_SERVER_URL=http://grafana-image-renderer:8081/render
- GF_RENDERING_CALLBACK_URL=http://grafana:3000/
- GF_LOG_FILTERS='rendering:debug'
restart: unless-stopped
expose:
- 3000
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
pushgateway:
image: prom/pushgateway:v1.3.0
container_name: pushgateway
restart: unless-stopped
expose:
- 9091
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
caddy:
image: stefanprodan/caddy
container_name: caddy
ports:
- "3000:3000"
- "9090:9090"
- "9093:9093"
- "9091:9091"
volumes:
- ./caddy:/etc/caddy
environment:
- ADMIN_USER=${ADMIN_USER:-admin}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
restart: unless-stopped
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
# alertmanager_notifier:
# image: ixdotai/alertmanager-notifier:latest
# container_name: alertmanager-notifier
# ports:
# - "8899:8899"
# environment:
# - TELEGRAM_TOKEN="1476293190:AAHIHOyZ7Zyf2V-jrEgWELy0XlWHz_7mk48"
# - TELEGRAM_CHAT_ID="-1001462755835"
# - EXCLUDE_LABELS="yes"
# restart: unless-stopped
# networks:
# - monitor-net
# labels:
# org.label-schema.group: "monitoring"
grafana-image-renderer:
image: grafana/grafana-image-renderer:latest
container_name: grafana-image-renderer
expose:
- 8081
environment:
- ENABLE_METRICS=true
- HTTP_HOST=grafana-image-renderer
- LOG_LEVEL=debug
restart: unless-stopped
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
db:
#image: mysql:5.7
image: mysql:8.0.22
container_name: db
cpus: 4.0
cap_add:
- SYS_NICE
volumes:
# - db_data:/var/lib/mysql
- /home/ixyd/dev/dockprom/db:/var/lib/mysql
- /home/ixyd/dev/wsjtx_dashboards:/wsjtx
restart: always
environment:
MYSQL_ROOT_PASSWORD: verysecret
MYSQL_DATABASE: digimode_stats
MYSQL_USER: wsjtx
MYSQL_PASSWORD: secret
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
mysqld-exporter:
image: prom/mysqld-exporter
container_name: mysqld-exporter
restart: always
environment:
- DATA_SOURCE_NAME=root:verysecret@(db:3306)/digimode_stats
expose:
- 9104
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
wsjtx_exporter:
image: localhost:5000/wsjtx_exporter:latest
container_name: wsjtx_exporter
volumes:
- /home/ixyd/.local/share/WSJT-X:/wsjtx
expose:
- 2112
environment:
- PROMETHEUS=true
- MYSQL=true
- TRACE=true
- STATION=DL3SD
restart: unless-stopped
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
pskreporter_exporter:
image: localhost:5000/pskreporter_exporter:latest
container_name: pskreporter_exporter
expose:
- 2112
environment:
- PROMETHEUS=true
- MYSQL=true
- TRACE=true
- STATION=DL3SD
restart: unless-stopped
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"

12
docker-compose/grafana/provisioning/dashboards/dashboard.yml

@ -0,0 +1,12 @@
apiVersion: 1
providers:
- name: 'Prometheus'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
allowUiUpdates: true
options:
path: /etc/grafana/provisioning/dashboards

1270
docker-compose/grafana/provisioning/dashboards/docker_containers.json

File diff suppressed because it is too large

1441
docker-compose/grafana/provisioning/dashboards/docker_host.json

File diff suppressed because it is too large

3412
docker-compose/grafana/provisioning/dashboards/monitor_services.json

File diff suppressed because it is too large

398
docker-compose/grafana/provisioning/dashboards/nginx_container.json

@ -0,0 +1,398 @@
{
"id": null,
"title": "Nginx",
"description": "Nginx exporter metrics",
"tags": [
"nginx"
],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": true,
"rows": [
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Prometheus",
"decimals": 2,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 3,
"isNew": true,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(irate(nginx_connections_processed_total{stage=\"any\"}[5m])) by (stage)",
"hide": false,
"interval": "",
"intervalFactor": 10,
"legendFormat": "requests",
"metric": "",
"refId": "B",
"step": 10
}
],
"timeFrom": null,
"timeShift": null,
"title": "Requests/sec",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "Prometheus",
"decimals": 2,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 2,
"isNew": true,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(nginx_connections_current) by (state)",
"interval": "",
"intervalFactor": 2,
"legendFormat": "{{state}}",
"metric": "",
"refId": "A",
"step": 2
}
],
"timeFrom": null,
"timeShift": null,
"title": "Connections",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "Prometheus",
"decimals": 2,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"isNew": true,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(irate(nginx_connections_processed_total{stage!=\"any\"}[5m])) by (stage)",
"hide": false,
"interval": "",
"intervalFactor": 10,
"legendFormat": "{{stage}}",
"metric": "",
"refId": "B",
"step": 10
}
],
"timeFrom": null,
"timeShift": null,
"title": "Connections rate",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "Nginx exporter metrics"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 4,
"isNew": true,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(container_cpu_usage_seconds_total{name=~\"nginx\"}[5m])) / count(node_cpu_seconds_total{mode=\"system\"}) * 100",
"intervalFactor": 2,
"legendFormat": "nginx",
"refId": "A",
"step": 2
}
],
"timeFrom": null,
"timeShift": null,
"title": "CPU usage",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "Nginx container metrics"
}
],
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "10s",
"schemaVersion": 12,
"version": 9,
"links": [],
"gnetId": null
}

11
docker-compose/grafana/provisioning/datasources/datasource.yml

@ -0,0 +1,11 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
orgId: 1
url: http://prometheus:9090
basicAuth: false
isDefault: true
editable: true

70
docker-compose/prometheus/alert.rules

@ -0,0 +1,70 @@
groups:
- name: targets
rules:
- alert: monitor_service_down
expr: up == 0
for: 30s
labels:
severity: critical
annotations:
summary: "Monitor service non-operational"
description: "Service {{ $labels.instance }} is down."
- name: host
rules:
- alert: high_cpu_load
expr: node_load1 > 1.5
for: 30s
labels:
severity: warning
annotations:
summary: "Server under high load"
description: "Docker host is under high load, the avg load 1m is at {{ $value}}. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
- alert: high_memory_load
expr: (sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) ) / sum(node_memory_MemTotal_bytes) * 100 > 85
for: 30s
labels:
severity: warning
annotations:
summary: "Server memory is almost full"
description: "Docker host memory usage is {{ humanize $value}}%. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
- alert: high_storage_load
expr: (node_filesystem_size_bytes{fstype="aufs"} - node_filesystem_free_bytes{fstype="aufs"}) / node_filesystem_size_bytes{fstype="aufs"} * 100 > 85
for: 30s
labels:
severity: warning
annotations:
summary: "Server storage is almost full"
description: "Docker host storage usage is {{ humanize $value}}%. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
- name: containers
rules:
- alert: jenkins_down
expr: absent(container_memory_usage_bytes{name="jenkins"})
for: 30s
labels:
severity: critical
annotations:
summary: "Jenkins down"
description: "Jenkins container is down for more than 30 seconds."
- alert: jenkins_high_cpu
expr: sum(rate(container_cpu_usage_seconds_total{name="jenkins"}[1m])) / count(node_cpu_seconds_total{mode="system"}) * 100 > 10
for: 30s
labels:
severity: warning
annotations:
summary: "Jenkins high CPU usage"
description: "Jenkins CPU usage is {{ humanize $value}}%."
- alert: jenkins_high_memory
expr: sum(container_memory_usage_bytes{name="jenkins"}) > 1200000000
for: 30s
labels:
severity: warning
annotations:
summary: "Jenkins high memory usage"
description: "Jenkins memory consumption is at {{ humanize $value}}."

67
docker-compose/prometheus/prometheus.yml

@ -0,0 +1,67 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'docker-host-alpha'
# Load and evaluate rules in this file every 'evaluation_interval' seconds.
rule_files:
- "alert.rules"
# A scrape configuration containing exactly one endpoint to scrape.
scrape_configs:
- job_name: 'nodeexporter'
scrape_interval: 10s
static_configs:
- targets: ['nodeexporter:9100']
# - job_name: 'cadvisor'
# scrape_interval: 10s
# static_configs:
# - targets: ['cadvisor:8080']
- job_name: 'prometheus'
scrape_interval: 10s
static_configs:
- targets: ['localhost:9090']
- job_name: 'pushgateway'
scrape_interval: 10s
honor_labels: true
static_configs:
- targets: ['pushgateway:9091']
- job_name: 'wsjtx_exporter'
scrape_interval: 15s
static_configs:
- targets: ['wsjtx_exporter:2112']
- job_name: 'pskreporter_exporter'
scrape_interval: 300s
static_configs:
- targets: ['pskreporter_exporter:2112']
- job_name: 'mysqld-exporter'
scrape_interval: 10s
static_configs:
- targets: ['mysqld-exporter:9104']
alerting:
alertmanagers:
- scheme: http
static_configs:
- targets:
- 'alertmanager:9093'
# - job_name: 'nginx'
# scrape_interval: 10s
# static_configs:
# - targets: ['nginxexporter:9113']
# - job_name: 'aspnetcore'
# scrape_interval: 10s
# static_configs:
# - targets: ['eventlog-proxy:5000', 'eventlog:5000']

BIN
docker-compose/screens/Grafana_Docker_Containers.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

BIN
docker-compose/screens/Grafana_Docker_Host.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

BIN
docker-compose/screens/Grafana_Prometheus.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

BIN
docker-compose/screens/Slack_Notifications.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

21
misc/bandswitcher.sh

@ -0,0 +1,21 @@
#!/bin/bash
RIGCTL=$(which rigctl)
PARAMETERS="-m 2"
DELAY="300"
BANDS="14074000 21074000 28074000"
# wait for full minute
sleep $(((60 - $(date +%s) % 60)-1))
# start switching
while true
do
for band in $BANDS
do
$RIGCTL $PARAMETERS F $band
sleep $DELAY
done
done

9628
misc/dashboards/prometheus.json

File diff suppressed because it is too large

6
misc/import_csv.sql

@ -0,0 +1,6 @@
LOAD DATA LOCAL INFILE '/wsjtx/DL3SD.CSV'
INTO TABLE wsjtx_all_txt
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(ts, station, callsign, band, continent, mode, dxcc, geohash, report, rx)

BIN
screenshots/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Loading…
Cancel
Save